All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.parosproxy.paros.core.scanner.VariantJSONQuery Maven / Gradle / Ivy

Go to download

The Zed Attack Proxy (ZAP) is an easy to use integrated penetration testing tool for finding vulnerabilities in web applications. It is designed to be used by people with a wide range of security experience and as such is ideal for developers and functional testers who are new to penetration testing. ZAP provides automated scanners as well as a set of tools that allow you to find security vulnerabilities manually.

There is a newer version: 2.15.0
Show newest version
/*
 * Zed Attack Proxy (ZAP) and its related class files.
 *
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 *
 * Copyright 2013 The ZAP Development Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.parosproxy.paros.core.scanner;

import org.apache.commons.lang.StringEscapeUtils;

/** @author andy */
public class VariantJSONQuery extends VariantAbstractRPCQuery {

    public static final String JSON_RPC_CONTENT_TYPE = "application/json";

    public static final int NAME_SEPARATOR = ':';
    public static final int VALUE_SEPARATOR = ',';
    public static final int BEGIN_ARRAY = '[';
    public static final int QUOTATION_MARK = '"';
    public static final int BEGIN_OBJECT = '{';
    public static final int END_OBJECT = '}';
    public static final int END_ARRAY = ']';
    public static final int BACKSLASH = '\\';

    private SimpleStringReader sr;

    public VariantJSONQuery() {
        super(NameValuePair.TYPE_JSON);
    }

    /**
     * @param contentType
     * @return
     */
    @Override
    public boolean isValidContentType(String contentType) {
        return contentType.startsWith(JSON_RPC_CONTENT_TYPE);
    }

    /** @param content */
    @Override
    public void parseContent(String content) {
        sr = new SimpleStringReader(content);
        parseObject();
    }

    /**
     * @param value
     * @param toQuote
     * @return
     */
    @Override
    public String getEscapedValue(String value, boolean toQuote) {
        String result = StringEscapeUtils.escapeJava(value);
        return (toQuote)
                ? VariantJSONQuery.QUOTATION_MARK + result + VariantJSONQuery.QUOTATION_MARK
                : result;
    }

    @Override
    public String getUnescapedValue(String value) {
        return StringEscapeUtils.unescapeJava(value);
    }

    // --------------------------------------------------------------------

    private static final int STATE_READ_START_OBJECT = 0;
    private static final int STATE_READ_FIELD = 1;
    private static final int STATE_READ_VALUE = 2;
    private static final int STATE_READ_POST_VALUE = 3;

    private void parseObject() {
        int state = STATE_READ_START_OBJECT;
        boolean objectRead = false;
        boolean done = false;
        String field = null;
        int beginToken;
        int endToken;
        int chr;

        while (!done) {
            switch (state) {
                case STATE_READ_START_OBJECT:
                    chr = sr.skipWhitespaceRead();
                    if (chr == BEGIN_OBJECT) {
                        objectRead = true;

                        chr = sr.skipWhitespaceRead();
                        if (chr == END_OBJECT) { // empty object
                            return;
                        }

                        sr.unreadLastCharacter();
                        state = STATE_READ_FIELD;

                    } else if (chr == BEGIN_ARRAY) {
                        sr.unreadLastCharacter();
                        state = STATE_READ_VALUE;

                    } else {
                        throw new IllegalArgumentException(
                                "Input is invalid JSON; does not start with '{' or '[', c=" + chr);
                    }

                    break;

                case STATE_READ_FIELD:
                    chr = sr.skipWhitespaceRead();
                    if (chr == QUOTATION_MARK) {

                        beginToken = sr.getPosition();
                        readEscapedString();

                        endToken = sr.getPosition() - 1;
                        // Now we have the string object name
                        // we can do something here for value filtering...
                        field = getToken(beginToken, endToken);

                        chr = sr.skipWhitespaceRead();
                        if (chr != NAME_SEPARATOR) {
                            throw new IllegalArgumentException(
                                    "Expected ':' between string field and value at position "
                                            + sr.getPosition());
                        }

                        sr.skipWhitespaceRead();
                        sr.unreadLastCharacter();
                        state = STATE_READ_VALUE;

                    } else {
                        throw new IllegalArgumentException(
                                "Expected quote at position " + sr.getPosition());
                    }

                    break;

                case STATE_READ_VALUE:
                    if (field == null) {
                        // field is null when you have an untyped Object[], so we place
                        // the JsonArray on the @items field.
                        field = "@items";
                    }

                    parseValue(field);
                    state = STATE_READ_POST_VALUE;
                    break;

                case STATE_READ_POST_VALUE:
                    chr = sr.skipWhitespaceRead();
                    if (chr == -1 && objectRead) {
                        throw new IllegalArgumentException("EOF reached before closing '}'");
                    }

                    if (chr == END_OBJECT || chr == -1) {
                        done = true;

                    } else if (chr == VALUE_SEPARATOR) {
                        state = STATE_READ_FIELD;

                    } else {
                        throw new IllegalArgumentException(
                                "Object not ended with '}' or ']' at position " + sr.getPosition());
                    }
                    break;
            }
        }
    }

