Пример #1
0
def probe_typename(input_document: str, config: graphql.Config) -> str:
    typename = ""
    wrong_field = "imwrongfield"
    document = input_document.replace("FUZZ", wrong_field)

    response = graphql.post(config.url,
                            headers=config.headers,
                            json={"query": document})
    errors = response.json()["errors"]

    wrong_field_regexes = [
        f'Cannot query field "{wrong_field}" on type "(?P<typename>[_0-9a-zA-Z\[\]!]*)".',
        f'Field "[_0-9a-zA-Z\[\]!]*" must not have a selection since type "(?P<typename>[_A-Za-z\[\]!][_0-9a-zA-Z\[\]!]*)" has no subfields.',
    ]

    match = None

    for regex in wrong_field_regexes:
        for error in errors:
            match = re.fullmatch(regex, error["message"])
            if match:
                break
        if match:
            break

    if not match:
        raise Exception(
            f"Expected '{errors}' to match any of '{wrong_field_regexes}'.")

    typename = (match.group("typename").replace("[", "").replace("]",
                                                                 "").replace(
                                                                     "!", ""))

    return typename
Пример #2
0
def probe_input_fields(field: str, argument: str, wordlist: Set,
                       config: graphql.Config) -> Set[str]:
    valid_input_fields = set(wordlist)

    document = f"mutation {{ {field}({argument}: {{ {', '.join([w + ': 7' for w in wordlist])} }}) }}"

    response = graphql.post(config.url,
                            headers=config.headers,
                            json={"query": document})
    errors = response.json()["errors"]

    for error in errors:
        error_message = error["message"]

        # First remove field if it produced an error
        match = re.search(
            'Field "(?P<invalid_field>[_0-9a-zA-Z\[\]!]*)" is not defined by type [_0-9a-zA-Z\[\]!]*.',
            error_message,
        )
        if match:
            valid_input_fields.discard(match.group("invalid_field"))

        # Second obtain field suggestions from error message
        valid_input_fields |= get_valid_input_fields(error_message)

    return valid_input_fields
Пример #3
0
def probe_valid_args(field: str, wordlist: Set, config: graphql.Config,
                     input_document: str) -> Set[str]:
    valid_args = set(wordlist)

    document = input_document.replace(
        "FUZZ", f"{field}({', '.join([w + ': 7' for w in wordlist])})")

    response = graphql.post(config.url,
                            headers=config.headers,
                            json={"query": document})
    errors = response.json()["errors"]

    for error in errors:
        error_message = error["message"]

        if ("must not have a selection since type" in error_message
                and "has no subfields" in error_message):
            return set()

        # First remove arg if it produced an "Unknown argument" error
        match = re.search(
            'Unknown argument "(?P<invalid_arg>[_A-Za-z][_0-9A-Za-z]*)" on field "[_A-Za-z][_0-9A-Za-z.]*"',
            error_message,
        )
        if match:
            valid_args.discard(match.group("invalid_arg"))

        # Second obtain args suggestions from error message
        valid_args |= get_valid_args(error_message)

    return valid_args
Пример #4
0
def fetch_root_typenames(config: graphql.Config) -> Dict[str, Optional[str]]:
    documents = {
        "queryType": "query { __typename }",
        "mutationType": "mutation { __typename }",
        "subscriptionType": "subscription { __typename }",
    }
    typenames = {
        "queryType": None,
        "mutationType": None,
        "subscriptionType": None,
    }

    for name, document in documents.items():
        response = graphql.post(
            config.url,
            headers=config.headers,
            json={"query": document},
            verify=config.verify,
        )
        data = response.json().get("data", {})

        if data:
            typenames[name] = data["__typename"]

    logging.debug(f"Root typenames are: {typenames}")

    return typenames
Пример #5
0
def probe_typeref(documents: List[str], context: str,
                  config: graphql.Config) -> Optional[graphql.TypeRef]:
    typeref = None

    for document in documents:
        response = graphql.post(
            config.url,
            headers=config.headers,
            json={"query": document},
            verify=config.verify,
        )
        errors = response.json().get("errors", [])

        for error in errors:
            typeref = get_typeref(error["message"], context)
            logging.debug(
                f"get_typeref('{error['message']}', '{context}') -> {typeref}")
            if typeref:
                return typeref

    if not typeref and context != 'InputValue':
        raise Exception(
            f"Unable to get TypeRef for {documents} in context {context}")

    return None
Пример #6
0
def probe_typeref(documents: List[str], context: str,
                  config: graphql.Config) -> Optional[graphql.TypeRef]:
    typeref = None

    for document in documents:
        response = graphql.post(config.url,
                                headers=config.headers,
                                json={"query": document})
        errors = response.json().get("errors", [])

        for error in errors:
            typeref = get_typeref(error["message"], context)
            if typeref:
                return typeref

    if not typeref:
        raise Exception(f"Unable to get TypeRef for {documents}")

    return None
Пример #7
0
def probe_valid_fields(wordlist: Set, config: graphql.Config,
                       input_document: str) -> Set[str]:
    # We're assuming all fields from wordlist are valid,
    # then remove fields that produce an error message
    valid_fields = set(wordlist)

    for i in range(0, len(wordlist), config.bucket_size):
        bucket = wordlist[i:i + config.bucket_size]

        document = input_document.replace("FUZZ", " ".join(bucket))

        response = graphql.post(
            config.url,
            headers=config.headers,
            json={"query": document},
            verify=config.verify,
        )
        errors = response.json()["errors"]
        logging.debug(
            f"Sent {len(bucket)} fields, recieved {len(errors)} errors in {response.elapsed.total_seconds()} seconds"
        )

        for error in errors:
            error_message = error["message"]

            if ("must not have a selection since type" in error_message
                    and "has no subfields" in error_message):
                return set()

            # First remove field if it produced an "Cannot query field" error
            match = re.search(
                'Cannot query field [\'"](?P<invalid_field>[_A-Za-z][_0-9A-Za-z]*)[\'"]',
                error_message,
            )
            if match:
                valid_fields.discard(match.group("invalid_field"))

            # Second obtain field suggestions from error message
            valid_fields |= get_valid_fields(error_message)

    return valid_fields
Пример #8
0
 def test_retries_on_500(self):
     response = graphql.post("http://localhost:8000")
     self.assertEqual(response.status_code, 200)