    /** @param fieldName */
    private void parseValue(String fieldName) {
        int chr = sr.read();

        // Check if the value is a string
        if (chr == QUOTATION_MARK) {
            int beginToken = sr.getPosition();
            readEscapedString();

            // Now we have the string object value
            // Put everything inside the parameter array
            addParameter(fieldName, beginToken, sr.getPosition() - 1, false, true);

            // check if the value is a number
        } else if (Character.isDigit(chr) || chr == '-') {
            sr.unreadLastCharacter();

            int beginToken = sr.getPosition();
            do {
                chr = sr.read();
                if (chr == -1) {
                    throw new IllegalArgumentException("Reached EOF while reading number");
                }

            } while (Character.isDigit(chr)
                    || (chr == '.')
                    || (chr == 'e')
                    || (chr == 'E')
                    || (chr == '+')
                    || (chr == '-'));

            sr.unreadLastCharacter();
            // Now we have the int object value
            // Put everything inside the parameter array
            addParameter(fieldName, beginToken, sr.getPosition() - 1, true, false);

        } else if (chr == BEGIN_OBJECT) {
            sr.unreadLastCharacter();
            parseObject();

        } else if (chr == BEGIN_ARRAY) {
            parseArray(fieldName);

        } else if (chr == END_ARRAY) { // [] empty array
            sr.unreadLastCharacter();

        } else if (chr == 't' || chr == 'T') {
            sr.unreadLastCharacter();
            parseToken("true");

        } else if (chr == 'f' || chr == 'F') {
            sr.unreadLastCharacter();
            parseToken("false");

        } else if (chr == 'n' || chr == 'N') {
            sr.unreadLastCharacter();
            parseToken("null");

        } else if (chr == -1) {
            throw new IllegalArgumentException("EOF reached prematurely");

        } else {
            throw new IllegalArgumentException(
                    "Unknown value type at position " + sr.getPosition());
        }
    }

    /** Read a JSON array */
    private void parseArray(String fieldName) {
        int chr;
        int idx = 0;
        while (true) {
            sr.skipWhitespaceRead();
            sr.unreadLastCharacter();
            parseValue(fieldName + "[" + (idx++) + "]");

            chr = sr.skipWhitespaceRead();
            if (chr == END_ARRAY) {
                break;
            }

            if (chr != VALUE_SEPARATOR) {
                throw new IllegalArgumentException(
                        "Expected ',' or ']' inside array at position " + sr.getPosition());
            }
        }
    }

    /**
     * Return the specified token from the reader. If it is not found, throw an IOException
     * indicating that. Converting to chr to (char) chr is acceptable because the 'tokens' allowed
     * in a JSON input stream (true, false, null) are all ASCII.
     */
    private void parseToken(String token) {
        int len = token.length();

        for (int i = 0; i < len; i++) {
            int chr = sr.read();
            if (chr == -1) {
                throw new IllegalArgumentException("EOF reached while reading token: " + token);
            }
            chr = Character.toLowerCase((char) chr);
            int loTokenChar = token.charAt(i);

            if (loTokenChar != chr) {
                throw new IllegalArgumentException(
                        "Expected token: " + token + " at position " + sr.getPosition());
            }
        }
    }

    private void readEscapedString() {
        int chr;
        while (true) {
            chr = sr.read();
            if (chr == BACKSLASH) {
                chr = sr.read();
            } else if (chr == QUOTATION_MARK) {
                break;
            }
            if (chr == -1) {
                throw new IllegalArgumentException("EOF reached while reading JSON field name");
            }
        }
    }

    protected class SimpleStringReader {
        private static final String WS = " \t\r\n";
        private String str;
        private int length;
        private int next = 0;

        /**
         * Creates a new string reader.
         *
         * @param s String providing the character stream.
         */
        public SimpleStringReader(String s) {
            this.str = s;
            this.length = s.length();
        }

        /**
         * Read until non-whitespace character and then return it. This saves extra read/pushback.
         *
         * @return int repesenting the next non-whitespace character in the stream.
         */
        public int skipWhitespaceRead() {
            int c = read();
            while (WS.indexOf(c) != -1) {
                c = read();
            }
            return c;
        }

        /**
         * Reads a single character.
         *
         * @return The character read, or -1 if the end of the stream has been reached
         */
        public int read() {
            if (next >= length) {
                return -1;
            }
            return str.charAt(next++);
        }

        public void unreadLastCharacter() {
            next--;
        }

        /** @return */
        public int getPosition() {
            return next;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy