Showing preview only (1,919K chars total). Download the full file or copy to clipboard to get everything.
Repository: cerberauth/vulnapi
Branch: main
Commit: 7f758ba732be
Files: 234
Total size: 1.8 MB
Directory structure:
gitextract_zxxg5r_h/
├── .cobra.yaml
├── .docker/
│ ├── Dockerfile-build
│ └── Dockerfile-goreleaser
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ └── workflows/
│ ├── ci.yml
│ ├── scans.yml
│ └── stale.yml
├── .gitignore
├── .golangci.yml
├── .goreleaser.yaml
├── .vscode/
│ └── launch.json
├── .whitesource
├── LICENSE
├── README.md
├── api/
│ ├── curl.go
│ ├── graphql.go
│ ├── handler.go
│ ├── openapi.go
│ ├── request.go
│ ├── response.go
│ └── response_test.go
├── cmd/
│ ├── discover/
│ │ ├── api.go
│ │ ├── domain.go
│ │ ├── root.go
│ │ └── root_test.go
│ ├── jwt/
│ │ └── root.go
│ ├── root.go
│ ├── scan/
│ │ ├── curl.go
│ │ ├── graphql.go
│ │ ├── openapi.go
│ │ ├── root.go
│ │ └── root_test.go
│ └── serve/
│ └── root.go
├── demo.cast
├── docs/
│ ├── best-practices/
│ │ ├── _meta.yml
│ │ ├── jwt.mdx
│ │ └── security-headers.mdx
│ ├── first-scan.mdx
│ ├── getting-started.mdx
│ ├── github-action.mdx
│ ├── index.mdx
│ ├── installation.mdx
│ ├── labs.mdx
│ ├── reference/
│ │ ├── _meta.yml
│ │ ├── cli/
│ │ │ ├── _meta.yml
│ │ │ ├── discover-api.mdx
│ │ │ ├── discover-domain.mdx
│ │ │ ├── index.mdx
│ │ │ ├── jwt-generate.mdx
│ │ │ ├── scan-curl.mdx
│ │ │ ├── scan-graphql.mdx
│ │ │ ├── scan-openapi.mdx
│ │ │ └── serve.mdx
│ │ ├── output-formats.mdx
│ │ └── scan-ids.mdx
│ ├── vampi.mdx
│ ├── vulnerabilities/
│ │ ├── _meta.yml
│ │ ├── broken-authentication/
│ │ │ ├── _meta.yml
│ │ │ ├── authentication-bypass.mdx
│ │ │ ├── brute-force-attack.mdx
│ │ │ ├── jwt-alg-none.mdx
│ │ │ ├── jwt-blank-secret.mdx
│ │ │ ├── jwt-cross-service-relay-attack.excalidraw
│ │ │ ├── jwt-cross-service-relay-attack.mdx
│ │ │ ├── jwt-kid-injection.mdx
│ │ │ ├── jwt-not-verified.mdx
│ │ │ ├── jwt-null-signature.mdx
│ │ │ └── jwt-weak-secret.mdx
│ │ └── security-misconfiguration/
│ │ ├── _meta.yml
│ │ ├── graphql-introspection.mdx
│ │ ├── http-cookies.mdx
│ │ ├── http-method-allow-override.mdx
│ │ ├── http-trace-track.mdx
│ │ └── tls.mdx
│ └── vulnerabilities.mdx
├── go.mod
├── go.sum
├── internal/
│ ├── auth/
│ │ ├── api_key.go
│ │ ├── api_key_test.go
│ │ ├── basic.go
│ │ ├── basic_test.go
│ │ ├── bearer.go
│ │ ├── bearer_test.go
│ │ ├── headers.go
│ │ ├── no_auth.go
│ │ ├── no_auth_test.go
│ │ ├── oauth.go
│ │ ├── oauth_test.go
│ │ ├── scheme.go
│ │ ├── scheme_test.go
│ │ ├── security_scheme.go
│ │ ├── security_scheme_test.go
│ │ ├── type.go
│ │ ├── uniq_name.go
│ │ └── uniq_name_test.go
│ ├── cmd/
│ │ ├── args.go
│ │ ├── args_test.go
│ │ ├── http.go
│ │ ├── printtable/
│ │ │ ├── fingerprint_table.go
│ │ │ ├── printttable.go
│ │ │ ├── report_table.go
│ │ │ ├── report_table_test.go
│ │ │ └── wellknown_paths_table.go
│ │ ├── progressbar.go
│ │ └── report.go
│ ├── operation/
│ │ ├── operation.go
│ │ ├── operation_test.go
│ │ ├── operations.go
│ │ └── operations_test.go
│ ├── request/
│ │ ├── client.go
│ │ ├── client_test.go
│ │ ├── error.go
│ │ ├── request.go
│ │ ├── request_test.go
│ │ ├── response.go
│ │ └── response_test.go
│ └── scan/
│ ├── attempt.go
│ ├── attempt_test.go
│ ├── scan_url.go
│ ├── utils.go
│ └── utils_test.go
├── logo-text-art.txt
├── main.go
├── openapi/
│ ├── base_url.go
│ ├── base_url_test.go
│ ├── loader.go
│ ├── loader_test.go
│ ├── openapi.go
│ ├── operation.go
│ ├── param.go
│ ├── param_test.go
│ ├── security_scheme.go
│ ├── security_scheme_test.go
│ ├── security_scheme_values.go
│ ├── security_scheme_values_test.go
│ ├── validate.go
│ └── validate_test.go
├── renovate.json
├── report/
│ ├── capec.go
│ ├── curl_report.go
│ ├── curl_report_test.go
│ ├── cwe.go
│ ├── graphql_report.go
│ ├── issue.go
│ ├── issue_report.go
│ ├── issue_report_test.go
│ ├── openapi_report.go
│ ├── openapi_report_test.go
│ ├── options_report.go
│ ├── owasp.go
│ ├── report.go
│ ├── report_test.go
│ ├── reporter.go
│ ├── reporter_test.go
│ └── test/
│ ├── issue.yaml
│ └── issue_nil_classifications.yaml
├── scan/
│ ├── broken_authentication/
│ │ ├── authentication_bypass/
│ │ │ ├── authentication_bypass.go
│ │ │ └── authentication_bypass_test.go
│ │ └── jwt/
│ │ ├── alg_none/
│ │ │ ├── alg_none.go
│ │ │ ├── alg_none_test.go
│ │ │ └── methods.go
│ │ ├── blank_secret/
│ │ │ ├── blank_secret.go
│ │ │ └── blank_secret_test.go
│ │ ├── kid_injection/
│ │ │ ├── kid_injection.go
│ │ │ └── kid_injection_test.go
│ │ ├── not_verified/
│ │ │ ├── not_verified.go
│ │ │ └── not_verified_test.go
│ │ ├── null_signature/
│ │ │ ├── null_signature.go
│ │ │ └── null_signature_test.go
│ │ └── weak_secret/
│ │ ├── weak_secret.go
│ │ └── weak_secret_test.go
│ ├── discover/
│ │ ├── accept_unauthenticated/
│ │ │ ├── accept_unauthenticated_operation.go
│ │ │ └── accept_unauthenticated_operation_test.go
│ │ ├── discoverable_graphql/
│ │ │ ├── discoverable_graphql.go
│ │ │ └── discoverable_graphql_test.go
│ │ ├── discoverable_openapi/
│ │ │ ├── discoverable_openapi.go
│ │ │ └── discoverable_openapi_test.go
│ │ ├── exposed_files/
│ │ │ ├── exposed_files.go
│ │ │ └── exposed_files_test.go
│ │ ├── fingerprint/
│ │ │ ├── fingerprint.go
│ │ │ └── fingerprint_test.go
│ │ ├── healthcheck/
│ │ │ ├── healthcheck.go
│ │ │ └── healthcheck_test.go
│ │ ├── utils.go
│ │ ├── utils_test.go
│ │ └── well-known/
│ │ ├── well_known.go
│ │ └── well_known_test.go
│ ├── graphql/
│ │ └── introspection_enabled/
│ │ ├── introspection_enabled.go
│ │ └── introspection_enabled_test.go
│ ├── misconfiguration/
│ │ ├── http_cookies/
│ │ │ ├── http_cookies.go
│ │ │ └── http_cookies_test.go
│ │ ├── http_headers/
│ │ │ ├── http_headers.go
│ │ │ └── http_headers_test.go
│ │ ├── http_method_override/
│ │ │ ├── http_method_override.go
│ │ │ └── http_method_override_test.go
│ │ ├── http_trace/
│ │ │ ├── http_trace_method.go
│ │ │ └── http_trace_method_test.go
│ │ └── http_track/
│ │ ├── http_track_method.go
│ │ └── http_track_method_test.go
│ ├── operation_scan.go
│ ├── operation_scan_test.go
│ ├── scan.go
│ └── scan_test.go
├── scenario/
│ ├── discover_api.go
│ ├── discover_api_test.go
│ ├── discover_domain.go
│ ├── graphql.go
│ ├── graphql_test.go
│ ├── openapi.go
│ ├── openapi_test.go
│ ├── scans.go
│ ├── url.go
│ ├── url_test.go
│ └── utils.go
├── seclist/
│ ├── lists/
│ │ ├── exposed-paths.txt
│ │ ├── graphql.txt
│ │ ├── healthcheck.txt
│ │ ├── jwt-secrets.txt
│ │ ├── swagger.txt
│ │ └── well-known.txt
│ ├── seclist.go
│ └── seclist_test.go
├── test/
│ └── stub/
│ ├── basic_http_bearer.openapi.json
│ ├── basic_http_bearer_jwt.openapi.json
│ ├── complex.openapi.json
│ ├── petstore.openapi.json
│ ├── simple_api_key.openapi.json
│ ├── simple_http_basic.openapi.json
│ ├── simple_http_bearer.openapi.json
│ ├── simple_http_bearer_jwt.openapi.json
│ ├── simple_http_bearer_jwt.openapi.yaml
│ └── simple_no_scheme.openapi.json
└── vulnapi.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .cobra.yaml
================================================
author: Emmanuel Gautier <emmanuel@cerberauth.com>
license: MIT License
================================================
FILE: .docker/Dockerfile-build
================================================
FROM golang:1.26-bookworm AS builder
WORKDIR /go/src/github.com/cerberauth/vulnapi
COPY go.mod go.mod
COPY go.sum go.sum
ENV CGO_ENABLED 0
ENV GO111MODULE on
RUN go mod download
COPY . .
RUN go build -o /usr/bin/vulnapi .
FROM gcr.io/distroless/static-debian12:nonroot AS runner
COPY --from=builder --chown=nonroot:nonroot /usr/bin/vulnapi /usr/bin/vulnapi
ENTRYPOINT ["vulnapi"]
CMD ["vulnapi"]
================================================
FILE: .docker/Dockerfile-goreleaser
================================================
FROM gcr.io/distroless/static-debian12:nonroot
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
COPY --chown=nonroot:nonroot ${TARGETOS}/${TARGETARCH}${TARGETVARIANT:+/$TARGETVARIANT}/vulnapi /usr/bin/vulnapi
ENTRYPOINT ["/usr/bin/vulnapi"]
================================================
FILE: .github/CODEOWNERS
================================================
* @cerberauth @emmanuelgautier
================================================
FILE: .github/FUNDING.yml
================================================
github: [emmanuelgautier]
buy_me_a_coffee: emmanuelgautier
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
push:
tags:
- "v*.*.*"
branches:
- main
pull_request:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
checks: write
steps:
- uses: actions/checkout@v6
- uses: cerberauth/ci/actions/go-build-test@main
with:
codecov-token: ${{ secrets.CODECOV_TOKEN }}
publish:
needs: build
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
packages: write
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- run: git fetch --force --tags
- uses: cerberauth/ci/actions/releaser@main
with:
package-name: vulnapi
dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }}
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
snapcraft-store-credentials: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
chocolatey-api-key: ${{ secrets.CHOCOLATEY_API_KEY }}
aur-key: ${{ secrets.AUR_KEY }}
github-token: ${{ secrets.PAT }}
================================================
FILE: .github/workflows/scans.yml
================================================
name: Scans
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
GO_VERSION: "1.26"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
packages: read
jobs:
run-api-discovery:
name: API Discovery
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/discoverable:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
run: |
go run main.go discover api http://localhost:8080 --rate 500/s --sqa-opt-out
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/discoverable:latest)
run-unreachable-curl-scan:
name: Unreachable API
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan curl http://localhost:8080 --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Unreachable API found"
run-bad-ssl-scan-curl:
name: Bad SSL Scan ${{ matrix.challenge.name }}
runs-on: ubuntu-latest
strategy:
fail-fast: true
matrix:
challenge:
[
{ name: "expired", url: "https://expired.badssl.com" },
{ name: "wrong-hostname", url: "https://wrong.host.badssl.com" },
{ name: "self-signed", url: "https://self-signed.badssl.com" },
{ name: "untrusted", url: "https://untrusted-root.badssl.com" },
{ name: "revoked", url: "https://revoked.badssl.com" },
{ name: "pinning-test", url: "https://pinning-test.badssl.com" },
]
steps:
- uses: actions/checkout@v6
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan curl ${{ matrix.challenge.url }} --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Bad SSL found in ${{ matrix.challenge.name }}"
run-jwt-scans:
name: JWT Scans
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
challenge:
[
"jwt-alg-none-bypass",
"jwt-blank-secret",
"jwt-kid-path-traversal",
"jwt-kid-sql-injection",
"jwt-not-verified",
"jwt-null-signature",
"jwt-weak-hmac-secret",
]
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/${{ matrix.challenge }}:latest
- name: Get JWT
id: get-jwt
run: echo "jwt=$(docker run --rm ghcr.io/cerberauth/api-vulns-challenges/jwt-strong-eddsa-key:latest jwt)" >> $GITHUB_OUTPUT
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan curl http://localhost:8080 -H "Authorization: Bearer ${{ steps.get-jwt.outputs.jwt }}" --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Vulnerabilities found in ${{ matrix.challenge }}"
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/${{ matrix.challenge }}:latest)
run-header-strong-api-key-scan:
name: Strong API Key Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/strong-api-key:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
run: |
go run main.go scan curl http://localhost:8080 -H "X-API-Key: abcdef1234" --sqa-opt-out
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/strong-api-key:latest)
run-header-api-key-scan:
name: API Key in header Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/auth-not-verified:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan curl http://localhost:8080 -H "X-API-Key: abcdef1234" --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Vulnerabilities found"
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/auth-not-verified:latest)
run-bearer-api-key-scan:
name: Bearer API Key Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/auth-not-verified:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan curl http://localhost:8080 -H "Authorization: Bearer abcdef1234" --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Vulnerabilities found"
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/auth-not-verified:latest)
run-header-strong-http-basic-scan:
name: Strong HTTP Basic Scan
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/strong-http-basic:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
run: |
go run main.go scan curl http://localhost:8080 -H "X-API-Key: abcdef1234" --sqa-opt-out
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/strong-http-basic:latest)
run-http-misconfigurations-scans:
name: HTTP Misconfigurations Scans
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- challenge: "misconfiguration.http_headers"
url: "http://localhost:8080"
- challenge: "misconfiguration.http_headers"
url: "http://localhost:8080/headers/cors-wildcard"
- challenge: "misconfiguration.http_headers"
url: "http://localhost:8080/headers/csp-frame-ancestors"
- challenge: "misconfiguration.http_cookies"
url: "http://localhost:8080/cookies/unsecure"
- challenge: "misconfiguration.http_cookies"
url: "http://localhost:8080/cookies/not-httponly"
- challenge: "misconfiguration.http_cookies"
url: "http://localhost:8080/cookies/samesite-none"
- challenge: "misconfiguration.http_cookies"
url: "http://localhost:8080/cookies/no-expiration"
- challenge: "misconfiguration.http_method_override"
url: "http://localhost:8080/cookies/http-method-override"
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/http-misconfigurations:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan curl ${{ matrix.url }} --scans "${{ matrix.challenge }}" --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Vulnerabilities found in ${{ matrix.challenge }}"
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/http-misconfigurations:latest)
run-graphql-scans:
name: GraphQL Scans
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- challenge: "graphql.introspection_enabled"
url: "http://localhost:4000/graphql"
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 4000:4000 ghcr.io/cerberauth/api-vulns-challenges/apollo:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan graphql ${{ matrix.url }} --scans "${{ matrix.challenge }}" --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Vulnerabilities found in ${{ matrix.challenge }}"
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/apollo:latest)
run-openapi-scans:
name: OpenAPI Scans
runs-on: ubuntu-latest
permissions:
contents: read
packages: read
strategy:
fail-fast: false
matrix:
openapi:
[
"simple_api_key.openapi.json",
"simple_http_bearer_jwt.openapi.json",
"simple_http_bearer.openapi.json",
"simple_no_scheme.openapi.json",
"complex.openapi.json",
"petstore.openapi.json"
]
steps:
- uses: actions/checkout@v6
- name: Login to GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Run Server
run: docker run -d -p 8080:8080 ghcr.io/cerberauth/api-vulns-challenges/auth-not-verified:latest
- name: Setup Go environment
uses: actions/setup-go@v6
with:
go-version: ${{ env.GO_VERSION }}
- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan openapi ./test/stub/${{ matrix.openapi }} --sqa-opt-out
- name: Check for vulnerabilities
if: ${{ steps.vulnapi.outputs.conclusion == 'failure' }}
run: echo "Vulnerabilities found"
- name: Stop Server
if: ${{ always() }}
run: docker stop $(docker ps -q --filter ancestor=ghcr.io/cerberauth/api-vulns-challenges/auth-not-verified:latest)
================================================
FILE: .github/workflows/stale.yml
================================================
name: "Close Stale Issues"
on:
workflow_dispatch: {}
schedule:
- cron: "0 0 * * *"
jobs:
stale:
uses: cerberauth/ci/.github/workflows/stale.yml@main
permissions:
contents: write
issues: write
pull-requests: write
================================================
FILE: .gitignore
================================================
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
__debug_bin*
vulnapi
dist/
report.json
================================================
FILE: .golangci.yml
================================================
version: "2"
linters:
enable:
- goconst
- gocritic
- gosec
settings:
gosec:
excludes:
- G101
- G107
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- .+_test.go
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- .+_test.go
- third_party$
- builtin$
- examples$
================================================
FILE: .goreleaser.yaml
================================================
# yaml-language-server: $schema=https://goreleaser.com/static/schema.json
# vim: set ts=2 sw=2 tw=0 fo=jcroql
version: 2
project_name: vulnapi
before:
hooks:
- go mod tidy
- go generate ./...
snapshot:
version_template: "{{ incpatch .Version }}-next"
gomod:
proxy: true
report_sizes: true
metadata:
mod_timestamp: "{{ .CommitTimestamp }}"
builds:
- id: vulnapi
binary: vulnapi
env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
- arm
- "386"
goarm:
- "7"
ignore:
- goos: windows
goarch: arm64
- goos: windows
goarch: arm
- goos: darwin
goarch: "386"
- goos: darwin
goarch: arm
mod_timestamp: "{{ .CommitTimestamp }}"
flags:
- -trimpath
ldflags:
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }}
universal_binaries:
- id: vulnapi-universal
ids:
- vulnapi
replace: true
checksum:
name_template: "checksums.txt"
source:
enabled: true
archives:
- name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
formats: ["tar.gz"]
format_overrides:
- goos: windows
formats: ["zip"]
builds_info:
group: root
owner: root
mtime: "{{ .CommitDate }}"
files:
- README.md
- LICENSE
sboms:
- artifacts: archive
signs:
- cmd: cosign
signature: "${artifact}.sigstore.json"
output: true
artifacts: checksum
args:
- sign-blob
- "--bundle=${signature}"
- "${artifact}"
- --yes
changelog:
sort: asc
use: github
format: "{{ .SHA }}: {{ .Message }}{{ if .Logins }} ({{ .Logins | englishJoin }}){{ end }}"
groups:
- title: "New Features"
regexp: '^.*?feat(\(.+\))??!?:.+$'
order: 0
- title: "Bug Fixes"
regexp: '^.*?(fix|refactor)(\(.+\))??!?:.+$'
order: 1
- title: "Other"
order: 999
filters:
exclude:
- '^docs:'
- '^test:'
- '^chore\(deps\): '
- '^(build|ci): '
- Merge pull request
- Merge branch
milestones:
- close: true
release:
name_template: "v{{ .Version }}"
homebrew_casks:
- name: vulnapi
description: &description "Scan your APIs for security vulnerabilities and weaknesses."
license: &license MIT
homepage: &homepage https://github.com/cerberauth/vulnapi
repository:
owner: &owner cerberauth
name: homebrew-tap
branch: &main main
commit_author: &commit_author
name: cerberauth-bot
email: 268418261+cerberauth-bot@users.noreply.github.com
scoops:
- name: vulnapi
description: *description
homepage: *homepage
license: *license
repository:
owner: *owner
name: scoop-bucket
branch: *main
commit_author: *commit_author
nfpms:
- file_name_template: "{{ .ConventionalFileName }}"
package_name: vulnapi
vendor: CerberAuth
homepage: *homepage
maintainer: Emmanuel Gautier <emmanuel@cerberauth.com>
description: *description
license: *license
bindir: /usr/bin
section: utils
formats:
- apk
- deb
- rpm
- termux.deb
- archlinux
snapcrafts:
- name_template: "{{ .ProjectName }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
title: VulnAPI
publish: true
summary: *description
description: "Scan your APIs for common security vulnerabilities and weaknesses, including OWASP top 10."
license: *license
confinement: strict
grade: stable
apps:
vulnapi:
plugs:
- home
- network
- removable-media
channel_templates:
- edge
- beta
- candidate
- stable
aurs:
- name: vulnapi-bin
homepage: *homepage
description: *description
maintainers:
- Emmanuel Gautier <emmanuel@cerberauth.com>
license: *license
private_key: "{{ .Env.AUR_KEY }}"
git_url: ssh://aur@aur.archlinux.org/vulnapi-bin.git
skip_upload: auto
package: |-
install -Dm755 "./vulnapi" "${pkgdir}/usr/bin/vulnapi"
install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/vulnapi/LICENSE"
winget:
- name: vulnapi
publisher: CerberAuth
publisher_url: https://www.cerberauth.com
license: *license
license_url: https://github.com/cerberauth/vulnapi/blob/main/LICENSE
homepage: *homepage
short_description: *description
repository:
owner: *owner
name: winget-pkgs
branch: "vulnapi-{{.Version}}"
chocolateys:
- name: vulnapi
title: VulnAPI
owners: CerberAuth
authors: CerberAuth
description: *description
project_url: *homepage
icon_url: "https://www.cerberauth.com/icons/vulnapi.png"
license_url: https://github.com/cerberauth/vulnapi/blob/main/LICENSE
copyright: CerberAuth
tags: "security vulnerability api openapi developer"
summary: *description
api_key: "{{ .Env.CHOCOLATEY_API_KEY }}"
source_repo: https://push.chocolatey.org/
skip_publish: false
dockers_v2:
- id: vulnapi-dockerhub
dockerfile: .docker/Dockerfile-goreleaser
images:
- "cerberauth/vulnapi"
tags:
- "{{ .Tag }}"
- "v{{ .Major }}"
- "v{{ .Major }}.{{ .Minor }}"
- "latest"
platforms:
- linux/amd64
- linux/arm64
- linux/arm/v7
- id: vulnapi-ghcr
dockerfile: .docker/Dockerfile-goreleaser
images:
- "ghcr.io/cerberauth/vulnapi"
tags:
- "{{ .Tag }}"
- "v{{ .Major }}"
- "v{{ .Major }}.{{ .Minor }}"
- "latest"
platforms:
- linux/amd64
- linux/arm64
- linux/arm/v7
docker_signs:
- cmd: cosign
artifacts: manifests
output: true
args:
- "sign"
- "${artifact}@${digest}"
- "--yes"
================================================
FILE: .vscode/launch.json
================================================
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Server",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceRoot}",
"args": ["--sqa-opt-out", "serve"]
}
]
}
================================================
FILE: .whitesource
================================================
{
"scanSettings": {
"baseBranches": []
},
"checkRunSettings": {
"vulnerableCheckRunConclusionLevel": "failure",
"displayMode": "diff",
"useMendCheckNames": true
},
"issueSettings": {
"minSeverityLevel": "LOW",
"issueType": "DEPENDENCY"
}
}
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2023 CerberAuth
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
<p align="center">
<img src="https://vulnapi.cerberauth.com/logo-ascii-text-art.png" height="150" alt="vulnapi logo">
</p>
---
[](https://www.cerberauth.com/community)
[](https://github.com/cerberauth/vulnapi/actions/workflows/ci.yml)

[](https://github.com/cerberauth/vulnapi)

# VulnAPI: An API Security Vulnerability Scanner
VulnAPI is an Open-Source DAST designed to help you scan your APIs for common security vulnerabilities and weaknesses.
By using this tool, you can detect and mitigate security vulnerabilities in your APIs before they are exploited by attackers.

## Installation
Before making your first scan with VulnAPI, you have to download and install it. Please follow the instructions on the [Installation documentation](https://www.cerberauth.com/docs/vulnapi/installation/) page.
## Documentation
Before scanning, you can discover target API useful information by using the `discover` command.
The Vulnerability Scanner CLI offers two methods for scanning APIs:
* **Using Curl-like CLI**: This method involves directly invoking the CLI with parameters resembling curl commands.
* **Using OpenAPI Contracts**: This method utilizes OpenAPI contracts to specify API endpoints for scanning.
### Discover Command
To discover target API useful information, leaked files and well-known path execute the following command:
```bash
vulnapi discover api [API_URL]
```
Example output:
```bash
| TYPE | URL |
|---------------|---------------------------------------------|
| OpenAPI | http://localhost:5000/openapi.json |
| GraphQL | http://localhost:5000/graphql |
| Well-Known | http://localhost:8080/.well-known/jwks.json |
| Exposed Files | http://localhost:8080/.env.dev |
| TECHNOLOGIE/SERVICE | VALUE |
|---------------------|---------------|
| Framework | Flask:2.2.3 |
| Language | Python:3.7.17 |
| Server | Flask:2.2.3 |
```
### Using Curl-like CLI
To perform a scan using the Curl-like CLI, execute the following command:
```bash
vulnapi scan curl [API_URL] [CURL_OPTIONS]
```
Replace `[API_URL]` with the URL of the API to scan, and `[CURL_OPTIONS]` with any additional curl options you wish to include.
Example:
```bash
vulnapi scan curl -X POST https://vulnapi.cerberauth.com/vulnerable/api -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ."
```
### Using OpenAPI Contracts
To perform a scan using OpenAPI contracts, execute the following command:
```bash
echo "[JWT_TOKEN]" | vulnapi scan openapi [PATH_OR_URL_TO_OPENAPI_FILE]
```
Replace [PATH_OR_URL_TO_OPENAPI_FILE] with the path or the URL to the OpenAPI contract JSON file and [JWT_TOKEN] with the JWT token to use for authentication.
Example:
```bash
vulnapi scan openapi https://vulnapi.cerberauth.com/vulnerable/.well-known/openapi.json
```
## Output
The CLI provides detailed reports on any vulnerabilities detected during the scan. Below is an example of the output format:
| TECHNOLOGIE/SERVICE | VALUE |
|---------------------|---------------|
| Framework | Flask:2.2.3 |
| Language | Python:3.11.9 |
| Server | Flask:2.2.3 |
Advice: There are some low-risk issues. It's advised to take a look.
| OPERATION | RISK LEVEL | CVSS 4.0 SCORE | OWASP | VULNERABILITY |
|------------------------------|------------|----------------|--------------------------------|--------------------------------|
| GET / | Medium | 5.1 | API8:2023 Security | X-Frame-Options Header is |
| | | | Misconfiguration | missing |
| | Medium | 5.1 | API8:2023 Security | CORS Headers are missing |
| | | | Misconfiguration | |
| | Medium | 5.1 | API8:2023 Security | CSP frame-ancestors policy is |
| | | | Misconfiguration | not set |
| | Info | 0.0 | API8:2023 Security | X-Content-Type-Options Header |
| | | | Misconfiguration | is missing |
| | Info | 0.0 | API8:2023 Security | Operation May Accepts |
| | | | Misconfiguration | Unauthenticated Requests |
| | Info | 0.0 | API8:2023 Security | HSTS Header is missing |
| | | | Misconfiguration | |
| | Info | 0.0 | API8:2023 Security | CSP Header is not set |
| | | | Misconfiguration | |
| GET /books/v1 | Medium | 5.1 | API8:2023 Security | CSP frame-ancestors policy is |
| | | | Misconfiguration | not set |
| | Medium | 5.1 | API8:2023 Security | X-Frame-Options Header is |
| | | | Misconfiguration | missing |
| | Medium | 5.1 | API8:2023 Security | CORS Headers are missing |
| | | | Misconfiguration | |
| | Info | 0.0 | API8:2023 Security | CSP Header is not set |
| | | | Misconfiguration | |
| | Info | 0.0 | API8:2023 Security | HSTS Header is missing |
| | | | Misconfiguration | |
| | Info | 0.0 | API8:2023 Security | X-Content-Type-Options Header |
| | | | Misconfiguration | is missing |
| | Info | 0.0 | API8:2023 Security | Operation May Accepts |
| | | | Misconfiguration | Unauthenticated Requests
In this example, each line represents a detected vulnerability, severity level (critical), vulnerability type, affected operation (GET http://localhost:8080/), and a description of the vulnerability.
## Vulnerabilities Detected
All the vulnerabilities detected by the project are listed at this URL: [API Vulnerabilities Detected](https://www.cerberauth.com/docs/vulnapi/vulnerabilities?utm_source=github&utm_medium=readme).
> More vulnerabilities and best practices will be added in future releases. If you have any suggestions or requests for additional vulnerabilities or best practices to be included, please feel free to open an issue or submit a pull request.
## Proxy Support
The scanner supports proxy configurations for scanning APIs behind a proxy server. To use a proxy, set the `HTTP_PROXY` or `HTTPS_PROXY` environment variables with the proxy URL.
A command arg `--proxy` is also available to specify the proxy URL.
## Additional Options
The VulnAPI may support additional options for customizing scans or output formatting. Run `vulnapi -h` or `vulnapi help` command to view available options and their descriptions.
## Telemetry
The scanner collects anonymous usage data to help improve the tool. This data includes the number of scans performed, number of detected vulnerabilities, and the severity of vulnerabilities. No sensitive information is collected. You can opt-out of telemetry by passing the `--sqa-opt-out` flag.
## Complete CLI Help
To view the complete CLI help, execute the following command:
```bash
vulnapi -h
```
Here is the output of the help command:
```bash
vulnapi
Usage:
vulnapi [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
help Help about any command
jwt Generate JWT tokens
scan API Scan
serve Start the server
Flags:
-h, --help help for vulnapi
--sqa-opt-out Opt out of sending anonymous usage statistics and crash reports to help improve the tool
Use "vulnapi [command] --help" for more information about a command.
```
## Disclaimer
This scanner is provided for educational and informational purposes only. It should not be used for malicious purposes or to attack any system without proper authorization. Always respect the security and privacy of others.
## Thanks
This project used the following open-source libraries:
* [SecLists](https://github.com/danielmiessler/SecLists)
* [projectdiscovery/wappalyzergo](https://github.com/projectdiscovery/wappalyzergo)
## License
This repository is licensed under the [MIT License](https://github.com/cerberauth/vulnapi/blob/main/LICENSE) @ [CerberAuth](https://www.cerberauth.com/). You are free to use, modify, and distribute the contents of this repository for educational and testing purposes.
================================================
FILE: api/curl.go
================================================
package api
import (
"net/http"
"net/url"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/gin-gonic/gin"
)
type NewURLScanRequest struct {
URL string `form:"url" json:"url" binding:"required"`
Method string `form:"method" json:"method" binding:"required"`
Data string `form:"data" json:"data"`
Opts *ScanOptions `json:"options"`
}
func (h *Handler) ScanURL(ctx *gin.Context) {
var form NewURLScanRequest
if err := ctx.ShouldBindJSON(&form); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
parsedUrl, err := url.Parse(form.URL)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
opts := parseScanOptions(form.Opts)
opts.Header = ctx.Request.Header
opts.Cookies = ctx.Request.Cookies()
client := request.NewClient(opts)
s, err := scenario.NewURLScan(form.Method, parsedUrl, form.Data, client, &scan.ScanOptions{
IncludeScans: form.Opts.Scans,
ExcludeScans: form.Opts.ExcludeScans,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
reporter, _, err := s.Execute(ctx, nil)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, HTTPResponseReports{
Reports: reporter.GetScanReports(),
})
}
================================================
FILE: api/graphql.go
================================================
package api
import (
"net/http"
"net/url"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/gin-gonic/gin"
)
type NewGraphQLScanRequest struct {
Endpoint string `form:"endpoint" json:"endpoint" binding:"required"`
Opts *ScanOptions `json:"options"`
}
func (h *Handler) ScanGraphQL(ctx *gin.Context) {
var form NewGraphQLScanRequest
if err := ctx.ShouldBindJSON(&form); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
parsedEndpoint, err := url.Parse(form.Endpoint)
if err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
opts := parseScanOptions(form.Opts)
opts.Header = ctx.Request.Header
opts.Cookies = ctx.Request.Cookies()
client := request.NewClient(opts)
s, err := scenario.NewGraphQLScan(parsedEndpoint, client, &scan.ScanOptions{
IncludeScans: form.Opts.Scans,
ExcludeScans: form.Opts.ExcludeScans,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
reporter, _, err := s.Execute(ctx, nil)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, HTTPResponseReports{
Reports: reporter.GetScanReports(),
})
}
================================================
FILE: api/handler.go
================================================
package api
import (
"github.com/gin-gonic/gin"
)
type Handler struct{}
func NewHandler() *Handler {
return &Handler{}
}
func Routes(r *gin.Engine, h *Handler) {
scanAPI := r.Group("/scans")
scanAPI.POST("/openapi", h.ScanOpenAPI)
scanAPI.POST("/graphql", h.ScanGraphQL)
scanAPI.POST("/url", h.ScanURL)
}
================================================
FILE: api/openapi.go
================================================
package api
import (
"encoding/json"
"net/http"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/openapi"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/gin-gonic/gin"
)
type NewOpenAPIScanRequest struct {
Schema string `json:"schema" binding:"required"`
SecuritySchemes map[string]struct {
Value string `json:"value" binding:"required"`
} `json:"securitySchemes"`
Opts *ScanOptions `json:"options"`
}
func (h *Handler) ScanOpenAPI(ctx *gin.Context) {
var form NewOpenAPIScanRequest
if err := ctx.ShouldBindJSON(&form); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
doc, err := openapi.LoadFromData(ctx, []byte(form.Schema))
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
if err := doc.Validate(ctx); err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
opts := parseScanOptions(form.Opts)
opts.Header = ctx.Request.Header
opts.Cookies = ctx.Request.Cookies()
client := request.NewClient(opts)
values := make(map[string]interface{}, len(form.SecuritySchemes))
if form.SecuritySchemes != nil {
for key, value := range form.SecuritySchemes {
values[key] = &value.Value
}
}
securitySchemesValues := openapi.NewSecuritySchemeValues(values)
s, err := scenario.NewOpenAPIScan(ctx, doc, securitySchemesValues, client, &scan.ScanOptions{
IncludeScans: form.Opts.Scans,
ExcludeScans: form.Opts.ExcludeScans,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
reporter, _, err := s.Execute(ctx, nil)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
response := HTTPResponseReports{
Reports: reporter.GetScanReports(),
}
_, err = json.Marshal(response)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, response)
}
================================================
FILE: api/request.go
================================================
package api
import (
"net/url"
"github.com/cerberauth/vulnapi/internal/request"
)
type ScanOptions struct {
RateLimit int `json:"rateLimit"`
ProxyURL string `json:"proxy"`
Scans []string `json:"scans"`
ExcludeScans []string `json:"excludeScans"`
}
func parseScanOptions(opts *ScanOptions) request.NewClientOptions {
if opts == nil {
opts = &ScanOptions{}
}
var proxyURL *url.URL
if opts.ProxyURL != "" {
proxyURL, _ = url.Parse(opts.ProxyURL)
}
return request.NewClientOptions{
RateLimit: opts.RateLimit,
ProxyURL: proxyURL,
Header: nil,
Cookies: nil,
}
}
================================================
FILE: api/response.go
================================================
package api
import (
"github.com/cerberauth/vulnapi/report"
)
type HTTPResponseReports struct {
Reports []*report.ScanReport `json:"reports"`
}
================================================
FILE: api/response_test.go
================================================
package api_test
import (
"encoding/json"
"net/http"
"testing"
"time"
"github.com/cerberauth/vulnapi/api"
"github.com/cerberauth/vulnapi/internal/operation"
"github.com/cerberauth/vulnapi/report"
"github.com/stretchr/testify/assert"
)
func TestMarshalHTTPResponseReports(t *testing.T) {
op := operation.MustNewOperation(http.MethodPost, "http://localhost:8080/", nil, nil)
sr := report.NewScanReport("id", "test", op)
sr.StartTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
sr.EndTime = time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
hrr := api.HTTPResponseReports{
Reports: []*report.ScanReport{sr},
}
b, err := json.Marshal(hrr)
assert.NoError(t, err)
assert.NotEmpty(t, b)
}
================================================
FILE: cmd/discover/api.go
================================================
package discover
import (
"log"
"net/http"
"net/url"
"github.com/cerberauth/cobracurl"
internalCmd "github.com/cerberauth/vulnapi/internal/cmd"
"github.com/cerberauth/vulnapi/internal/cmd/printtable"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/cerberauth/x/telemetryx"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/metric"
)
func NewAPICmd() (apiCmd *cobra.Command) {
apiCmd = &cobra.Command{
Use: "api [url]",
Short: "Discover api endpoints and server information",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
telemetryMeter := telemetryx.GetMeterProvider().Meter(otelName)
telemetryDiscoverApiSuccessCounter, _ := telemetryMeter.Int64Counter("discover.api.success.counter")
telemetryDiscoverApiErrorCounter, _ := telemetryMeter.Int64Counter("discover.api.error.counter")
ctx := cmd.Context()
if args[0] == "" {
telemetryDiscoverApiErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("url is required")))
log.Fatal("url is required")
}
parsedUrl, err := url.Parse(args[0])
if err != nil {
telemetryDiscoverApiErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("invalid url")))
log.Fatal(err)
}
client, err := internalCmd.NewHTTPClientFromCmd(cmd)
if err != nil {
telemetryDiscoverApiErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("invalid client")))
log.Fatal(err)
}
headers, cookies, err := cobracurl.BuildRequestHeaders(cmd)
if err != nil {
telemetryDiscoverApiErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("error building request headers")))
log.Fatal(err)
}
client = client.WithHeader(headers).WithCookies(cookies)
s, err := scenario.NewDiscoverAPIScan(http.MethodGet, parsedUrl, client, &scan.ScanOptions{
IncludeScans: internalCmd.GetIncludeScans(),
ExcludeScans: internalCmd.GetExcludeScans(),
})
if err != nil {
telemetryDiscoverApiErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("invalid scan")))
log.Fatal(err)
}
var bar *progressbar.ProgressBar
if !internalCmd.GetNoProgress() {
bar = internalCmd.NewProgressBar(len(s.GetOperationsScans()))
// nolint:errcheck
defer bar.Finish()
}
reporter, _, err := s.Execute(ctx, func(operationScan *scan.OperationScan) {
if bar != nil {
// nolint:errcheck
bar.Add(1)
}
})
if err != nil {
telemetryDiscoverApiErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("error executing scan")))
log.Fatal(err)
}
printtable.WellKnownPathsScanReport(reporter)
printtable.FingerprintScanReport(reporter)
telemetryDiscoverApiSuccessCounter.Add(ctx, 1)
},
}
internalCmd.AddCommonArgs(apiCmd)
return apiCmd
}
================================================
FILE: cmd/discover/domain.go
================================================
package discover
import (
"fmt"
"log"
"github.com/cerberauth/cobracurl"
internalCmd "github.com/cerberauth/vulnapi/internal/cmd"
"github.com/cerberauth/vulnapi/internal/cmd/printtable"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/cerberauth/x/telemetryx"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/metric"
)
func NewDomainCmd() (domainCmd *cobra.Command) {
domainCmd = &cobra.Command{
Use: "domain [domain]",
Short: "Discover subdomains with API endpoints",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
telemetryMeter := telemetryx.GetMeterProvider().Meter(otelName)
telemetryDiscoverDomainSuccessCounter, _ := telemetryMeter.Int64Counter("discover.domain.success.counter")
telemetryDiscoverDomainErrorCounter, _ := telemetryMeter.Int64Counter("discover.domain.error.counter")
ctx := cmd.Context()
domain := args[0]
client, err := internalCmd.NewHTTPClientFromCmd(cmd)
if err != nil {
telemetryDiscoverDomainErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("invalid client")))
log.Fatal(err)
}
headers, cookies, err := cobracurl.BuildRequestHeaders(cmd)
if err != nil {
telemetryDiscoverDomainErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("error building request headers")))
log.Fatal(err)
}
client = client.WithHeader(headers).WithCookies(cookies)
fmt.Printf("Discovering APIs for %s\n", domain)
scans, err := scenario.NewDiscoverDomainsScan(domain, client, &scan.ScanOptions{
IncludeScans: internalCmd.GetIncludeScans(),
ExcludeScans: internalCmd.GetExcludeScans(),
})
if err != nil {
telemetryDiscoverDomainErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("invalid scan")))
log.Fatal(err)
}
fmt.Printf("Found %d Domains\n", len(scans))
for _, s := range scans {
fmt.Println()
fmt.Printf("Scanning %s\n", s.Operations[0].URL.String())
var bar *progressbar.ProgressBar
if !internalCmd.GetNoProgress() {
bar = internalCmd.NewProgressBar(len(s.GetOperationsScans()))
// nolint:errcheck
defer bar.Finish()
}
reporter, _, err := s.Execute(ctx, func(operationScan *scan.OperationScan) {
if bar != nil {
// nolint:errcheck
bar.Add(1)
}
})
if err != nil {
telemetryDiscoverDomainErrorCounter.Add(ctx, 1, metric.WithAttributes(otelErrorReasonAttributeKey.String("error executing scan")))
log.Fatal(err)
}
printtable.WellKnownPathsScanReport(reporter)
printtable.FingerprintScanReport(reporter)
telemetryDiscoverDomainSuccessCounter.Add(ctx, 1)
}
},
}
internalCmd.AddCommonArgs(domainCmd)
return domainCmd
}
================================================
FILE: cmd/discover/root.go
================================================
package discover
import (
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
)
const (
otelName = "github.com/cerberauth/vulnapi/cmd/discover"
otelErrorReasonAttributeKey = attribute.Key("error_reason")
)
func NewDiscoverCmd() (discoverCmd *cobra.Command) {
discoverCmd = &cobra.Command{
Use: "discover [type]",
Short: "Discover APIs, API endpoints and server information",
}
discoverCmd.AddCommand(NewDomainCmd())
discoverCmd.AddCommand(NewAPICmd())
return discoverCmd
}
================================================
FILE: cmd/discover/root_test.go
================================================
package discover_test
import (
"testing"
"github.com/cerberauth/vulnapi/cmd/discover"
"github.com/stretchr/testify/assert"
)
func TestNewDiscoverCmd(t *testing.T) {
scanCmd := discover.NewDiscoverCmd()
assert.NotNil(t, scanCmd)
// Assert that NewCURLScanCmd and NewOpenAPIScanCmd commands are added
assert.NotNil(t, scanCmd.Commands())
}
================================================
FILE: cmd/jwt/root.go
================================================
package jwt
import (
"errors"
"fmt"
"log"
"strings"
"github.com/cerberauth/jwtop/jwt/editor"
"github.com/cerberauth/x/telemetryx"
jwtlib "github.com/golang-jwt/jwt/v5"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
const (
otelName = "github.com/cerberauth/vulnapi/cmd/discover"
otelErrorReasonAttributeKey = attribute.Key("error_reason")
algorithmAttributeKey = attribute.Key("algorithm")
)
type Algorithm string
const (
None Algorithm = "NONE"
HS256 Algorithm = "HS256"
HS384 Algorithm = "HS384"
HS512 Algorithm = "HS512"
RS256 Algorithm = "RS256"
RS384 Algorithm = "RS384"
RS512 Algorithm = "RS512"
ES256 Algorithm = "ES256"
ES384 Algorithm = "ES384"
)
var (
secret string
alg string
aud string
)
func GetAlgorithm(alg string) (jwtlib.SigningMethod, error) {
switch strings.ToUpper(alg) {
case string(HS256):
return jwtlib.SigningMethodHS256, nil
case string(HS384):
return jwtlib.SigningMethodHS384, nil
case string(HS512):
return jwtlib.SigningMethodHS512, nil
case string(RS256):
return jwtlib.SigningMethodRS256, nil
case string(RS384):
return jwtlib.SigningMethodRS384, nil
case string(RS512):
return jwtlib.SigningMethodRS512, nil
case string(ES256):
return jwtlib.SigningMethodES256, nil
case string(ES384):
return jwtlib.SigningMethodES384, nil
case string(None):
return jwtlib.SigningMethodNone, nil
default:
return nil, fmt.Errorf("invalid algorithm: %s", alg)
}
}
func NewJWTCmd() (cmd *cobra.Command) {
rootCmd := &cobra.Command{
Use: "jwt",
Short: "Generate JWT tokens",
}
generateCmd := &cobra.Command{
Use: "generate [token]",
Short: "Generate a new JWT token from an existing token",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
otelAlgorithmAttribute := algorithmAttributeKey.String(alg)
telemetryMeter := telemetryx.GetMeterProvider().Meter(otelName)
telemetryJwtSuccessCounter, _ := telemetryMeter.Int64Counter("jwt.success.counter")
telemetryJwtErrorCounter, _ := telemetryMeter.Int64Counter("jwt.error.counter")
ctx := cmd.Context()
tokenString := args[0]
var key interface{}
var newTokenString string
tokenWriter, err := editor.NewTokenEditor(tokenString)
if err != nil {
telemetryJwtErrorCounter.Add(ctx, 1, metric.WithAttributes(otelAlgorithmAttribute, otelErrorReasonAttributeKey.String("invalid token")))
log.Fatal(err)
}
if secret != "" {
key = []byte(secret)
}
var signingMethod jwtlib.SigningMethod
if alg != "" {
if signingMethod, err = GetAlgorithm(alg); err != nil {
telemetryJwtErrorCounter.Add(ctx, 1, metric.WithAttributes(otelAlgorithmAttribute, otelErrorReasonAttributeKey.String("invalid algorithm")))
log.Fatal(err)
}
}
if signingMethod == jwtlib.SigningMethodNone {
key = jwtlib.UnsafeAllowNoneSignatureType
}
if signingMethod == nil || key == nil {
err = errors.New("algorithm and secret are required")
telemetryJwtErrorCounter.Add(ctx, 1, metric.WithAttributes(otelAlgorithmAttribute, otelErrorReasonAttributeKey.String(err.Error())))
log.Fatal(err)
}
newTokenString, err = tokenWriter.SignWithMethodAndKey(signingMethod, key)
if err != nil {
telemetryJwtErrorCounter.Add(ctx, 1, metric.WithAttributes(otelAlgorithmAttribute, otelErrorReasonAttributeKey.String("invalid token")))
log.Fatal(err)
}
fmt.Println(newTokenString)
telemetryJwtSuccessCounter.Add(ctx, 1, metric.WithAttributes(otelAlgorithmAttribute))
},
}
generateCmd.Flags().StringVarP(&secret, "secret", "", "", "Secret key to sign the token")
generateCmd.Flags().StringVarP(&alg, "alg", "", "", "Algorithm to sign the token")
generateCmd.Flags().StringVarP(&aud, "aud", "", "", "Audience of the token")
rootCmd.AddCommand(generateCmd)
return rootCmd
}
================================================
FILE: cmd/root.go
================================================
package cmd
import (
"context"
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/cerberauth/vulnapi/cmd/discover"
"github.com/cerberauth/vulnapi/cmd/jwt"
"github.com/cerberauth/vulnapi/cmd/scan"
"github.com/cerberauth/vulnapi/cmd/serve"
"github.com/cerberauth/x/telemetryx"
)
var (
sqaOptOut bool
otelShutdown func(context.Context) error
)
var name = "vulnapi"
func NewRootCmd(projectVersion, commit, date string) (cmd *cobra.Command) {
versionCmd := &cobra.Command{
Use: "version",
Short: "Print the version number of this application",
Long: `All software has versions. This is this application's`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(projectVersion + " (commit=" + commit + ", built=" + date + ")")
},
}
rootCmd := &cobra.Command{
Use: name,
Version: projectVersion + " (commit=" + commit + ", built=" + date + ")",
Short: name,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
if !sqaOptOut {
otelShutdown, _ = telemetryx.New(cmd.Context(), name, projectVersion, telemetryx.WithCommit(commit), telemetryx.WithBuildDate(date))
}
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
if otelShutdown != nil {
_ = otelShutdown(cmd.Context())
otelShutdown = nil
}
},
}
rootCmd.AddCommand(versionCmd)
rootCmd.AddCommand(discover.NewDiscoverCmd())
rootCmd.AddCommand(scan.NewScanCmd())
rootCmd.AddCommand(jwt.NewJWTCmd())
rootCmd.AddCommand(serve.NewServeCmd())
rootCmd.PersistentFlags().BoolVarP(&sqaOptOut, "sqa-opt-out", "", false, "Opt out of sending anonymous usage statistics and crash reports to help improve the tool")
return rootCmd
}
func Execute(projectVersion, commit, date string) {
c := NewRootCmd(projectVersion, commit, date)
defer func() {
if otelShutdown != nil {
_ = otelShutdown(context.Background())
otelShutdown = nil
}
}()
if err := c.Execute(); err != nil {
if otelShutdown != nil {
_ = otelShutdown(context.Background())
otelShutdown = nil
}
_, _ = os.Stderr.WriteString(err.Error() + "\n")
// nolint: gocritic // false positive
os.Exit(1)
}
}
================================================
FILE: cmd/scan/curl.go
================================================
package scan
import (
"io"
"log"
"github.com/cerberauth/cobracurl"
internalCmd "github.com/cerberauth/vulnapi/internal/cmd"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/cerberauth/x/telemetryx"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
var (
methodAttributeKey = attribute.Key("method")
)
func NewCURLScanCmd() (scanCmd *cobra.Command) {
var (
includeScans []string
excludeScans []string
reportFormat string
reportTransport string
reportFile string
reportURL string
noProgress bool
severityThreshold float64
)
scanCmd = &cobra.Command{
Use: "curl [URL]",
Short: "CURL style Scan",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
req, err := cobracurl.BuildRequest(cmd, args)
if err != nil {
log.Fatal(err)
}
otelMethodAttribute := methodAttributeKey.String(req.Method)
otelIncludeScansAttribute := includeScansAttributeKey.StringSlice(internalCmd.FilterScans(includeScans))
otelExcludeScansAttribute := excludeScansAttributeKey.StringSlice(internalCmd.FilterScans(excludeScans))
otelAttributes := []attribute.KeyValue{
otelMethodAttribute,
otelIncludeScansAttribute,
otelExcludeScansAttribute,
}
telemetryMeter := telemetryx.GetMeterProvider().Meter(otelName)
telemetryScanCurlSuccessCounter, _ := telemetryMeter.Int64Counter("scan.curl.success.counter")
telemetryScanCurlErrorCounter, _ := telemetryMeter.Int64Counter("scan.curl.error.counter")
ctx := cmd.Context()
// Extract body from the request for NewURLScan
var curlData string
if req.Body != nil {
bodyBytes, readErr := io.ReadAll(req.Body)
if readErr != nil {
telemetryScanCurlErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error reading body"))...))
log.Fatal(readErr)
}
curlData = string(bodyBytes)
}
client, err := internalCmd.NewHTTPClientFromCmd(cmd)
if err != nil {
telemetryScanCurlErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("invalid client"))...))
log.Fatal(err)
}
// Transfer headers and cookies from the built request onto the vulnapi client
client = client.WithHeader(req.Header).WithCookies(req.Cookies())
request.SetDefaultClient(client)
// Set package-level report vars used by PrintOrExportReport
internalCmd.SetReportFile(reportFile)
internalCmd.SetReportURL(reportURL)
internalCmd.SetSeverityThreshold(severityThreshold)
s, err := scenario.NewURLScan(req.Method, req.URL, curlData, client, &scan.ScanOptions{
IncludeScans: internalCmd.FilterScans(includeScans),
ExcludeScans: internalCmd.FilterScans(excludeScans),
})
if err != nil {
telemetryScanCurlErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("invalid scenario"))...))
log.Fatal(err)
}
var bar *progressbar.ProgressBar
if !noProgress {
bar = internalCmd.NewProgressBar(len(s.GetOperationsScans()))
// nolint:errcheck
defer bar.Finish()
}
reporter, _, err := s.Execute(ctx, func(operationScan *scan.OperationScan) {
if bar != nil {
// nolint:errcheck
bar.Add(1)
}
})
if err != nil {
telemetryScanCurlErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error executing scenario"))...))
log.Fatal(err)
}
err = internalCmd.PrintOrExportReport(reportFormat, reportTransport, reporter)
if err != nil {
telemetryScanCurlErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error printing report"))...))
log.Fatal(err)
}
telemetryScanCurlSuccessCounter.Add(ctx, 1, metric.WithAttributes(otelAttributes...))
},
}
cobracurl.RegisterFlags(scanCmd.Flags())
// vulnapi-specific flags (no conflicting shorthands with cobracurl)
scanCmd.Flags().StringArrayVar(&includeScans, "scans", nil, "Include specific scans")
scanCmd.Flags().StringArrayVar(&excludeScans, "exclude-scans", nil, "Exclude specific scans")
scanCmd.Flags().StringVar(&reportFormat, "report-format", "table", "Report format (table, json, yaml)")
scanCmd.Flags().StringVar(&reportTransport, "report-transport", "file", "The transport to use for report (e.g. file, http)")
scanCmd.Flags().StringVar(&reportFile, "report-file", "", "The file to write the report to")
scanCmd.Flags().StringVar(&reportURL, "report-url", "", "The URL to send the report to")
scanCmd.Flags().BoolVar(&noProgress, "no-progress", false, "Disable progress output")
scanCmd.Flags().Float64Var(&severityThreshold, "severity-threshold", 1, "Threshold to trigger stderr output if at least one vulnerability CVSS is higher")
return scanCmd
}
================================================
FILE: cmd/scan/graphql.go
================================================
package scan
import (
"log"
"github.com/cerberauth/cobracurl"
internalCmd "github.com/cerberauth/vulnapi/internal/cmd"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/cerberauth/x/telemetryx"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
func NewGraphQLScanCmd() (scanCmd *cobra.Command) {
var (
includeScans []string
excludeScans []string
reportFormat string
reportTransport string
reportFile string
reportURL string
noProgress bool
severityThreshold float64
)
scanCmd = &cobra.Command{
Use: "graphql [endpoint]",
Short: "GraphQL scan",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
req, err := cobracurl.BuildRequest(cmd, args)
if err != nil {
log.Fatal(err)
}
otelIncludeScansAttribute := includeScansAttributeKey.StringSlice(internalCmd.FilterScans(includeScans))
otelExcludeScansAttribute := excludeScansAttributeKey.StringSlice(internalCmd.FilterScans(excludeScans))
otelAttributes := []attribute.KeyValue{
otelIncludeScansAttribute,
otelExcludeScansAttribute,
}
telemetryMeter := telemetryx.GetMeterProvider().Meter(otelName)
telemetryScanGraphQLSuccessCounter, _ := telemetryMeter.Int64Counter("scan.graphql.success.counter")
telemetryScanGraphQLErrorCounter, _ := telemetryMeter.Int64Counter("scan.graphql.error.counter")
ctx := cmd.Context()
client, err := internalCmd.NewHTTPClientFromCmd(cmd)
if err != nil {
telemetryScanGraphQLErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("invalid client"))...))
log.Fatal(err)
}
// Transfer headers and cookies from the built request onto the vulnapi client
client = client.WithHeader(req.Header).WithCookies(req.Cookies())
request.SetDefaultClient(client)
// Set package-level report vars used by PrintOrExportReport
internalCmd.SetReportFile(reportFile)
internalCmd.SetReportURL(reportURL)
internalCmd.SetSeverityThreshold(severityThreshold)
s, err := scenario.NewGraphQLScan(req.URL, client, &scan.ScanOptions{
IncludeScans: internalCmd.FilterScans(includeScans),
ExcludeScans: internalCmd.FilterScans(excludeScans),
})
if err != nil {
telemetryScanGraphQLErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("invalid scenario"))...))
log.Fatal(err)
}
var bar *progressbar.ProgressBar
if !noProgress {
bar = internalCmd.NewProgressBar(len(s.GetOperationsScans()))
// nolint:errcheck
defer bar.Finish()
}
reporter, _, err := s.Execute(ctx, func(operationScan *scan.OperationScan) {
if bar != nil {
// nolint:errcheck
bar.Add(1)
}
})
if err != nil {
telemetryScanGraphQLErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error executing scenario"))...))
log.Fatal(err)
}
err = internalCmd.PrintOrExportReport(reportFormat, reportTransport, reporter)
if err != nil {
telemetryScanGraphQLErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error printing report"))...))
log.Fatal(err)
}
telemetryScanGraphQLSuccessCounter.Add(ctx, 1, metric.WithAttributes(otelAttributes...))
},
}
cobracurl.RegisterFlags(scanCmd.Flags())
// vulnapi-specific flags (no conflicting shorthands with cobracurl)
scanCmd.Flags().StringArrayVar(&includeScans, "scans", nil, "Include specific scans")
scanCmd.Flags().StringArrayVar(&excludeScans, "exclude-scans", nil, "Exclude specific scans")
scanCmd.Flags().StringVar(&reportFormat, "report-format", "table", "Report format (table, json, yaml)")
scanCmd.Flags().StringVar(&reportTransport, "report-transport", "file", "The transport to use for report (e.g. file, http)")
scanCmd.Flags().StringVar(&reportFile, "report-file", "", "The file to write the report to")
scanCmd.Flags().StringVar(&reportURL, "report-url", "", "The URL to send the report to")
scanCmd.Flags().BoolVar(&noProgress, "no-progress", false, "Disable progress output")
scanCmd.Flags().Float64Var(&severityThreshold, "severity-threshold", 1, "Threshold to trigger stderr output if at least one vulnerability CVSS is higher")
return scanCmd
}
================================================
FILE: cmd/scan/openapi.go
================================================
package scan
import (
"bufio"
"log"
"os"
"github.com/cerberauth/cobracurl"
internalCmd "github.com/cerberauth/vulnapi/internal/cmd"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/openapi"
"github.com/cerberauth/vulnapi/scan"
"github.com/cerberauth/vulnapi/scenario"
"github.com/cerberauth/x/telemetryx"
"github.com/schollz/progressbar/v3"
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
func isStdinOpen() bool {
stat, _ := os.Stdin.Stat()
return (stat.Mode() & os.ModeCharDevice) == 0
}
func readStdin() *string {
scanner := bufio.NewScanner(os.Stdin)
if scanner.Scan() {
t := scanner.Text()
return &t
}
return nil
}
func NewOpenAPIScanCmd() (scanCmd *cobra.Command) {
var (
securitySchemesValueArg map[string]string
)
scanCmd = &cobra.Command{
Use: "openapi [OpenAPIPAth]",
Short: "OpenAPI Operations Scan",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
otelIncludeScansAttribute := includeScansAttributeKey.StringSlice(internalCmd.GetIncludeScans())
otelExcludeScansAttribute := excludeScansAttributeKey.StringSlice(internalCmd.GetExcludeScans())
otelAttributes := []attribute.KeyValue{
otelIncludeScansAttribute,
otelExcludeScansAttribute,
}
telemetryMeter := telemetryx.GetMeterProvider().Meter(otelName)
telemetryScanOpenAPISuccessCounter, _ := telemetryMeter.Int64Counter("scan.openapi.success.counter")
telemetryScanOpenAPIErrorCounter, _ := telemetryMeter.Int64Counter("scan.openapi.error.counter")
ctx := cmd.Context()
openapiUrlOrPath := args[0]
if openapiUrlOrPath == "" {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("openapi path is required"))...))
log.Fatal("OpenAPI path is required")
}
doc, err := openapi.LoadOpenAPI(ctx, openapiUrlOrPath)
if err != nil {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error loading openapi"))...))
log.Fatal(err)
}
if err := doc.Validate(ctx); err != nil {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("invalid openapi"))...))
log.Fatal(err)
}
var validToken *string
if isStdinOpen() {
validToken = readStdin()
}
values := make(map[string]interface{}, len(securitySchemesValueArg))
for key, value := range securitySchemesValueArg {
values[key] = &value
}
securitySchemesValues := openapi.NewSecuritySchemeValues(values).WithDefault(validToken)
client, err := internalCmd.NewHTTPClientFromCmd(cmd)
if err != nil {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error creating http client"))...))
log.Fatal(err)
}
headers, cookies, err := cobracurl.BuildRequestHeaders(cmd)
if err != nil {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error building request headers"))...))
log.Fatal(err)
}
client = client.WithHeader(headers).WithCookies(cookies)
request.SetDefaultClient(client)
s, err := scenario.NewOpenAPIScan(ctx, doc, securitySchemesValues, client, &scan.ScanOptions{
IncludeScans: internalCmd.GetIncludeScans(),
ExcludeScans: internalCmd.GetExcludeScans(),
})
if err != nil {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("invalid scenario"))...))
log.Fatal(err)
}
var bar *progressbar.ProgressBar
if !internalCmd.GetNoProgress() {
bar = internalCmd.NewProgressBar(len(s.GetOperationsScans()))
// nolint:errcheck
defer bar.Finish()
}
reporter, _, err := s.Execute(ctx, func(operationScan *scan.OperationScan) {
if bar != nil {
// nolint:errcheck
bar.Add(1)
}
})
if err != nil {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error executing scan"))...))
log.Fatal(err)
}
if err = internalCmd.PrintOrExportReport(internalCmd.GetReportFormat(), internalCmd.GetReportTransport(), reporter); err != nil {
telemetryScanOpenAPIErrorCounter.Add(ctx, 1, metric.WithAttributes(append(otelAttributes, otelErrorReasonAttributeKey.String("error printing report"))...))
log.Fatal(err)
}
telemetryScanOpenAPISuccessCounter.Add(ctx, 1, metric.WithAttributes(otelAttributes...))
},
}
internalCmd.AddCommonArgs(scanCmd)
scanCmd.Flags().StringToStringVarP(&securitySchemesValueArg, "security-schemes", "", nil, "Example value for each security scheme")
return scanCmd
}
================================================
FILE: cmd/scan/root.go
================================================
package scan
import (
"github.com/spf13/cobra"
"go.opentelemetry.io/otel/attribute"
)
const (
otelName = "github.com/cerberauth/vulnapi/cmd/scan"
otelErrorReasonAttributeKey = attribute.Key("error_reason")
includeScansAttributeKey = attribute.Key("include_scans")
excludeScansAttributeKey = attribute.Key("exclude_scans")
)
func NewScanCmd() (scanCmd *cobra.Command) {
scanCmd = &cobra.Command{
Use: "scan [type]",
Short: "API Scan",
}
scanCmd.AddCommand(NewCURLScanCmd())
scanCmd.AddCommand(NewOpenAPIScanCmd())
scanCmd.AddCommand(NewGraphQLScanCmd())
return scanCmd
}
================================================
FILE: cmd/scan/root_test.go
================================================
package scan_test
import (
"testing"
"github.com/cerberauth/vulnapi/cmd/scan"
"github.com/stretchr/testify/assert"
)
func TestNewScanCmd(t *testing.T) {
scanCmd := scan.NewScanCmd()
assert.NotNil(t, scanCmd)
// Assert that NewCURLScanCmd and NewOpenAPIScanCmd commands are added
assert.NotNil(t, scanCmd.Commands())
}
================================================
FILE: cmd/serve/root.go
================================================
package serve
import (
"log"
"github.com/cerberauth/vulnapi/api"
"github.com/gin-contrib/requestid"
"github.com/gin-gonic/gin"
"github.com/spf13/cobra"
)
var (
port string
)
func NewServeCmd() (serveCmd *cobra.Command) {
serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the server",
Run: func(cmd *cobra.Command, args []string) {
r := gin.New()
r.Use(gin.Recovery())
r.Use(requestid.New())
handler := api.NewHandler()
api.Routes(r, handler)
if err := r.Run(":" + port); err != nil {
log.Fatal(err)
}
},
}
serveCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to listen to")
return serveCmd
}
================================================
FILE: demo.cast
================================================
{"version": 2, "width": 190, "height": 46, "timestamp": 1729172921, "env": {"SHELL": "/usr/bin/zsh", "TERM": "xterm-256color"}}
[0.549615, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r\u001b]2;manu@manu-pc:~\u0007\u001b]1;~\u0007"]
[0.5607, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[39m\u001b[0m\u001b[49m\u001b[40m\u001b[39m manu@manu-pc \u001b[44m\u001b[30m\u001b[30m ~ \u001b[49m\u001b[34m\u001b[39m \u001b[K"]
[0.560786, "o", "\u001b[?1h\u001b=\u001b[?2004h"]
[0.872746, "o", "c"]
[0.92654, "o", "\bcu"]
[1.030414, "o", "r"]
[1.094325, "o", "l"]
[1.166719, "o", " "]
[1.351184, "o", "http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[K"]
[1.582737, "o", "\u001b[?1l\u001b>"]
[1.582788, "o", "\u001b[?2004l\r\r\n"]
[1.583479, "o", "\u001b]2;curl http://127.0.0.1:8080 -I -H \u0007\u001b]1;curl\u0007"]
[1.59193, "o", "HTTP/1.1 401 Unauthorized\r\r\n\u001b[1mDate\u001b[0m: Thu, 17 Oct 2024 13:48:43 GMT\r\r\n\r\r\n"]
[1.592547, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
[1.592617, "o", "\u001b]2;manu@manu-pc:~\u0007\u001b]1;~\u0007"]
[1.600249, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[39m\u001b[0m\u001b[49m\u001b[40m\u001b[39m manu@manu-pc \u001b[44m\u001b[30m\u001b[30m ~ \u001b[49m\u001b[34m\u001b[39m \u001b[K"]
[1.600281, "o", "\u001b[?1h\u001b="]
[1.600308, "o", "\u001b[?2004h"]
[2.991362, "o", "curl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[K"]
[3.366271, "o", "\u001b[A\u001b[79D"]
[4.007501, "o", "vcurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[4.062914, "o", "\bvucurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[4.110254, "o", "lcurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[4.278522, "o", "ncurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[4.278632, "o", "acurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.070958, "o", "pcurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.118741, "o", "icurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.198965, "o", " curl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.623227, "o", "scurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.646896, "o", "\u001b[1Ccurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.815085, "o", "acurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.910902, "o", "ncurl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[5.942392, "o", " curl http://127.0.0.1:8080 -I -H \"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjkxNTczMzYsImlhdCI6MTcyOTE1MzczNiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.dqaX_Jsabyqz-fJyF9tfQdpOL6ft2SfZKkRdQr0qhDY\"\u001b[A\u001b[79D"]
[6.143006, "o", "\u001b[?1l\u001b>"]
[6.143066, "o", "\u001b[?2004l\u001b[1B\r\r\n"]
[6.143722, "o", "\u001b]2;vulnapi scan curl http://127.0.0.1:8080 -I -H \u0007\u001b]1;vulnapi\u0007"]
[6.298097, "o", "\r 7% |████████████ | (1/14) "]
[6.308194, "o", "\r \r\r 14% |████████████████████████ | (2/14) "]
[6.549512, "o", "\r \r\r 21% |████████████████████████████████████ | (3/14) \r \r\r 28% |████████████████████████████████████████████████ | (4/14) "]
[6.549898, "o", "\r \r"]
[6.549944, "o", "\r 35% |████████████████████████████████████████████████████████████ | (5/14) "]
[6.550186, "o", "\r \r"]
[6.550226, "o", "\r 42% |████████████████████████████████████████████████████████████████████████ | (6/14) "]
[6.550378, "o", "\r \r"]
[6.550415, "o", "\r 50% |██████████████████████████████████████████████████████████████████████████████████████ | (7/14) "]
[6.550701, "o", "\r \r"]
[6.550769, "o", "\r 57% |██████████████████████████████████████████████████████████████████████████████████████████████████ | (8/14) "]
[6.550866, "o", "\r \r"]
[6.550902, "o", "\r 64% |██████████████████████████████████████████████████████████████████████████████████████████████████████████████ | (9/14) "]
[8.327448, "o", "\r \r\r 71% |██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | (10/14) "]
[8.327905, "o", "\r \r\r 78% |██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | (11/14) "]
[8.328168, "o", "\r \r"]
[8.328314, "o", "\r 85% |██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | (12/14) "]
[8.328485, "o", "\r \r\r 92% |██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████ | (13/14) "]
[8.328776, "o", "\r \r\r 100% |████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| (14/14) "]
[8.367945, "o", "\r\nError: There are some high-risk issues. It's advised to take immediate action.\r\n\r\n"]
[8.368049, "o", "| STATUS | SCANS NUMBER |\r\n|---------|"]
[8.368084, "o", "--------------|\r\n| \u001b[1mPassed\u001b[0m | \u001b[1m11\u001b[0m |\r\n| \u001b[1mFailed\u001b[0m | \u001b[1m2\u001b[0m |\r\n| \u001b[1mSkipped\u001b[0m | \u001b[1m2\u001b[0m |\r\n"]
[8.368178, "o", "| \u001b[1mNone\u001b[0m | \u001b[1m0\u001b[0m |\r\n\r\n"]
[8.368697, "o", "| OPERATION | RISK LEVEL | CVSS 4.0 SCORE | OWASP | ISSUE |\r\n"]
[8.368738, "o", "|-----------|------------|----------------|--------------------------------|--------------------------------|\r\n"]
[8.368787, "o", "| GET | \u001b[1;101mCritical\u001b[0m | 9.3 | API2:2023 Broken | JWT Algorithm None is accepted |\r\n| | | | Authentication | |\r\n"]
[8.36884, "o", "| | \u001b[1;43mMedium\u001b[0m | 5.1 | API8:2023 Security | CSP frame-ancestors policy is |\r\n| | | | Misconfiguration | not set |\r\n"]
[8.368882, "o", "| | \u001b[1;43mMedium\u001b[0m | 5.1 | API8:2023 Security | CORS Headers are missing |\r\n| | | | Misconfiguration | |\r\n"]
[8.368933, "o", "| | \u001b[1;43mMedium\u001b[0m | 5.1 | API8:2023 Security | X-Frame-Options Header is |\r\n| | | | Misconfiguration | missing |\r\n"]
[8.368969, "o", "| | \u001b[1;44mInfo\u001b[0m | 0.0 | API8:2023 Security | CSP Header is not set |\r\n| | | | Misconfiguration | |\r\n"]
[8.369268, "o", "| | \u001b[1;44mInfo\u001b[0m | 0.0 | API8:2023 Security | HSTS Header is missing |\r\n| | | | Misconfiguration | |\r\n| | \u001b[1;44mInfo\u001b[0m | 0.0 | API8:2023 Security | X-Content-Type-Options Header |\r\n| | | | Misconfiguration | is missing |\r\n\r\n"]
[8.373265, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
[8.373387, "o", "\u001b]2;manu@manu-pc:~\u0007\u001b]1;~\u0007"]
[8.381705, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[39m\u001b[0m\u001b[49m\u001b[40m\u001b[39m manu@manu-pc \u001b[44m\u001b[30m\u001b[30m ~ \u001b[49m\u001b[34m\u001b[39m \u001b[K"]
[8.381772, "o", "\u001b[?1h\u001b=\u001b[?2004h"]
[8.90217, "o", "\u001b[?2004l\r\r\n"]
================================================
FILE: docs/best-practices/_meta.yml
================================================
label: Best Practices
================================================
FILE: docs/best-practices/jwt.mdx
================================================
---
title: JWT Security Best Practices
description: Best practices for securely implementing JSON Web Tokens (JWTs) in your APIs.
---
JSON Web Tokens (JWTs) are widely used for authentication and authorization in APIs. When implemented incorrectly they become a high-severity attack surface. This guide covers the most important best practices and links each point to the relevant VulnAPI scan.
## Use Strong Signing Algorithms
Prefer asymmetric algorithms (RS256, ES256) for distributed systems where multiple services verify tokens but only one issues them. This ensures the private key never leaves the issuer.
- **Recommended**: RS256, RS384, RS512, ES256, ES384
- **Acceptable for single-service systems**: HS256, HS384, HS512 (with a strong secret — see below)
- **Never use**: `alg: none` in production
Using `alg: none` removes all cryptographic protection, making tokens trivially forgeable. VulnAPI detects this with the [`jwt.alg_none`](../vulnerabilities/broken-authentication/jwt-alg-none) scan.
## Use a Minimum 256-bit Random Secret for HMAC
When using HMAC algorithms (HS256/HS384/HS512), the secret key must be:
- At least 256 bits (32 bytes) of cryptographically random data
- Not a dictionary word, well-known default, or guessable string
- Not an empty string
Tokens signed with empty or weak secrets are detected by the [`jwt.blank_secret`](../vulnerabilities/broken-authentication/jwt-blank-secret) and [`jwt.weak_secret`](../vulnerabilities/broken-authentication/jwt-weak-secret) scans.
```bash
# Generate a secure 256-bit secret (Linux/macOS)
openssl rand -base64 32
```
## Always Verify Signatures Server-Side
Never trust a JWT without verifying its signature using the correct key. Skipping signature verification is one of the most dangerous implementation mistakes.
VulnAPI detects this with the [`jwt.not_verified`](../vulnerabilities/broken-authentication/jwt-not-verified) scan (CVSS 9.3).
```python
# Python example using PyJWT — always pass algorithms explicitly
import jwt
payload = jwt.decode(token, public_key, algorithms=["RS256"])
```
## Reject Tokens With a Stripped Signature
Some JWT libraries accept tokens whose signature segment is empty (null signature). Always configure your library to require a valid, non-empty signature.
VulnAPI detects this with the [`jwt.null_signature`](../vulnerabilities/broken-authentication/jwt-null-signature) scan.
## Validate Standard Claims
After verifying the signature, validate these claims before trusting any payload data:
| Claim | Description | Action |
|-------|-------------|--------|
| `iss` | Issuer — who created the token | Reject if not from a trusted issuer |
| `aud` | Audience — who the token is intended for | Reject if your service is not the intended audience |
| `exp` | Expiration time | Reject if current time is past `exp` |
| `nbf` | Not-before time | Reject if current time is before `nbf` |
| `sub` | Subject — who the token represents | Verify the user still exists / is active |
Failure to validate `iss` and `aud` enables [cross-service relay attacks](../vulnerabilities/broken-authentication/jwt-cross-service-relay-attack).
## Use Short Token Lifetimes With Refresh Token Rotation
Short-lived access tokens limit the damage from a stolen token:
- **Access tokens**: 5–15 minutes
- **Refresh tokens**: hours to days, single-use only (rotate on each use)
Rotate refresh tokens on every use to detect token theft (if an already-used refresh token is presented, revoke the entire session).
## Never Use `alg: none` in Production
The JWT specification allows `alg: none` for unsecured tokens (e.g., locally exchanged messages). This must never be accepted by any production API endpoint.
Harden your JWT library to reject `none` explicitly:
```go
// Go example using golang-jwt
token, err := jwt.Parse(tokenString, keyFunc, jwt.WithValidMethods([]string{"RS256", "ES256"}))
```
## Store Tokens Securely on the Client
| Storage | XSS risk | CSRF risk | Recommendation |
|---------|----------|-----------|----------------|
| `localStorage` | High (JS readable) | None | Avoid for sensitive tokens |
| `sessionStorage` | High (JS readable) | None | Avoid for sensitive tokens |
| `HttpOnly` cookie | None (not JS readable) | Medium (mitigate with SameSite) | **Preferred** |
| Memory (JS variable) | Low | None | Acceptable for SPAs |
Prefer `HttpOnly; Secure; SameSite=Strict` cookies for web applications. See [HTTP Cookies Misconfiguration](../vulnerabilities/security-misconfiguration/http-cookies) for related checks.
## References
- [OWASP JWT Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html)
- [RFC 7519 — JSON Web Token](https://datatracker.ietf.org/doc/html/rfc7519)
- [Auth0 — JWT Best Practices](https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-best-practices)
================================================
FILE: docs/best-practices/security-headers.mdx
================================================
---
title: API Security HTTP Headers
description: Learn about the importance of HTTP security headers for API security and how to test them using VulnAPI.
---
import { Tabs, TabItem } from '@/components/docs'
It's crucial to understand the importance of HTTP security headers, especially when it comes to API security. These headers provide important security features and protections for both the server and the client.
## How to test?
If you want to test only that the API is sending the correct security headers, you can use the following command:
<Tabs>
<TabItem label="cURL">
```bash
vulnapi scan curl [url] --scans misconfiguration.http_headers
```
</TabItem>
<TabItem label="OpenAPI">
```bash
vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans misconfiguration.http_headers
```
</TabItem>
<TabItem label="GraphQL">
```bash
vulnapi scan graphql --scans misconfiguration.http_headers [url]
```
</TabItem>
</Tabs>
## API Content Security Policies (CSP)
Content Security Policy (CSP) headers are essential for mitigating various security risks in the context of APIs. CSP helps to prevent a range of attacks, including:
- **Data Injection Attacks:** CSP can restrict the types of content that can be loaded, reducing the risk of attackers injecting malicious data into the API responses.
- **Clickjacking:** Although primarily a concern for web pages, CSP can also help mitigate clickjacking by controlling the framing of content.
### Example of Secure CSP Header for APIs
```http
Content-Security-Policy: default-src 'none'; frame-ancestors 'none';
```
`frame-ancestors 'none';` ensures that the API response cannot be embedded in iframes, preventing clickjacking attacks as your API should probably not be embedded in iframes.
### Risks Mitigated by CSP
1. **Cross-Site Scripting (XSS):** By restricting the sources from which scripts can be loaded, CSP helps to prevent attackers from injecting and executing malicious scripts.
2. **Data Injection:** By controlling the sources of content, CSP reduces the risk of attackers injecting malicious data into the API responses.
3. **Clickjacking:** By specifying frame-ancestors, CSP can prevent the API responses from being embedded in iframes, mitigating clickjacking attacks.
Implementing CSP headers in your API responses is a proactive measure to enhance the security posture of your API, protecting both the server and the clients from various types of attacks.
## API Cross-Origin Resource Sharing (CORS)
CORS headers control access to resources from different origins. For APIs, proper CORS configuration ensures that only trusted domains can access the API resources, preventing unauthorized access from potentially malicious websites.
### Example of Secure CORS Header for APIs
```http
Access-Control-Allow-Origin: https://trusted-domain.com
```
## HTTP Strict Transport Security (HSTS)
HSTS headers instruct the browser to always use HTTPS when communicating with the server, even if the user types "http" in the address bar. This prevents man-in-the-middle attacks and ensures that all data exchanged between the client and the server is encrypted.
### Example of Secure HSTS Header for APIs
```http
Strict-Transport-Security: max-age=31536000; includeSubDomains
```
## X-Content-Type-Options
This header prevents browsers from **MIME-sniffing** a response away from the declared content type, which can help prevent certain types of attacks, such as content type confusion attacks.
### Example of Secure X-Content-Type-Options Header for APIs
```http
X-Content-Type-Options: nosniff
```
## X-Frame-Options
X-Frame-Options header protects against clickjacking attacks by preventing the API response from being embedded within a frame or iframe on another domain.
### Example of Secure X-Frame-Options Header for APIs
```http
X-Frame-Options: DENY
```
## X-Permitted-Cross-Domain-Policies (deprecated)
This header specifies the policy file that governs how Flash and Adobe AIR applications can interact with the API resources across different domains. However, this header is deprecated and should not be used in modern applications.
## References
- [OWASP REST Security Headers](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html#security-headers)
- [MDN CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
================================================
FILE: docs/first-scan.mdx
================================================
---
title: Your First Scan
description: Run your first API security scan in under a minute using vulnapi scan curl.
sidebar:
order: 4
---
import { Steps, Aside, CardGrid, LinkCard } from '@/components/docs'
The fastest way to try VulnAPI is with `vulnapi scan curl`. It works like curl — point it at any HTTP endpoint and it immediately starts testing for vulnerabilities.
## Basic scan
```bash
vulnapi scan curl https://api.example.com/endpoint
```
That's it. VulnAPI sends a series of crafted requests to the endpoint and prints a report.
## Scanning an authenticated endpoint
Most real APIs require authentication. Pass an `Authorization` header the same way you would with curl:
```bash
vulnapi scan curl https://api.example.com/endpoint \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."
```
You can repeat `-H` for additional headers (API keys, custom headers, etc.):
```bash
vulnapi scan curl https://api.example.com/endpoint \
-H "Authorization: Bearer <token>" \
-H "X-API-Key: <key>"
```
## Scanning a non-GET endpoint
Use `-X` to set the HTTP method and `-d` to send a request body:
```bash
vulnapi scan curl https://api.example.com/login \
-X POST \
-d '{"username":"alice","password":"secret"}' \
-H "Content-Type: application/json"
```
## Reading the report
After the scan completes, VulnAPI prints a table like this:
```
| OPERATION | RISK LEVEL | CVSS 4.0 SCORE | OWASP | VULNERABILITY |
|-----------------------------|------------|----------------|--------------------------|-----------------------------|
| GET /endpoint | High | 9.3 | API2:2023 Broken Auth | JWT Token is not verified |
| GET /endpoint | Medium | 5.1 | API8:2023 Security Misc | X-Frame-Options is missing |
| GET /endpoint | Info | 0.0 | API8:2023 Security Misc | HSTS Header is missing |
```
| Column | What it means |
|--------|---------------|
| **OPERATION** | The HTTP method and path that was tested |
| **RISK LEVEL** | Severity: Info / Low / Medium / High / Critical |
| **CVSS 4.0 SCORE** | Numeric score from 0.0 to 10.0 |
| **OWASP** | The OWASP API Security Top 10 category |
| **VULNERABILITY** | A short description of the finding |
<Aside type="tip">
A summary line above the table shows the highest overall risk level. Start there to quickly assess whether the endpoint has critical issues.
</Aside>
## No findings?
A scan with no rows in the vulnerability table means VulnAPI found nothing with a CVSS score above the default threshold of `1.0`. This is a good sign, but not a guarantee — VulnAPI tests for a specific set of [known vulnerabilities](./vulnerabilities).
## Next steps
<CardGrid>
<LinkCard title="GitHub Action" href="./github-action" description="Automate scans on every pull request." />
<LinkCard title="Output Formats" href="./reference/output-formats" description="Export results as JSON or YAML." />
<LinkCard title="CLI Reference" href="./reference/cli" description="All scan flags and their defaults." />
<LinkCard title="Vulnerabilities" href="./vulnerabilities" description="Every vulnerability VulnAPI checks for." />
</CardGrid>
================================================
FILE: docs/getting-started.mdx
================================================
---
title: Getting Started
description: Get your first API security scan running in under five minutes.
sidebar:
order: 2
---
import { CardGrid, LinkCard, Steps } from '@/components/docs'
Get your first API security scan running in under five minutes.
<Steps>
1. **[Install VulnAPI](./installation)** — Download a pre-built binary for Linux, macOS, Windows, or run via Docker.
2. **[Run your first scan](./first-scan)** — Use `vulnapi scan curl` or point VulnAPI at your OpenAPI spec.
3. **Read the report** — Understand the OPERATION / RISK LEVEL / CVSS / OWASP / VULNERABILITY columns and what each finding means.
4. **[Automate in CI/CD](./github-action)** — Add the VulnAPI GitHub Action to your workflow so every PR is scanned automatically.
</Steps>
<CardGrid>
<LinkCard title="Installation" href="./installation" description="Download and install VulnAPI on your platform." />
<LinkCard title="First Scan" href="./first-scan" description="Step-by-step guide to your first scan." />
<LinkCard title="GitHub Action" href="./github-action" description="Automate scans in your CI/CD pipeline." />
<LinkCard title="CLI Reference" href="./reference/cli" description="All commands, flags, and their defaults." />
</CardGrid>
================================================
FILE: docs/github-action.mdx
================================================
---
title: GitHub Action
description: VulnAPI can be integrated into your CI/CD pipeline using GitHub Actions. This allows you to automate security testing and vulnerability scanning of your APIs as part of your development workflow.
---
VulnAPI can be integrated into your CI/CD pipeline using [GitHub Actions](https://github.com/marketplace/actions/vulnapi-action). Integrating VulnAPI with GitHub Actions enables you to scan your APIs for vulnerabilities and security risks as part of your CI/CD pipeline.
This allows you to automate security testing and vulnerability scanning of your APIs as part of your development workflow, ensuring that your APIs are **secure** and free from vulnerabilities **before they are deployed to production**.
## Example Workflow
Here's an example of a GitHub Actions workflow that uses VulnAPI to scan your API for vulnerabilities:
```yaml
name: VulnAPI
on: [push]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: VulnAPI
uses: cerberauth/vulnapi-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
curl: 'curl http://localhost:8080 -H "Authorization: Bearer eyJhbGci..."'
```
## Documentation
For more information on how to use VulnAPI with GitHub Actions, please refer to the [VulnAPI GitHub Action](https://github.com/marketplace/actions/vulnapi-action) on GitHub Marketplace.
================================================
FILE: docs/index.mdx
================================================
---
title: Introduction
description: VulnAPI is an Open Source DAST that scans APIs for vulnerabilities and security risks.
sidebar:
order: 1
---
import { Aside } from '@/components/docs'
Welcome to VulnAPI Documentation!

---
## What is VulnAPI?
VulnAPI is an Open Source DAST that scans APIs for vulnerabilities and security risks. It provides reports on any vulnerabilities detected during the scan, including the risk level, vulnerability, description, and operation performed when the vulnerability has been found.
VulnAPI offers three methods for scanning APIs:
* **Using Curl-like CLI**: Directly invoke the CLI with parameters resembling curl commands.
* **Using OpenAPI Contracts**: Use an OpenAPI spec to enumerate all endpoints for scanning.
* **Using GraphQL**: Point VulnAPI at a GraphQL endpoint to scan it for vulnerabilities.
## Installation
Before making your first scan with VulnAPI, you have to download and install it. Please follow the instructions on the [Installation](./installation) page.
<Aside type="tip">Ready to scan? Follow the [First Scan](./first-scan) guide.</Aside>
## Documentation
Before scanning, you can discover target API useful information by using the `discover` command.
### Discover Command
To discover target API useful information, execute the following command:
```bash
vulnapi discover api [API_URL]
```
Example output:
```bash
| WELL-KNOWN PATHS | URL |
|------------------|------------------------------------|
| OpenAPI | http://localhost:5000/openapi.json |
| GraphQL | N/A |
| TECHNOLOGIE/SERVICE | VALUE |
|---------------------|---------------|
| Framework | Flask:2.2.3 |
| Language | Python:3.7.17 |
| Server | Flask:2.2.3 |
```
### Using Curl-like CLI
To perform a scan using the Curl-like CLI, execute the following command:
```bash
vulnapi scan curl [API_URL] [CURL_OPTIONS]
```
Replace `[API_URL]` with the URL of the API to scan, and `[CURL_OPTIONS]` with any additional curl options you wish to include.
Example:
```bash
vulnapi scan curl -X POST https://vulnapi.cerberauth.com/vulnerable/api -H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ."
```
### Using OpenAPI Contracts
To perform a scan using OpenAPI contracts, execute the following command:
```bash
echo "[JWT_TOKEN]" | vulnapi scan openapi [PATH_OR_URL_TO_OPENAPI_FILE]
```
Replace `[PATH_OR_URL_TO_OPENAPI_FILE]` with the path or the URL to the OpenAPI contract JSON file and `[JWT_TOKEN]` with the JWT token to use for authentication.
### Using GraphQL
To perform a scan against a GraphQL endpoint, execute the following command:
```bash
vulnapi scan graphql [GRAPHQL_ENDPOINT] -H "Authorization: Bearer [JWT_TOKEN]"
```
## Output
The CLI provides detailed reports on any vulnerabilities detected during the scan. Below is a condensed example:
| OPERATION | RISK LEVEL | CVSS 4.0 SCORE | OWASP | VULNERABILITY |
|------------------------------|------------|----------------|--------------------------------|--------------------------------|
| GET / | Medium | 5.1 | API8:2023 Security | X-Frame-Options Header is |
| | | | Misconfiguration | missing |
| | High | 9.3 | API2:2023 Broken | JWT Token is not verified |
| | | | Authentication | |
| | Info | 0.0 | API8:2023 Security | HSTS Header is missing |
| | | | Misconfiguration | |
See the [Output Formats](./reference/output-formats) reference for full details on table columns, JSON/YAML formats, and CI/CD integration.
## Vulnerabilities Detected
All the vulnerabilities detected by the project are listed at this URL: [API Vulnerabilities Detected](./vulnerabilities).
> More vulnerabilities and best practices will be added in future releases. If you have any suggestions or requests for additional vulnerabilities or best practices to be included, please feel free to open an issue or submit a pull request.
## Proxy Support
The scanner supports proxy configurations for scanning APIs behind a proxy server. To use a proxy, set the `HTTP_PROXY` or `HTTPS_PROXY` environment variables with the proxy URL.
A command arg `--proxy` is also available to specify the proxy URL.
## Additional Options
See the [CLI Reference](./reference/cli) for complete command documentation.
## Telemetry
The scanner collects anonymous usage data to help improve the tool. This data includes the number of scans performed, number of detected vulnerabilities, and the severity of vulnerabilities. No sensitive information is collected. You can opt-out of telemetry by passing the `--sqa-opt-out` flag.
## Disclaimer
This scanner is provided for educational and informational purposes only. It should not be used for malicious purposes or to attack any system without proper authorization. Always respect the security and privacy of others.
================================================
FILE: docs/installation.mdx
================================================
---
title: Installation
description: Learn how to install VulnAPI.
sidebar:
order: 3
---
You can installed pre-built binaries of VulnAPI on Linux, Windows, and MacOS.
Below are the instructions to install VulnAPI on Linux, Windows, MacOS, and Docker. You can choose the installation method that best suits your needs and environment.
## Linux
### Snap
You can install VulnAPI on Linux using [Snap](https://snapcraft.io/vulnapi).
```bash
sudo snap install vulnapi
```
Verify:
```bash
vulnapi --version
```
### APT
Download the latest [VulnAPI release](https://github.com/cerberauth/vulnapi/releases?ref=deb) and install it using `dpkg`.
```bash
sudo dpkg -i vulnapi.deb
```
Verify:
```bash
vulnapi --version
```
### RPM
Download the latest [VulnAPI release](https://github.com/cerberauth/vulnapi/releases?ref=rpm) and install it using `rpm -i`.
```bash
sudo rpm -i vulnapi.rpm
```
Verify:
```bash
vulnapi --version
```
### Other
You can install VulnAPI on Linux by downloading the latest [VulnAPI release](https://github.com/cerberauth/vulnapi/releases?ref=other) and extracting the contents of the ZIP file. After extracting the contents, run the `vulnapi` binary from the command line.
Verify:
```bash
vulnapi --version
```
## Windows
### Chocolatey
You can install VulnAPI on Windows using [Chocolatey](https://chocolatey.org/packages/vulnapi).
```bash
choco install vulnapi
```
You can also install VulnAPI on Windows by downloading the latest [VulnAPI release](https://github.com/cerberauth/vulnapi/releases?ref=windows) and extracting the contents of the ZIP file. After extracting the contents, you can run the `vulnapi.exe` binary from the command line.
Verify:
```bash
vulnapi --version
```
## MacOS (Homebrew)
You can install VulnAPI on MacOS using Homebrew. To do so, run the following command:
```bash
brew tap cerberauth/vulnapi https://github.com/cerberauth/vulnapi
brew install $(brew --repository cerberauth/vulnapi)/vulnapi.rb
```
Verify:
```bash
vulnapi --version
```
## Docker
You can also use VulnAPI as a Docker container with [VulnaAPI Docker Image](https://hub.docker.com/r/cerberauth/vulnapi). To do so, run the following command:
```bash
docker run --rm cerberauth/vulnapi scan curl [API_URL] [CURL_OPTIONS]
```
## GitHub Action
VulnAPI can be integrated into your CI/CD pipeline using [GitHub Actions](./github-action). Integrating VulnAPI with GitHub Actions enables you to scan your APIs for vulnerabilities and security risks as part of your CI/CD pipeline. This allows you to automate security testing and vulnerability scanning of your APIs as part of your development workflow, ensuring that your APIs are **secure** and free from vulnerabilities **before they are deployed to production**.
## Upgrading
Keep VulnAPI up to date to get the latest vulnerability checks and bug fixes.
| Platform | Command |
|----------|---------|
| Snap | `sudo snap refresh vulnapi` |
| Chocolatey | `choco upgrade vulnapi` |
| Homebrew | `brew upgrade vulnapi` |
| Docker | `docker pull cerberauth/vulnapi` |
For APT/RPM installations, download the latest release package and reinstall using the same commands as above.
================================================
FILE: docs/labs.mdx
================================================
---
title: API Vulnerabilities Labs
description: Open Source vulnerability labs for practicing API security testing with VulnAPI.
sidebar:
order: 100
---
The following list of Open Source vulnerability labs offers resources for both beginners and experienced cybersecurity experts. These labs tends to cover a wide variety of API vulnerabilities, from the most basic to the most complex, allowing you to gain hands-on experience in identifying, exploiting, and mitigating security flaws. For now, this list is not so large but do not hesitate to contribute.
To enhance your API cybersecurity knowledge and skill set, we invite you to explore and contribute to this list of vulnerabilities labs:
| Lab | Vulnerability Documentation |
| ------------------------------------------------------------------------------------------------------------------------ | --------------------------- |
| [Authentication Not Verified](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/auth-not-verified) | |
| [JWT Alg None Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-alg-none-bypass) | [JWT Alg None Documentation](./vulnerabilities/broken-authentication/jwt-alg-none) |
| [JWT Blank Secret Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-blank-secret) | [JWT Blank Secret Documentation](./vulnerabilities/broken-authentication/jwt-blank-secret) |
| [JWT Not Verified Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-not-verified) | |
| [JWT Null Signature Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-null-signature) | [JWT Null Signature Documentation](./vulnerabilities/broken-authentication/jwt-null-signature) |
| [JWT Weak HMAC Secret Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-weak-hmac-secret) | [JWT Weak HMAC Secret Documentation](./vulnerabilities/broken-authentication/jwt-weak-secret) |
| [JWT Weak RSA Key Lab](https://github.com/cerberauth/api-vulns-challenges/tree/main/challenges/jwt-weak-rsa-key) | |
================================================
FILE: docs/reference/_meta.yml
================================================
label: Reference
================================================
FILE: docs/reference/cli/_meta.yml
================================================
label: CLI
================================================
FILE: docs/reference/cli/discover-api.mdx
================================================
---
title: discover api
description: Discover well-known paths and technology fingerprints for an API.
---
Probe a base URL for common API paths (e.g. `/openapi.json`, `/graphql`, `/.well-known/`) and read response headers to fingerprint the server framework and language.
## Usage
```sh
vulnapi discover api <URL> [flags]
```
Accepts all [common flags](./#common-flags).
## Output
The command prints two tables:
- **Well-known paths** — paths that returned a non-404 response, with their HTTP status codes.
- **Fingerprints** — detected technologies inferred from response headers (server, framework, language, etc.).
## Examples
**Discover well-known paths and fingerprints**
```sh
vulnapi discover api https://api.example.com
```
**Pass an Authorization header**
```sh
vulnapi discover api https://api.example.com \
-H "Authorization: Bearer <token>"
```
## Exit codes
| Code | Meaning |
|---|---|
| `0` | Discovery completed successfully |
| `1` | Fatal error (invalid URL, network failure, etc.) |
================================================
FILE: docs/reference/cli/discover-domain.mdx
================================================
---
title: discover domain
description: Discover API endpoints across common subdomains of a domain.
---
Enumerate common subdomains of a domain, perform reverse DNS lookups, and run the [discover api](./discover-api) checks against each discovered host.
## Usage
```sh
vulnapi discover domain <DOMAIN> [flags]
```
Accepts all [common flags](./#common-flags).
## Examples
**Discover APIs across all subdomains**
```sh
vulnapi discover domain example.com
```
**Limit the request rate**
```sh
vulnapi discover domain example.com --rate 5/s
```
## Exit codes
| Code | Meaning |
|---|---|
| `0` | Discovery completed successfully |
| `1` | Fatal error (invalid domain, network failure, etc.) |
================================================
FILE: docs/reference/cli/index.mdx
================================================
---
title: CLI reference
description: Overview of all VulnAPI CLI commands.
sidebar:
order: 1
---
import { LinkCard, CardGrid } from '@/components/docs';
The `vulnapi` CLI covers API security scanning, discovery, and JWT utilities.
<CardGrid>
<LinkCard title="scan curl" description="Scan a single API endpoint using curl-style options." href="./scan-curl/" />
<LinkCard title="scan openapi" description="Scan all endpoints defined in an OpenAPI specification." href="./scan-openapi/" />
<LinkCard title="scan graphql" description="Scan a GraphQL endpoint." href="./scan-graphql/" />
<LinkCard title="discover api" description="Discover well-known paths and technology fingerprints for an API." href="./discover-api/" />
<LinkCard title="discover domain" description="Discover API endpoints across common subdomains." href="./discover-domain/" />
<LinkCard title="jwt generate" description="Generate a new JWT from an existing token with a different algorithm or key." href="./jwt-generate/" />
<LinkCard title="serve" description="Start the VulnAPI HTTP server in REST API mode." href="./serve/" />
</CardGrid>
## Global usage
```sh
vulnapi <command> [flags]
vulnapi --help
vulnapi version
```
All commands exit `0` on success and non-zero on error.
## Global flags
| Flag | Description |
|---|---|
| `--sqa-opt-out` | Opt out of anonymous usage statistics and crash reporting |
| `-h, --help` | Show help for any command |
## Common flags
All `scan` and `discover` commands share these flags. HTTP client flags follow the same conventions as `curl`.
| Flag | Default | Description |
|---|---|---|
| `--rate <rate>` | `` | Request rate limit (e.g. `10/s`, `1/m`) |
| `-x, --proxy <url>` | `` | Proxy URL for all requests |
| `-H, --header <header>` | `` | Header to include in requests (repeatable) |
| `-b, --cookie <cookie>` | `` | Cookie to include in requests (repeatable) |
| `-u, --user <user:pass>` | `` | Username and password for HTTP Basic authentication |
| `--basic` | `` | Use HTTP Basic Auth (use with `-u`) |
| `--digest` | `` | Use HTTP Digest Auth (use with `-u`) |
| `--scans <scan-id>` | `` | Run only specific scans (repeatable; supports wildcards like `jwt.*`) |
| `--exclude-scans <scan-id>` | `` | Exclude specific scans (repeatable) |
| `--report-format <format>` | `table` | Output format: `table`, `json`, `yaml` |
| `--report-transport <transport>` | `file` | Report transport: `file`, `http` |
| `--report-file <path>` | `` | File path to write the report to |
| `--report-url <url>` | `` | URL to send the report to (when `--report-transport http`) |
| `--no-progress` | `false` | Disable the progress bar |
| `--severity-threshold <score>` | `1.0` | CVSS threshold above which output is also written to stderr |
> **Deprecated:** `--rate-limit` (`-r`) was replaced by `--rate`. The old flag is still accepted as an alias.
## Exit behaviour
When at least one finding has a CVSS score above `--severity-threshold`, VulnAPI writes output to **stderr** in addition to stdout. This causes `|| exit 1` patterns and CI/CD failure checks to trigger automatically. The default threshold is `1.0`, meaning any finding with CVSS > 1.0 triggers stderr output.
================================================
FILE: docs/reference/cli/jwt-generate.mdx
================================================
---
title: jwt generate
description: Generate a new JWT from an existing token, re-signed with a different algorithm or key.
---
Parse an existing JWT and re-sign it with a new algorithm and key. The original claims are preserved. Useful for manually crafting tokens to test JWT vulnerability mitigations.
## Usage
```sh
vulnapi jwt generate <TOKEN> --alg <alg> --secret <secret>
```
## Flags
| Flag | Description |
|---|---|
| `--alg <algorithm>` | Signing algorithm **(required)**: `HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `NONE` |
| `--secret <secret>` | Secret key to sign the token (required for HMAC algorithms) |
| `--aud <audience>` | Audience claim (`aud`) to set in the token |
## Examples
**Re-sign with a different HMAC secret**
```sh
vulnapi jwt generate eyJhbGciOiJIUzI1NiI... --alg HS256 --secret newsecret
```
**Strip the signature (alg=none)**
```sh
vulnapi jwt generate eyJhbGciOiJIUzI1NiI... --alg NONE
```
**Re-sign with a custom audience**
```sh
vulnapi jwt generate eyJhbGciOiJIUzI1NiI... --alg HS256 --secret mysecret --aud myservice
```
## Output
The signed token string is printed to stdout.
## Exit codes
| Code | Meaning |
|---|---|
| `0` | Token generated successfully |
| `1` | Invalid input token, unknown algorithm, or missing required flags |
================================================
FILE: docs/reference/cli/scan-curl.mdx
================================================
---
title: scan curl
description: Scan a single API endpoint using curl-style options.
---
Scan a single API endpoint by specifying a URL and optional HTTP method and body. Accepts the same flags as `curl` for familiarity when pasting commands from documentation or browser DevTools.
## Usage
```sh
vulnapi scan curl <URL> [flags]
```
## Flags
| Flag | Default | Description |
|---|---|---|
| `-X, --request <method>` | `GET` | HTTP method to use |
| `-d, --data <data>` | `` | HTTP request body |
Also accepts all [common flags](./#common-flags).
## Examples
**Simple GET request**
```sh
vulnapi scan curl https://api.example.com/endpoint
```
**POST with a JSON body and an Authorization header**
```sh
vulnapi scan curl https://api.example.com/endpoint \
-X POST \
-d '{"key":"value"}' \
-H "Authorization: Bearer <token>"
```
**Run only JWT-related scans**
```sh
vulnapi scan curl https://api.example.com/endpoint \
-H "Authorization: Bearer <token>" \
--scans "jwt.*"
```
**Write the report as JSON to a file**
```sh
vulnapi scan curl https://api.example.com/endpoint \
--report-format json \
--report-file report.json
```
## Exit codes
| Code | Meaning |
|---|---|
| `0` | Scan completed successfully |
| `1` | Fatal error (invalid URL, network failure, etc.) |
When a finding exceeds `--severity-threshold`, additional output is written to stderr. See [exit behaviour](./#exit-behaviour).
================================================
FILE: docs/reference/cli/scan-graphql.mdx
================================================
---
title: scan graphql
description: Scan a GraphQL endpoint for security vulnerabilities.
---
Run security scans against a GraphQL endpoint, including checks for introspection exposure and other GraphQL-specific vulnerabilities.
## Usage
```sh
vulnapi scan graphql <ENDPOINT> [flags]
```
Accepts all [common flags](./#common-flags).
## Examples
**Scan a GraphQL endpoint with a bearer token**
```sh
vulnapi scan graphql https://api.example.com/graphql \
-H "Authorization: Bearer <token>"
```
**Scan through a proxy**
```sh
vulnapi scan graphql https://api.example.com/graphql \
-H "Authorization: Bearer <token>" \
--proxy http://localhost:8080
```
**Output results as JSON**
```sh
vulnapi scan graphql https://api.example.com/graphql \
--report-format json \
--report-file report.json
```
## Exit codes
| Code | Meaning |
|---|---|
| `0` | Scan completed successfully |
| `1` | Fatal error (invalid URL, network failure, etc.) |
When a finding exceeds `--severity-threshold`, additional output is written to stderr. See [exit behaviour](./#exit-behaviour).
================================================
FILE: docs/reference/cli/scan-openapi.mdx
================================================
---
title: scan openapi
description: Scan all endpoints defined in an OpenAPI specification.
---
Load an OpenAPI specification (local file or remote URL) and run security scans against every operation it defines.
## Usage
```sh
vulnapi scan openapi <PATH_OR_URL> [flags]
```
## Flags
| Flag | Default | Description |
|---|---|---|
| `--security-schemes <name=value>` | `` | Override the token value for a named security scheme (repeatable) |
Also accepts all [common flags](./#common-flags).
## Token via stdin
A token piped via stdin is used as the default value for all security schemes that accept a bearer token. Named overrides from `--security-schemes` take precedence.
```sh
echo "<token>" | vulnapi scan openapi ./openapi.json
```
## Examples
**Local spec with a token piped via stdin**
```sh
echo "<token>" | vulnapi scan openapi ./openapi.json
```
**Remote spec with a token piped via stdin**
```sh
echo "<token>" | vulnapi scan openapi https://api.example.com/.well-known/openapi.json
```
**Named security scheme override**
```sh
vulnapi scan openapi ./openapi.json --security-schemes bearerAuth=<token>
```
**Multiple scheme overrides**
```sh
vulnapi scan openapi ./openapi.json \
--security-schemes bearerAuth=<token> \
--security-schemes apiKey=<key>
```
## Exit codes
| Code | Meaning |
|---|---|
| `0` | Scan completed successfully |
| `1` | Fatal error (invalid spec, network failure, etc.) |
When a finding exceeds `--severity-threshold`, additional output is written to stderr. See [exit behaviour](./#exit-behaviour).
================================================
FILE: docs/reference/cli/serve.mdx
================================================
---
title: serve
description: Start the VulnAPI HTTP server in REST API mode.
---
Start VulnAPI as an HTTP server that exposes a REST API, allowing scans to be triggered programmatically rather than through the CLI.
## Usage
```sh
vulnapi serve [flags]
```
## Flags
| Flag | Default | Description |
|---|---|---|
| `-p, --port <port>` | `8080` | Port to listen on |
## Examples
**Start on the default port**
```sh
vulnapi serve
```
**Start on a custom port**
```sh
vulnapi serve --port 9090
```
## Exit codes
| Code | Meaning |
|---|---|
| `0` | Server exited cleanly |
| `1` | Fatal error (port already in use, etc.) |
================================================
FILE: docs/reference/output-formats.mdx
================================================
---
title: Output Formats
description: Reference for VulnAPI's table, JSON, and YAML output formats and CI/CD severity threshold integration.
---
VulnAPI supports three output formats: `table` (default), `json`, and `yaml`. Use `--report-format` to select the format and `--report-transport` + `--report-file` / `--report-url` to control where the output goes.
## Table (Default)
The table format prints human-readable ASCII tables to the terminal. It consists of up to three sub-tables:
**Well-Known Paths** — discovered API spec and GraphQL endpoints:
```
| WELL-KNOWN PATHS | URL |
|------------------|------------------------------------|
| OpenAPI | http://localhost:5000/openapi.json |
| GraphQL | N/A |
```
**Technology Fingerprint** — framework, language, and server detected from response headers:
```
| TECHNOLOGIE/SERVICE | VALUE |
|---------------------|---------------|
| Framework | Flask:2.2.3 |
| Language | Python:3.11.9 |
| Server | Flask:2.2.3 |
```
**Vulnerability List** — each finding on its own row:
| Column | Description |
|--------|-------------|
| **OPERATION** | HTTP method + path tested (e.g. `GET /api/users`) |
| **RISK LEVEL** | Human-readable severity: Info / Low / Medium / High / Critical |
| **CVSS 4.0 SCORE** | Numeric score 0.0–10.0 |
| **OWASP** | Relevant OWASP API Security Top 10 category |
| **VULNERABILITY** | Short description of the finding |
An advice line above the table summarises the overall risk level.
## JSON
Export a structured JSON report to a file:
```bash
vulnapi scan curl https://api.example.com/endpoint \
-H "Authorization: Bearer <token>" \
--report-format json \
--report-transport file \
--report-file report.json
```
The JSON output contains an array of scan results, each with operation details, issue metadata, CVSS score, OWASP classification, and scan attempt responses. Use this format for automated processing, dashboards, or storing artifacts in CI/CD.
## YAML
```bash
vulnapi scan curl https://api.example.com/endpoint \
--report-format yaml \
--report-transport file \
--report-file report.yaml
```
The YAML format contains the same data as the JSON format in YAML syntax.
## Severity Threshold and CI/CD Integration
The `--severity-threshold` flag (default `1.0`) controls when VulnAPI writes findings to **stderr**. If any finding has a CVSS score strictly above the threshold, the output is also written to stderr.
This enables CI/CD pipeline failure detection without a wrapper script:
```bash
# Fail the pipeline if any finding has CVSS > 5.0
vulnapi scan curl https://api.example.com/endpoint \
-H "Authorization: Bearer <token>" \
--severity-threshold 5.0 \
2>/dev/null || exit 1
```
```yaml
# GitHub Actions example
- name: VulnAPI Scan
run: |
vulnapi scan openapi ./openapi.json \
--severity-threshold 7.0 \
--report-format json \
--report-file vulnapi-report.json
# The step will fail automatically if CVSS > 7.0 is found (stderr output)
```
Set `--severity-threshold 0` to always write to stderr regardless of CVSS score.
================================================
FILE: docs/reference/scan-ids.mdx
================================================
---
title: Scan IDs Reference
description: Complete reference table of all VulnAPI scan IDs, categories, and severity scores.
---
Use scan IDs with `--scans` and `--exclude-scans` flags to filter which checks VulnAPI runs.
```bash
# Run only JWT checks
vulnapi scan curl https://api.example.com -H "Authorization: Bearer <token>" --scans jwt.alg_none
# Run all JWT checks using a wildcard
vulnapi scan curl ... --scans "jwt.*"
# Exclude all discovery checks
vulnapi scan curl ... --exclude-scans "discover.*"
```
## Broken Authentication
| Scan ID | Name | OWASP | CVSS |
|---------|------|-------|------|
| `jwt.alg_none` | JWT Accepts `alg: none` | API2:2023 | High |
| `jwt.blank_secret` | JWT Signed With Blank Secret | API2:2023 | High |
| `jwt.weak_secret` | JWT Signed With Weak Secret | API2:2023 | High |
| `jwt.kid_injection` | JWT KID Header Injection (SQL injection & path traversal) | API2:2023 | 9.3 |
| `jwt.not_verified` | JWT Signature Not Verified | API2:2023 | 9.3 |
| `jwt.null_signature` | JWT Null Signature | API2:2023 | High |
| `generic.accept_unauthenticated_operation` | Endpoint Accepts Unauthenticated Requests | API2:2023 | 9.3 |
## Security Misconfiguration
| Scan ID | Name | OWASP | CVSS |
|---------|------|-------|------|
| `misconfiguration.http_headers` | Security Response Headers Missing | API8:2023 | Info–Medium |
| `misconfiguration.http_cookies` | Cookie Security Flags Misconfigured | API8:2023 | 0.0 |
| `misconfiguration.http_trace` | HTTP TRACE Method Enabled | API8:2023 | Info |
| `misconfiguration.http_track` | HTTP TRACK Method Enabled | API8:2023 | Info |
| `misconfiguration.http_method_override` | HTTP Method Override Headers Accepted | API8:2023 | Info–High |
## GraphQL
| Scan ID | Name | OWASP | CVSS |
|---------|------|-------|------|
| `graphql.introspection_enabled` | GraphQL Introspection Enabled | API8:2023 | Low |
## Discovery
Discovery scans run automatically with `vulnapi discover` commands. They can also be included/excluded on scan commands.
| Scan ID | Name | Description |
|---------|------|-------------|
| `discover.api` | API Fingerprint | Detects framework, language, and server from response headers |
| `discover.well_known_paths` | Well-Known Path Discovery | Checks for OpenAPI, GraphQL, and other common paths |
| `discover.domain` | Domain Discovery | Common subdomain patterns and reverse DNS lookup |
## HTTP Security Headers (sub-checks of `misconfiguration.http_headers`)
The `misconfiguration.http_headers` scan covers multiple individual header checks. You can exclude the parent scan ID to skip all of them.
| Header | Risk |
|--------|------|
| `Content-Security-Policy` | Medium |
| `X-Frame-Options` | Medium |
| `X-Content-Type-Options` | Info |
| `Strict-Transport-Security` | Info |
| `Access-Control-Allow-Origin` (CORS) | Medium |
| Server signature leak | Info |
## Cookie Security (sub-checks of `misconfiguration.http_cookies`)
The `misconfiguration.http_cookies` scan covers five individual cookie flag checks:
| Check ID | Description | CWE |
|----------|-------------|-----|
| `security_misconfiguration.http_cookies_not_http_only` | Cookie accessible via JavaScript (XSS risk) | CWE-1004 |
| `security_misconfiguration.http_cookies_not_secure` | Cookie transmitted over HTTP | CWE-614 |
| `security_misconfiguration.http_cookies_same_site_none` | `SameSite=None` allows cross-site requests | CWE-1275 |
| `security_misconfiguration.http_cookies_without_same_site` | `SameSite` attribute not set | CWE-1275 |
| `security_misconfiguration.http_cookies_without_expires` | Session cookie never expires | CWE-613 |
================================================
FILE: docs/vampi.mdx
================================================
---
title: VulnAPI Training on VAmPI (Vulnerable REST API)
description: Train yourself on how to use VulnAPI to scan REST APIs for vulnerabilities and security risks.
sidebar:
order: 5
---
VulnAPI is a powerful tool that allows you to scan REST APIs for vulnerabilities and security risks. This training module will guide you through the process of using VulnAPI to scan your APIs for vulnerabilities and security risks.
VAmPI is a vulnerable REST API with OWASP Top 10 vulnerabilities. You can use VAmPI to practice scanning REST APIs for vulnerabilities and security risks using VulnAPI.
## Prerequisites
Before you begin this training module, you need to have the following prerequisites:
- [VulnAPI](./installation) installed on your machine.
- [VAmPI](https://github.com/erev0s/VAmPI) installed on your machine.
## Training Objectives
By the end of this training module, you will be able to:
- Scan a REST API for vulnerabilities and security risks using VulnAPI.
- Identify and remediate vulnerabilities and security risks in a REST API.
## Training Steps
Follow the steps below to complete this training module:
1. Start the VAmPI server by running the following command:
```bash
docker run -e vulnerable=1 -p 5000:5000 erev0s/vampi
```
2. Open a new terminal window and run the following command to make a discovery scan of the VAmPI API using VulnAPI:
```bash
vulnapi scan discover http://localhost:5000
```
3. Review the scan results to identify OpenAPI Contract and additionnal service informations.
4. Download the OpenAPI Contract and save it to a file named `vampi-openapi.json`. Edit the file, add a base url to the server and security scheme.
The security should be added for the endpoint `PUT /users/v1/{username}/email`, `PUT /users/v1/{username}/password` and `DELETE /users/v1/{username}` with the `bearerAuth` security scheme.
```json
{
"components": {
"securitySchemes": {
"bearerAuth": {
"bearerFormat": "JWT",
"scheme": "bearer",
"type": "http"
}
}
},
"info": {
"description": "OpenAPI v3 specs for VAmPI",
"title": "VAmPI",
"version": "0.1"
},
"openapi": "3.0.1",
"paths": {
"/users/v1/{username}/email": {
"put": {
"description": "Update a single users email",
"operationId": "api_views.users.update_email",
"security": [
{
"bearerAuth": []
}
],
...
"servers": [
{
"url": "http://localhost:5000"
}
]
}
```
5. Run the following command to scan the VAmPI API using the OpenAPI Contract:
```bash
vulnapi scan openapi vampi-openapi.json
```
6. Review the scan results to identify vulnerabilities and security risks in the VAmPI API.
================================================
FILE: docs/vulnerabilities/_meta.yml
================================================
label: Vulnerabilities
================================================
FILE: docs/vulnerabilities/broken-authentication/_meta.yml
================================================
label: Broken Authentication
================================================
FILE: docs/vulnerabilities/broken-authentication/authentication-bypass.mdx
================================================
---
title: Authentication Bypass
description: A vulnerability where an endpoint that requires authentication accepts requests with no credentials, allowing unauthenticated access.
---
import { Tabs, TabItem } from '@/components/docs'
<table>
<tr>
<th>Scan ID</th>
<td><code>generic.accept_unauthenticated_operation</code></td>
</tr>
<tr>
<th>Severity</th>
<td>High</td>
</tr>
<tr>
<th>CVSS 4.0</th>
<td>9.3 — <code>CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N</code></td>
</tr>
<tr>
<th>OWASP Category</th>
<td>
<a href="https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/">OWASP API2:2023 Broken Authentication</a>
</td>
</tr>
</table>
The "Authentication Bypass" vulnerability occurs when an endpoint that is expected to require authentication returns a success response (2xx) when the request contains no credentials at all. VulnAPI detects this by sending a request to the endpoint without any `Authorization` header, cookies, or other credentials and checking if the response indicates success rather than returning a `401 Unauthorized` or `403 Forbidden`.
## Example
**Normal authenticated request** (should succeed):
```http
GET /api/users/profile HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiI...
HTTP/1.1 200 OK
```
**Unauthenticated request** (should be rejected — but is vulnerable if it returns 200):
```http
GET /api/users/profile HTTP/1.1
HTTP/1.1 200 OK ← vulnerable!
```
## How to test?
<Tabs>
<TabItem label="cURL">
```bash
vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans generic.accept_unauthenticated_operation
```
</TabItem>
<TabItem label="OpenAPI">
```bash
echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans generic.accept_unauthenticated_operation
```
</TabItem>
<TabItem label="GraphQL">
```bash
vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans generic.accept_unauthenticated_operation [url]
```
</TabItem>
</Tabs>
VulnAPI supports scanning against various types of other [vulnerabilities](../) as well.
## What is the impact?
If an attacker can access a protected endpoint without any credentials, they can:
- **Access sensitive data**: read user profiles, financial records, private messages, or any other data the endpoint returns
- **Perform privileged operations**: create, modify, or delete resources without being logged in
- **Bypass the entire authentication layer**: any security controls that depend on authentication become ineffective
- **Enumerate users or data**: scrape all data from the API without needing an account
CVSS 9.3 reflects that this is network-exploitable with no prior authentication required and provides full read/write access.
## How to remediate?
1. **Enforce authentication at every protected endpoint**: every route that serves or modifies user data must validate credentials before processing the request.
2. **Return 401 for unauthenticated requests**: always return `401 Unauthorized` (not `200`, `404`, or `500`) when no valid credentials are provided. A `403 Forbidden` is appropriate when the user is authenticated but lacks the required permissions.
3. **Apply authentication middleware globally**: use a gateway-level or framework-level middleware that intercepts all requests and enforces authentication, rather than adding auth checks endpoint by endpoint.
4. **Test unauthenticated access for every new endpoint**: add an automated test that verifies each protected endpoint returns 401 when no credentials are provided.
```python
# FastAPI example — require authentication globally
from fastapi import Depends, HTTPException, Security
from fastapi.security import HTTPBearer
security = HTTPBearer()
@app.get("/api/users/profile")
async def get_profile(credentials = Security(security)):
# credentials.credentials is the Bearer token
user = verify_token(credentials.credentials)
return user
```
================================================
FILE: docs/vulnerabilities/broken-authentication/brute-force-attack.mdx
================================================
---
title: API Authentication Brute Force Attack
description: A brute force attack, in the context of API authentication, is a trial-and-error method used by attackers to guess the correct credentials (username and password) or access tokens. Attackers use automated tools to make multiple requests to the authentication endpoint, trying different combinations of credentials until they find the correct one. This attack can lead to unauthorized access to the API and sensitive data.
---
<table>
<tr>
<th>Severity</th>
<td>High</td>
</tr>
<tr>
<th>CVEs</th>
<td>
<ul>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-51558">CVE-2024-51558</a></li>
</ul>
</td>
</tr>
<tr>
<th>Classifications</th>
<td>
<ul>
<li><a href="https://cwe.mitre.org/data/definitions/287.html">CWE-287: Improper Authentication</a></li>
<li><a href="https://cwe.mitre.org/data/definitions/307.html">CWE-307: Improper Restriction of Excessive Authentication Attempts</a></li>
</ul>
</td>
</tr>
<tr>
<th>OWASP Category</th>
<td>
<a href="https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/">OWASP API2:2023 Broken Authentication</a>
</td>
</tr>
</table>
A brute force attack, in the context of API authentication, is a trial-and-error method used by attackers to guess the correct credentials (username and password) or access tokens. Attackers use automated tools to make multiple requests to the authentication endpoint, trying different combinations of credentials until they find the correct one. This attack can lead to unauthorized access to the API and sensitive data.
## Example
TODO: Add an example of a brute force attack on an API authentication endpoint.
## How to test?
TODO: Add steps to test for the API authentication brute force attack vulnerability.
## What is the impact?
When an API authentication endpoint is vulnerable to brute force attacks, attackers can gain unauthorized access to the API and sensitive data. This can lead to data breaches, data manipulation, and other security incidents.
## How to remediate?
To prevent brute force attacks on API authentication endpoints, consider implementing the following security measures. If it is an user authentication, you can:
* Implement account lockout mechanisms after a certain number of failed login attempts for user authentication.
* Implement CAPTCHA or reCAPTCHA challenges to prevent automated brute force attacks.
In any case, you can:
* Implement rate limiting on authentication requests to prevent multiple failed login attempts.
* Monitor authentication logs for suspicious activities and anomalies.
================================================
FILE: docs/vulnerabilities/broken-authentication/jwt-alg-none.mdx
================================================
---
title: JWT None Algorithm
description: Accepting the "none" algorithm in a JSON Web Token (JWT) occurs when a JWT is signed with the "none" algorithm, it means there is no signature, making it easy for attackers to tamper with the token's content without detection.
---
import { Tabs, TabItem } from '@/components/docs'
<table>
<tr>
<th>Severity</th>
<td>High</td>
</tr>
<tr>
<th>CVEs</th>
<td>
<ul>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2015-9235">CVE-2015-9235</a></li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2015-2951">CVE-2015-2951</a></li>
</ul>
</td>
</tr>
<tr>
<th>Classifications</th>
<td>
<ul>
<li><a href="https://cwe.mitre.org/data/definitions/345.html">CWE-345: Insufficient Verification of Data Authenticity</a></li>
<li><a href="https://cwe.mitre.org/data/definitions/327.html">CWE-327: Use of a Broken or Risky Cryptographic Algorithm</a></li>
<li><a href="https://cwe.mitre.org/data/definitions/20.html">CWE-20: Improper Input Validation</a></li>
</ul>
</td>
</tr>
<tr>
<th>OWASP Category</th>
<td>
<a href="https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/">OWASP API2:2023 Broken Authentication</a>
</td>
</tr>
</table>
Accepting the "none" algorithm in a JSON Web Token (JWT) occurs when a JWT is signed with the "none" algorithm, it means there is no signature, making it easy for attackers to tamper with the token's content without detection. This can lead to unauthorized access and data manipulation.
## Example
Here is a valid JWT signed with RS512 algorithm:
```bash
eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.MnECRBSUQEi8GjiAyWHPhPhhpzCiMLldkq-N_VS-iwI08c4xEVUhT1Xrx9kNGuwusiQuLI3AOBTPwtbdaasQDCOpF0nxxQNKkufJYFds61ooFZfXCuRyXe1yGnXPRzTfgr5YVe9-T8_JDccx5JP70d9hoO4DU4GNYQMvrOQl4xu8DEyyDT2hsjyTbrodVhrV9znMfEBCsYPPLI-Q-HYLquGThPdJe2kBNA-CiLRV6Mwzji67cTd_4P_oUHKXsAxMqVpo-xC2xiVpO2P9X1__uXrRrfiNFUur4B71UMgGYJ2z_cQqwFfSXz9glBIf_-BJU10Rkmyo2ew862d7WsHx8g
```
This decoded JWT contains, this parts:
```json
{
"alg": "RS512",
"typ": "JWT"
}
```
```json
{
"sub": "1234567890",
"iat": 1516239022
}
```
The following JWT is invalid but is wrongly authorized when the implementation is vulnerable to this attack.
```bash
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNTE2MjM5MDIyfQ.
```
Invalid JWT has this header with algorithm set to none.
```json
{
"alg": "none",
"typ": "JWT"
}
```
## How to test?
If you want to test only the "JWT Alg None" vulnerability, you can use the following command:
<Tabs>
<TabItem label="cURL">
```bash
vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.alg_none
```
</TabItem>
<TabItem label="OpenAPI">
```bash
echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.alg_none
```
</TabItem>
<TabItem label="GraphQL">
```bash
vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.alg_none [url]
```
</TabItem>
</Tabs>
VulnAPI supports scanning against various types of other [vulnerabilities](../) as well.
## What is the impact?
The potential security impacts of the JWT "alg none" vulnerability are significant and can include:
- **Unauthorized Access**: Attackers can tamper with the JWT payload to modify user roles, permissions, or identifiers, granting them unauthorized access to resources or functionalities within the system.
- **Data Tampering**: Without the signature to ensure integrity, attackers can modify the data within the JWT payload, potentially altering sensitive information such as user details, session parameters, or application state.
- **Impersonation**: Attackers can forge JWTs by crafting their own payloads and presenting them as valid tokens, leading to impersonation attacks where they assume the identity of legitimate users or systems.
- **Privilege Escalation**: Manipulating the JWT payload, attackers may elevate their privileges within the system by granting themselves higher roles or permissions than originally assigned.
- **Replay Attacks**: Attackers can capture and replay valid JWTs since there's no signature validation, allowing them to reuse the tokens to gain unauthorized access or perform malicious actions.
- **Denial of Service (DDoS)**: In some cases, attackers may exploit the vulnerability to craft JWTs with payloads that cause unexpected behavior or errors within the application, potentially leading to service disruptions or system crashes.
- **Bypassing Security Controls**: In systems where JWTs are used for access control (authorization) or authentication, the "alg none" vulnerability can bypass security controls altogether, rendering any security mechanisms relying on JWTs ineffective.
## How to remediate?
Remediating the JWT "alg none" vulnerability is to ensure that the JWT library or implementation being used is not vulnerable to this issue and is correctly configured.
- **Updating the Library**: Ensure that your JWT library is updated to a version that addresses the "alg none" vulnerability. Check regularly for security updates and patches provided by the library maintainers.
- **Configuration for Algorithm Check**: Configure the JWT library or implementation to enforce checking the algorithm used in the JWT header. Specifically, configure it to reject tokens that are signed using the "none" algorithm. This ensures that only tokens signed with secure algorithms (e.g., HMAC, RSA) are accepted.
================================================
FILE: docs/vulnerabilities/broken-authentication/jwt-blank-secret.mdx
================================================
---
title: JWT Blank Secret
description: A vulnerability occurs when a JSON Web Token (JWT) is signed with an empty secret. In this scenario, the token lacks proper cryptographic protection, making it susceptible to manipulation.
---
import { Tabs, TabItem } from '@/components/docs'
<table>
<tr>
<th>Severity</th>
<td>High</td>
</tr>
<tr>
<th>CVEs</th>
<td>
<ul>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2019-20933">CVE-2019-20933</a></li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2020-28637">CVE-2020-28637</a></li>
</ul>
</td>
</tr>
<tr>
<th>Classifications</th>
<td>
<a href="https://cwe.mitre.org/data/definitions/287.html">CWE-287: Improper Authentication</a>
</td>
</tr>
<tr>
<th>OWASP Category</th>
<td>
<a href="https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/">OWASP API2:2023 Broken Authentication</a>
</td>
</tr>
</table>
A vulnerability occurs when a JSON Web Token (JWT) is signed with an empty secret. In this scenario, the token lacks proper cryptographic protection, making it susceptible to manipulation. Attackers can modify the token's claims and content without detection, potentially leading to unauthorized access and data tampering.
## Example
Here is a valid JWT signed with HS256 algorithm:
```bash
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTYyNDI2MjIsImlhdCI6MTUxNjIzOTAyMiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.SCC35SSgMSMr0kV1i_TuPAhiSGtsC1cFGCfvaus5GyU
```
This decoded JWT contains, this parts:
```json
{
"alg": "HS256",
"typ": "JWT"
}
```
```json
{
"iat": 1516239022,
"exp": 1516242622,
"name": "John Doe",
"sub": "2cb307ba-bb46-4194-854f-4774046d9c9b"
}
```
The following JWT is signed with an empty secret (blank key — same header and payload, different signature):
```bash
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MTYyNDI2MjIsImlhdCI6MTUxNjIzOTAyMiwibmFtZSI6IkpvaG4gRG9lIiwic3ViIjoiMmNiMzA3YmEtYmI0Ni00MTk0LTg1NGYtNDc3NDA0NmQ5YzliIn0.rpOjSoJBjzZifWAc-MBQ4UVS9tOY7gJV8cN0j6bN1oA
```
## How to test?
If you want to test only the "JWT Blank Secret" vulnerability, you can use the following command:
<Tabs>
<TabItem label="cURL">
```bash
vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.blank_secret
```
</TabItem>
<TabItem label="OpenAPI">
```bash
echo "eyJhbGciOiJSUzUxMiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.blank_secret
```
</TabItem>
<TabItem label="GraphQL">
```bash
vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJSUzUxMiI..." --scans jwt.blank_secret [url]
```
</TabItem>
</Tabs>
VulnAPI supports scanning against various types of other [vulnerabilities](../) as well.
## What is the impact?
Signing a JWT with a blank secret has a significant impact on the security of the token. A blank secret means that there is no secret key used to sign the token, making it vulnerable to tampering and unauthorized access.
By signing a JWT with a blank secret, anyone with access to the token can modify its contents without detection. This can lead to various security risks, such as impersonation, data tampering, and unauthorized access to protected resources.
## How to remediate?
To remediate the JWT blank secret vulnerability, ensure that all JWTs are signed with a secure secret key. Use strong cryptographic algorithms and keep the secret key confidential to prevent unauthorized access and tampering of the tokens.
================================================
FILE: docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw
================================================
{
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": [
{
"type": "text",
"version": 2035,
"versionNonce": 1818462084,
"index": "b6H",
"isDeleted": false,
"id": "ltyIvJkLl-pTJBLTGiDvt",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -505.87472326456395,
"y": 2309.5011349340184,
"strokeColor": "#000000",
"backgroundColor": "white",
"width": 103.51054382324219,
"height": 28.8310711010254,
"seed": 1804201402,
"groupIds": [
"TafrumkQql6FUeTFdBMm-"
],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152817116,
"link": null,
"locked": false,
"fontSize": 23.064856880820322,
"fontFamily": 1,
"text": "End User",
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "End User",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "line",
"version": 2123,
"versionNonce": 1628436228,
"index": "b6I",
"isDeleted": false,
"id": "pijBT8vPR7p7LIf6w_dcC",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": -476.24781473924645,
"y": 2292.555987811961,
"strokeColor": "#000000",
"backgroundColor": "#ced4da",
"width": 56.819606873238214,
"height": 50.596228152466416,
"seed": 104343162,
"groupIds": [
"XJCcWbsBNKnnIezwbpXsC",
"TafrumkQql6FUeTFdBMm-"
],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [],
"updated": 1730152817116,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null,
"points": [
[
0,
0
],
[
6.363795969802675,
-33.527621064887384
],
[
27.273411299154343,
-50.596228152466416
],
[
48.183026628506006,
-37.185179726511414
],
[
56.819606873238214,
-3.481418584602479
],
[
0,
0
]
]
},
{
"type": "ellipse",
"version": 1860,
"versionNonce": 1971484292,
"index": "b6J",
"isDeleted": false,
"id": "pcQc9n9kEAl-OVAUytn-b",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": -461.88547731665426,
"y": 2215.6450141913247,
"strokeColor": "#000000",
"backgroundColor": "#ced4da",
"width": 29.091638719097993,
"height": 25.455183879210832,
"seed": 1210140474,
"groupIds": [
"XJCcWbsBNKnnIezwbpXsC",
"TafrumkQql6FUeTFdBMm-"
],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152817116,
"link": null,
"locked": false
},
{
"type": "rectangle",
"version": 2228,
"versionNonce": 171340604,
"index": "b8p",
"isDeleted": false,
"id": "IDPR-T_GInZ_SYrUKdyiV",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 232.9885694829697,
"y": 1901.165608184967,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 215.2445549281264,
"height": 191.3848126773838,
"seed": 553856166,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 3
},
"boundElements": [
{
"id": "NIxHFrLYNc4GZ_eaCUNEX",
"type": "text"
},
{
"id": "QM3OeE-gZsJtTqpVyK6WY",
"type": "arrow"
},
{
"id": "L746tARKZ5EBHf_R0vgrz",
"type": "arrow"
},
{
"id": "KNsMX2SBrRRRZSGEwUC8b",
"type": "arrow"
},
{
"id": "1DW6jYie7TBBMPB2Tb6PL",
"type": "arrow"
}
],
"updated": 1730152887733,
"link": null,
"locked": false
},
{
"type": "text",
"version": 2546,
"versionNonce": 234662844,
"index": "b8q",
"isDeleted": false,
"id": "NIxHFrLYNc4GZ_eaCUNEX",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 283.41664833863445,
"y": 1961.8580145236588,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 114.38839721679688,
"height": 70,
"seed": 2122491878,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152887733,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 5,
"text": "Token\nProvider",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "IDPR-T_GInZ_SYrUKdyiV",
"originalText": "Token Provider",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "rectangle",
"version": 2186,
"versionNonce": 741797636,
"index": "b91",
"isDeleted": false,
"id": "0pKo0FuXTqVcAnFHg3eHW",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -205.52933980495288,
"y": 2178.953941971566,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"width": 215.2445549281264,
"height": 191.3848126773838,
"seed": 236441020,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 3
},
"boundElements": [
{
"id": "L9qPfblCPCj76bIqH4zPF",
"type": "text"
},
{
"id": "5zc8kBUOyFDyV3r5cJ5u1",
"type": "arrow"
},
{
"id": "GMVGnrJnqaFoheEbd232T",
"type": "arrow"
},
{
"id": "1DW6jYie7TBBMPB2Tb6PL",
"type": "arrow"
}
],
"updated": 1730152887733,
"link": null,
"locked": false
},
{
"type": "text",
"version": 2442,
"versionNonce": 493343804,
"index": "b92",
"isDeleted": false,
"id": "L9qPfblCPCj76bIqH4zPF",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -187.50743465534282,
"y": 2257.146348310258,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"width": 179.20074462890625,
"height": 35,
"seed": 1909372476,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152887733,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 5,
"text": "Malicious App",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "0pKo0FuXTqVcAnFHg3eHW",
"originalText": "Malicious App",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "text",
"version": 2141,
"versionNonce": 1555780868,
"index": "b93",
"isDeleted": false,
"id": "T3PyqevJ-r5aTrOqjBFmC",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 682.0542956691074,
"y": 1874.6210808258843,
"strokeColor": "#000000",
"backgroundColor": "white",
"width": 120.95199584960938,
"height": 28.8310711010254,
"seed": 824973372,
"groupIds": [
"B6zTjTN5MAfYoqaWmyIpC"
],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152736532,
"link": null,
"locked": false,
"fontSize": 23.064856880820322,
"fontFamily": 1,
"text": "Developers",
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "Developers",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "line",
"version": 2219,
"versionNonce": 1611797636,
"index": "b94",
"isDeleted": false,
"id": "l7ybVNV2OMm-nKOKuikqR",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 720.4019302076085,
"y": 1857.6759337038266,
"strokeColor": "#000000",
"backgroundColor": "#ced4da",
"width": 56.819606873238214,
"height": 50.596228152466416,
"seed": 1895149756,
"groupIds": [
"aNKxm8YeKZVFgjEt1CgRW",
"B6zTjTN5MAfYoqaWmyIpC"
],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [],
"updated": 1730152736532,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null,
"points": [
[
0,
0
],
[
6.363795969802675,
-33.527621064887384
],
[
27.273411299154343,
-50.596228152466416
],
[
48.183026628506006,
-37.185179726511414
],
[
56.819606873238214,
-3.481418584602479
],
[
0,
0
]
]
},
{
"type": "ellipse",
"version": 1956,
"versionNonce": 1499784196,
"index": "b95",
"isDeleted": false,
"id": "K8bdPFGmyuaYudSU_OE_3",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 734.7642676302007,
"y": 1780.7649600831905,
"strokeColor": "#000000",
"backgroundColor": "#ced4da",
"width": 29.091638719097993,
"height": 25.455183879210832,
"seed": 620247356,
"groupIds": [
"aNKxm8YeKZVFgjEt1CgRW",
"B6zTjTN5MAfYoqaWmyIpC"
],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152736532,
"link": null,
"locked": false
},
{
"type": "text",
"version": 2229,
"versionNonce": 1766236476,
"index": "b96",
"isDeleted": false,
"id": "drKxgai7tWhbRkPAkFoaW",
"fillStyle": "solid",
"strokeWidth": 1,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 706.11979529216,
"y": 2290.73338734735,
"strokeColor": "#e03131",
"backgroundColor": "white",
"width": 102.93438720703125,
"height": 28.8310711010254,
"seed": 373772420,
"groupIds": [
"6MwyX96Lf9eym84D59YgW"
],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152840065,
"link": null,
"locked": false,
"fontSize": 23.064856880820322,
"fontFamily": 1,
"text": "Attacker",
"textAlign": "center",
"verticalAlign": "top",
"containerId": null,
"originalText": "Attacker",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "line",
"version": 2273,
"versionNonce": 920066492,
"index": "b97",
"isDeleted": false,
"id": "ANLjDXSBmq2lDuXiHhPKE",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 735.458625509372,
"y": 2273.7882402252926,
"strokeColor": "#e03131",
"backgroundColor": "#ced4da",
"width": 56.819606873238214,
"height": 50.596228152466416,
"seed": 227713028,
"groupIds": [
"kFpzVapCBJpFzZjwFXhER",
"6MwyX96Lf9eym84D59YgW"
],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [],
"updated": 1730152840065,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": null,
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": null,
"points": [
[
0,
0
],
[
6.363795969802675,
-33.527621064887384
],
[
27.273411299154343,
-50.596228152466416
],
[
48.183026628506006,
-37.185179726511414
],
[
56.819606873238214,
-3.481418584602479
],
[
0,
0
]
]
},
{
"type": "ellipse",
"version": 2010,
"versionNonce": 1556382268,
"index": "b98",
"isDeleted": false,
"id": "wS4NSv0k6ivd-min90xVs",
"fillStyle": "cross-hatch",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 0,
"opacity": 100,
"angle": 0,
"x": 749.8209629319642,
"y": 2196.877266604656,
"strokeColor": "#e03131",
"backgroundColor": "#ced4da",
"width": 29.091638719097993,
"height": 25.455183879210832,
"seed": 768081796,
"groupIds": [
"kFpzVapCBJpFzZjwFXhER",
"6MwyX96Lf9eym84D59YgW"
],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152840065,
"link": null,
"locked": false
},
{
"type": "arrow",
"version": 3307,
"versionNonce": 752590212,
"index": "b99",
"isDeleted": false,
"id": "QM3OeE-gZsJtTqpVyK6WY",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 701.9411573717334,
"y": 1844.8912709167275,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 244.26981175285607,
"height": 120.72995808263295,
"seed": 306836540,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [
{
"type": "text",
"id": "R_kzZWp-mTJGBEknO4BDX"
}
],
"updated": 1730152887734,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": {
"elementId": "IDPR-T_GInZ_SYrUKdyiV",
"focus": 0.17879744648147,
"gap": 9.438221207781197,
"fixedPoint": null
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-244.26981175285607,
120.72995808263295
]
]
},
{
"id": "R_kzZWp-mTJGBEknO4BDX",
"type": "text",
"x": 121.65945464057293,
"y": 1853.7601556245634,
"width": 144.31985473632812,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "b9A",
"roundness": null,
"seed": 235483396,
"version": 19,
"versionNonce": 1684440708,
"isDeleted": false,
"boundElements": null,
"updated": 1730152612311,
"link": null,
"locked": false,
"text": "Create Project",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "QM3OeE-gZsJtTqpVyK6WY",
"originalText": "Create Project",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "arrow",
"version": 3418,
"versionNonce": 147191356,
"index": "b9B",
"isDeleted": false,
"id": "L746tARKZ5EBHf_R0vgrz",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 716.5736657298296,
"y": 2249.302134297232,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"width": 256.56483322792286,
"height": 232.4291067895324,
"seed": 1422419204,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [
{
"type": "text",
"id": "siYG6VKVSb_LJCZ4mZhGR"
}
],
"updated": 1730152923070,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": {
"elementId": "IDPR-T_GInZ_SYrUKdyiV",
"focus": -0.4562902862666964,
"gap": 11.775708090810582,
"fixedPoint": null
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-256.56483322792286,
-232.4291067895324
]
]
},
{
"type": "text",
"version": 61,
"versionNonce": 69135492,
"index": "b9C",
"isDeleted": false,
"id": "siYG6VKVSb_LJCZ4mZhGR",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 516.1313217477041,
"y": 2120.5875809024656,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"width": 144.31985473632812,
"height": 25,
"seed": 1945969796,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152921956,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 5,
"text": "Create Project",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "L746tARKZ5EBHf_R0vgrz",
"originalText": "Create Project",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "arrow",
"version": 3257,
"versionNonce": 493737532,
"index": "b9D",
"isDeleted": false,
"id": "5zc8kBUOyFDyV3r5cJ5u1",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -389.669252006124,
"y": 2282.7876018612146,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 169.1290066673917,
"height": 5.596252304153495,
"seed": 1553075644,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [
{
"type": "text",
"id": "7RKMXj2cxBpP35OoP6w88"
}
],
"updated": 1730152887734,
"link": null,
"locked": false,
"startBinding": null,
"endBinding": {
"elementId": "0pKo0FuXTqVcAnFHg3eHW",
"focus": 0.015241445198305226,
"gap": 15.010905533779408,
"fixedPoint": null
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
169.1290066673917,
-5.596252304153495
]
]
},
{
"id": "7RKMXj2cxBpP35OoP6w88",
"type": "text",
"x": -349.63218044871417,
"y": 2182.1009987335074,
"width": 49.35993957519531,
"height": 25,
"angle": 0,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "b9E",
"roundness": null,
"seed": 1020353412,
"version": 7,
"versionNonce": 286799876,
"isDeleted": false,
"boundElements": null,
"updated": 1730152666495,
"link": null,
"locked": false,
"text": "Login",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "5zc8kBUOyFDyV3r5cJ5u1",
"originalText": "Login",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "rectangle",
"version": 2219,
"versionNonce": 1519381636,
"index": "b9F",
"isDeleted": false,
"id": "BVL6kArEACCUWcQSWHFae",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -197.31659691308204,
"y": 1891.50794075608,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 215.2445549281264,
"height": 191.3848126773838,
"seed": 1674875964,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 3
},
"boundElements": [
{
"id": "L40neTwxvfRhgN0RNPCjk",
"type": "text"
},
{
"id": "KNsMX2SBrRRRZSGEwUC8b",
"type": "arrow"
},
{
"id": "GMVGnrJnqaFoheEbd232T",
"type": "arrow"
}
],
"updated": 1730152771829,
"link": null,
"locked": false
},
{
"type": "text",
"version": 2484,
"versionNonce": 1616512188,
"index": "b9G",
"isDeleted": false,
"id": "L40neTwxvfRhgN0RNPCjk",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -158.99460478837432,
"y": 1969.7003470947718,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 138.60057067871094,
"height": 35,
"seed": 825142460,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152713281,
"link": null,
"locked": false,
"fontSize": 28,
"fontFamily": 5,
"text": "Victim App",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "BVL6kArEACCUWcQSWHFae",
"originalText": "Victim App",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "arrow",
"version": 3243,
"versionNonce": 1053846276,
"index": "b9H",
"isDeleted": false,
"id": "KNsMX2SBrRRRZSGEwUC8b",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 20.480215068902226,
"y": 1988.10732399155,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 203.34876871685455,
"height": 2.0759069603368516,
"seed": 1956413316,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [
{
"type": "text",
"id": "qohNI9nUisxSE0t98F5c6"
}
],
"updated": 1730152960724,
"link": null,
"locked": false,
"startBinding": {
"elementId": "BVL6kArEACCUWcQSWHFae",
"focus": -0.020951095762841803,
"gap": 2.5522570538578577,
"fixedPoint": null
},
"endBinding": {
"elementId": "IDPR-T_GInZ_SYrUKdyiV",
"focus": 0.12417236481001943,
"gap": 9.159585697212947,
"fixedPoint": null
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
203.34876871685455,
-2.0759069603368516
]
]
},
{
"type": "text",
"version": 53,
"versionNonce": 1016397884,
"index": "b9I",
"isDeleted": false,
"id": "qohNI9nUisxSE0t98F5c6",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 52.16467022811075,
"y": 1937.0693705113815,
"strokeColor": "#1e1e1e",
"backgroundColor": "transparent",
"width": 139.9798583984375,
"height": 100,
"seed": 1734995716,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152933693,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 5,
"text": "Login &\nVerify\nToken\n(not audience)",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "KNsMX2SBrRRRZSGEwUC8b",
"originalText": "Login &\nVerify\nToken\n(not audience)",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "arrow",
"version": 3326,
"versionNonce": 709865148,
"index": "b9J",
"isDeleted": false,
"id": "GMVGnrJnqaFoheEbd232T",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -85.78012969943508,
"y": 2175.762596605995,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"width": 7.444965507835249,
"height": 93.7848692528969,
"seed": 244411196,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [
{
"type": "text",
"id": "BK1Fa_XotI7vFgkPy2dbx"
}
],
"updated": 1730152887734,
"link": null,
"locked": false,
"startBinding": {
"elementId": "0pKo0FuXTqVcAnFHg3eHW",
"focus": 0.1733804513331944,
"gap": 3.1913453655711237,
"fixedPoint": null
},
"endBinding": {
"elementId": "BVL6kArEACCUWcQSWHFae",
"focus": 0.09594391447305084,
"gap": 1,
"fixedPoint": null
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
-7.444965507835249,
-93.7848692528969
]
]
},
{
"type": "text",
"version": 52,
"versionNonce": 107945148,
"index": "b9K",
"isDeleted": false,
"id": "BK1Fa_XotI7vFgkPy2dbx",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": -178.43252883518863,
"y": 2116.3701619795465,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"width": 177.85983276367188,
"height": 25,
"seed": 1010344892,
"groupIds": [],
"frameId": null,
"roundness": null,
"boundElements": [],
"updated": 1730152787158,
"link": null,
"locked": false,
"fontSize": 20,
"fontFamily": 5,
"text": "Use stolen tokens",
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "GMVGnrJnqaFoheEbd232T",
"originalText": "Use stolen tokens",
"autoResize": true,
"lineHeight": 1.25
},
{
"type": "arrow",
"version": 3432,
"versionNonce": 1305529476,
"index": "b9L",
"isDeleted": false,
"id": "1DW6jYie7TBBMPB2Tb6PL",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "solid",
"roughness": 1,
"opacity": 100,
"angle": 0,
"x": 26.268982827136426,
"y": 2288.1972340648817,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"width": 288.2137785995216,
"height": 181.38746009952138,
"seed": 647081148,
"groupIds": [],
"frameId": null,
"roundness": {
"type": 2
},
"boundElements": [
{
"type": "text",
"id": "7jSYHzSnihK5XvjTuh4_z"
}
],
"updated": 1730152951210,
"link": null,
"locked": false,
"startBinding": {
"elementId": "0pKo0FuXTqVcAnFHg3eHW",
"focus": 0.5611221909850164,
"gap": 16.553767703962905,
"fixedPoint": null
},
"endBinding": {
"elementId": "IDPR-T_GInZ_SYrUKdyiV",
"focus": -0.5721787148148761,
"gap": 14.259353103009744,
"fixedPoint": null
},
"lastCommittedPoint": null,
"startArrowhead": null,
"endArrowhead": "arrow",
"points": [
[
0,
0
],
[
288.2137785995216,
-181.38746009952138
]
]
},
{
"id": "7jSYHzSnihK5XvjTuh4_z",
"type": "text",
"x": 88.62596367963161,
"y": 2172.503504015121,
"width": 163.49981689453125,
"height": 50,
"angle": 0,
"strokeColor": "#e03131",
"backgroundColor": "transparent",
"fillStyle": "solid",
"strokeWidth": 2,
"strokeStyle": "dotted",
"roughness": 1,
"opacity": 100,
"groupIds": [],
"frameId": null,
"index": "b9LV",
"roundness": null,
"seed": 1300461060,
"version": 64,
"versionNonce": 1109535932,
"isDeleted": false,
"boundElements": null,
"updated": 1730152950133,
"link": null,
"locked": false,
"text": "Login with\nAttacker project",
"fontSize": 20,
"fontFamily": 5,
"textAlign": "center",
"verticalAlign": "middle",
"containerId": "1DW6jYie7TBBMPB2Tb6PL",
"originalText": "Login with\nAttacker project",
"autoResize": true,
"lineHeight": 1.25
}
],
"appState": {
"gridSize": 20,
"gridStep": 5,
"gridModeEnabled": false,
"viewBackgroundColor": "#ffffff"
},
"files": {}
}
================================================
FILE: docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx
================================================
---
title: Token Cross Service Relay Attack
description: A vulnerability arises when a Token is forged by the same service but doesn't verify the issuer. This can lead to security risks, as it means an attacker could create a forged Token and manipulate the fields or impersonate an user.
---
<table>
<tr>
<th>Severity</th>
<td>High</td>
</tr>
<tr>
<th>Classifications</th>
<td>
<ul>
<li><a href="https://cwe.mitre.org/data/definitions/20.html">CWE-20: Improper Input Validation</a></li>
<li><a href="https://cwe.mitre.org/data/definitions/287.html">CWE-287: Improper Authentication</a></li>
</ul>
</td>
</tr>
<tr>
<th>OWASP Category</th>
<td>
<a href="https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/">OWASP API2:2023 Broken Authentication</a>
</td>
</tr>
</table>
A vulnerability arises when a JSON Web Token (JWT) is signed by the same service but doesn't verify the issuer (the source of the token) and/or the audience (the intended recipient). This can lead to security risks, as it means an attacker could create a forged JWT with the same service signature and manipulate the issuer and audience fields. Without proper verification, the service may accept the forged token, potentially granting unauthorized access or compromising the system's security.

## Services impacted
### Social Identity Providers
Most of the social identity providers generate ID tokens using the same key pair. In addition of JWT integrity verifications, the `issuer` and `audience` must be verified to ensure the token has been generated from the expected client.
Here are some social identity providers that are impacted with this vulnerability:
| Service | Description | Documentation |
| ------- | ----------- | ------------- |
| **Google** | Google provides ID tokens generated from the same key pair. The `audience` must be verified to ensure the token has been generated from the expected Google Project's `Client Id`. | [Validate Google Id Token](https://developers.google.com/identity/openid-connect/openid-connect#validatinganidtoken) |
| **Facebook** | Facebook provides ID tokens generated from the same key pair. The `audience` must be verified to ensure the token has been generated from the expected `Facebook App Id`. | [Validate Facebook Token](https://developers.facebook.com/docs/facebook-login/limited-login/token/validating/) |
| **Microsoft** | Microsoft provides ID tokens generated from the same key pair. The `audience` must be verified to ensure the token has been generated from the expected `Microsoft App Id`. | [Validate Microsoft Id Tokens](https://learn.microsoft.com/en-us/entra/identity-platform/id-tokens#validate-tokens) |
| **Apple** | Apple provides ID tokens generated from the same key pair. The `audience` must be verified to ensure the token has been generated from the expected Apple App `client_id`. | [Validate Apple Id Tokens](https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api/authenticating_users_with_sign_in_with_apple#3383773) |
### IAM / Authorization Services
Some IAM Services generate JWT tokens using the same key pair. In addition of JWT integrity verifications, some additional checks must be performed to ensure the token has been generated from the expected client / tenant.
Here are some of the services that are impacted:
| Service | Description | Documentation |
| ------------------------------------------- | ----------- | ------------- |
| **Firebase** / **Google Identity Platform** | Firebase provides ID tokens generated from the same key pair. The issuer and audience must be verified to ensure the token has been generated from the expected Firebase Project. | [Firebase Verify Id Token](https://firebase.google.com/docs/auth/admin/verify-id-tokens) |
## Example
Here is an example of JWT (ID Token) issued by Google:
```json
{
"iss": "https://accounts.google.com",
"azp": "1234987819200.apps.googleusercontent.com",
"aud": "1234987819200.apps.googleusercontent.com",
"sub": "10769150350006150715113082367",
"at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
"hd": "example.com",
"email": "jsmith@example.com",
"email_verified": "true",
"iat": 1353601026,
"exp": 1353604926,
"nonce": "0394852-3190485-2490358"
}
```
In this example, the `aud` claim represents the audience, which is the client ID of the Google Project. The service must verify the `aud` claim to ensure the token has been generated from the expected Google Project's `Client Id`.
Let's say an attacker creates a new project with the same service and generates a token. The attacker can relay the token to the victim's project and impersonate the user making the victim's project believe that the user is authenticated.
Here is an example of a forged token:
```json
{
"iss": "https://accounts.google.com",
"azp": "1234987819201.apps.googleusercontent.com",
"aud": "1234987819201.apps.googleusercontent.com",
"sub": "10769150350006150715113082367",
"at_hash": "HK6E_P6Dh8Y93mRNtsDB1Q",
"hd": "example.com",
"email": "jsmith@example.com",
"email_verified": "true",
"iat": 1353601026,
"exp": 1353604926,
"nonce": "0394852-3190485-2490358"
}
```
Notice that the `aud` claim has been changed to the attacker's project `Client Id`. If the service doesn't verify the `aud` claim, it will accept the forged token and believe that the user is authenticated.
## How to test?
Create a new project with the same service and generate a token (through a legitimate authentication process). Then, try to relay the token to your original project and see if the service accepts the token or not. If the service accepts the token, then the service is vulnerable to this attack.
This test requires manual steps. VulnAPI does not currently automate this scan. Create a second project with the same identity provider, obtain a valid token for that project, then send it to your original project's API. If the API accepts the request, it is vulnerable.
## What is the impact?
The impact of this vulnerability is that an attacker could create another project with the same service. When user will authenticate with the malicious project, the attacker can relay the token to the victim's project and impersonate the user making the victim's project believe that the user is authenticated.
## How to remediate?
To remediate this vulnerability, the service must follow tokens provider's guidelines. Most of the time, the `issuer` and `audience` must be verified to ensure the token has been generated from the expected client. The service must keep verifying token integrity and other claims to ensure the token is not tampered with.
================================================
FILE: docs/vulnerabilities/broken-authentication/jwt-kid-injection.mdx
================================================
---
title: JWT KID Header Injection
description: JWT KID Header Injection occurs when the `kid` (Key ID) header parameter is passed unsanitized to a database query or file system lookup, allowing attackers to forge trusted tokens.
---
import { Tabs, TabItem } from '@/components/docs'
<table>
<tr>
<th>Severity</th>
<td>High</td>
</tr>
<tr>
<th>Classifications</th>
<td>
<a href="https://cwe.mitre.org/data/definitions/345.html">CWE-345: Insufficient Verification of Data Authenticity</a>
</td>
</tr>
<tr>
<th>OWASP Category</th>
<td>
<a href="https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/">OWASP API2:2023 Broken Authentication</a>
</td>
</tr>
</table>
The `kid` (Key ID) JWT header field is intended to tell the server which key to use for signature verification. When this value is passed unsanitized to a database query or a file system lookup, attackers can inject a payload that causes the server to use an attacker-controlled key, effectively allowing them to forge arbitrary tokens.
VulnAPI tests two variants of this attack:
- **SQL Injection** — injects a SQL payload into `kid` (e.g. `' UNION SELECT 'secret' ...`) so that the database returns a known value as the key.
- **Path Traversal** — sets `kid` to a filesystem path such as `/dev/null` so the server reads an empty file as the key, which can be matched with an empty HMAC secret.
## Example
Here is a valid JWT signed with HS256:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
### SQL Injection variant
The attacker crafts a token with a `kid` header that injects into the key-lookup query and re-signs it with the value the query is made to return (`secret`):
```json
{
"alg": "HS256",
"typ": "JWT",
"kid": "' UNION SELECT 'secret' FROM tokens WHERE '1'='1"
}
```
### Path Traversal variant
The attacker sets `kid` to `/dev/null` and re-signs the token with an empty HMAC secret. If the server reads the signing key from the path named by `kid`, it receives zero bytes:
```json
{
"alg": "HS256",
"typ": "JWT",
"kid": "/dev/null"
}
```
## How to test?
<Tabs>
<TabItem label="cURL">
```bash
vulnapi scan curl [url] -H "Authorization: Bearer eyJhbGciOiJIUzI1NiI..." --scans jwt.kid_injection
```
</TabItem>
<TabItem label="OpenAPI">
```bash
echo "eyJhbGciOiJIUzI1NiI..." | vulnapi scan openapi [OpenAPI_Path_Or_URL] --scans jwt.kid_injection
```
</TabItem>
<TabItem label="GraphQL">
```bash
vulnapi scan graphql -H "Authorization: Bearer eyJhbGciOiJIUzI1NiI..." --scans jwt.kid_injection [url]
```
</TabItem>
</Tabs>
VulnAPI supports scanning against various types of other [vulnerabilities](../) as well.
## What is the impact?
A successful KID injection attack allows an attacker to forge a JWT that the server accepts as legitimate. This can lead to full authentication bypass, privilege escalation, or impersonation of any user in the system.
## How to remediate?
- **Validate the `kid` value** before using it — reject values containing SQL metacharacters, path separators, or characters outside a safe allowlist (e.g. alphanumeric and hyphens only).
- **Use parameterised queries** when looking up keys by `kid` in a database to prevent SQL injection.
- **Never read key material from user-controlled file paths** — store keys in a secure key store and use `kid` only as an opaque identifier mapped server-side to a known key.
- **Hard-code or allowlist permitted `kid` values** where possible.
================================================
FILE: docs/vulnerabilities/broken-authentication/jwt-not-verified.mdx
================================================
---
title: JWT Signature Not Verified
description: A vulnerability where the server accepts JWTs without verifying their cryptographic signature, allowing attackers to forge arbitrary tokens.
---
import { Tabs, TabItem } from '@/components/docs'
<table>
<tr>
<th>Scan ID</th>
<td><code>jwt.not_verified</code></td>
</tr>
<tr>
<th>Severity</th>
<td>High</td>
</tr>
<tr>
<th>CVSS 4.0</th>
<td>9.3 — <code>CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N</code></td>
</tr>
<tr>
<th>Classifications</th>
<td>
<a href="https://cwe.mitre.org/data/definitions/345.html">CWE-345: Insufficient Verification of Data Authenticity</a>
</td>
</tr>
<tr>
<th>OWASP Category</th>
<td>
<a href="https://owasp.org/API-Security/editions/2023/en/0xa2-broken-authentication/">OWASP API2:2023 Broken Authentication</a>
</td>
</tr>
</table>
The "JWT Signature Not Verified" vulnerability occurs when a server accepts a JWT whose signature was created with a completely differe
gitextract_zxxg5r_h/ ├── .cobra.yaml ├── .docker/ │ ├── Dockerfile-build │ └── Dockerfile-goreleaser ├── .github/ │ ├── CODEOWNERS │ ├── FUNDING.yml │ └── workflows/ │ ├── ci.yml │ ├── scans.yml │ └── stale.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yaml ├── .vscode/ │ └── launch.json ├── .whitesource ├── LICENSE ├── README.md ├── api/ │ ├── curl.go │ ├── graphql.go │ ├── handler.go │ ├── openapi.go │ ├── request.go │ ├── response.go │ └── response_test.go ├── cmd/ │ ├── discover/ │ │ ├── api.go │ │ ├── domain.go │ │ ├── root.go │ │ └── root_test.go │ ├── jwt/ │ │ └── root.go │ ├── root.go │ ├── scan/ │ │ ├── curl.go │ │ ├── graphql.go │ │ ├── openapi.go │ │ ├── root.go │ │ └── root_test.go │ └── serve/ │ └── root.go ├── demo.cast ├── docs/ │ ├── best-practices/ │ │ ├── _meta.yml │ │ ├── jwt.mdx │ │ └── security-headers.mdx │ ├── first-scan.mdx │ ├── getting-started.mdx │ ├── github-action.mdx │ ├── index.mdx │ ├── installation.mdx │ ├── labs.mdx │ ├── reference/ │ │ ├── _meta.yml │ │ ├── cli/ │ │ │ ├── _meta.yml │ │ │ ├── discover-api.mdx │ │ │ ├── discover-domain.mdx │ │ │ ├── index.mdx │ │ │ ├── jwt-generate.mdx │ │ │ ├── scan-curl.mdx │ │ │ ├── scan-graphql.mdx │ │ │ ├── scan-openapi.mdx │ │ │ └── serve.mdx │ │ ├── output-formats.mdx │ │ └── scan-ids.mdx │ ├── vampi.mdx │ ├── vulnerabilities/ │ │ ├── _meta.yml │ │ ├── broken-authentication/ │ │ │ ├── _meta.yml │ │ │ ├── authentication-bypass.mdx │ │ │ ├── brute-force-attack.mdx │ │ │ ├── jwt-alg-none.mdx │ │ │ ├── jwt-blank-secret.mdx │ │ │ ├── jwt-cross-service-relay-attack.excalidraw │ │ │ ├── jwt-cross-service-relay-attack.mdx │ │ │ ├── jwt-kid-injection.mdx │ │ │ ├── jwt-not-verified.mdx │ │ │ ├── jwt-null-signature.mdx │ │ │ └── jwt-weak-secret.mdx │ │ └── security-misconfiguration/ │ │ ├── _meta.yml │ │ ├── graphql-introspection.mdx │ │ ├── http-cookies.mdx │ │ ├── http-method-allow-override.mdx │ │ ├── http-trace-track.mdx │ │ └── tls.mdx │ └── vulnerabilities.mdx ├── go.mod ├── go.sum ├── internal/ │ ├── auth/ │ │ ├── api_key.go │ │ ├── api_key_test.go │ │ ├── basic.go │ │ ├── basic_test.go │ │ ├── bearer.go │ │ ├── bearer_test.go │ │ ├── headers.go │ │ ├── no_auth.go │ │ ├── no_auth_test.go │ │ ├── oauth.go │ │ ├── oauth_test.go │ │ ├── scheme.go │ │ ├── scheme_test.go │ │ ├── security_scheme.go │ │ ├── security_scheme_test.go │ │ ├── type.go │ │ ├── uniq_name.go │ │ └── uniq_name_test.go │ ├── cmd/ │ │ ├── args.go │ │ ├── args_test.go │ │ ├── http.go │ │ ├── printtable/ │ │ │ ├── fingerprint_table.go │ │ │ ├── printttable.go │ │ │ ├── report_table.go │ │ │ ├── report_table_test.go │ │ │ └── wellknown_paths_table.go │ │ ├── progressbar.go │ │ └── report.go │ ├── operation/ │ │ ├── operation.go │ │ ├── operation_test.go │ │ ├── operations.go │ │ └── operations_test.go │ ├── request/ │ │ ├── client.go │ │ ├── client_test.go │ │ ├── error.go │ │ ├── request.go │ │ ├── request_test.go │ │ ├── response.go │ │ └── response_test.go │ └── scan/ │ ├── attempt.go │ ├── attempt_test.go │ ├── scan_url.go │ ├── utils.go │ └── utils_test.go ├── logo-text-art.txt ├── main.go ├── openapi/ │ ├── base_url.go │ ├── base_url_test.go │ ├── loader.go │ ├── loader_test.go │ ├── openapi.go │ ├── operation.go │ ├── param.go │ ├── param_test.go │ ├── security_scheme.go │ ├── security_scheme_test.go │ ├── security_scheme_values.go │ ├── security_scheme_values_test.go │ ├── validate.go │ └── validate_test.go ├── renovate.json ├── report/ │ ├── capec.go │ ├── curl_report.go │ ├── curl_report_test.go │ ├── cwe.go │ ├── graphql_report.go │ ├── issue.go │ ├── issue_report.go │ ├── issue_report_test.go │ ├── openapi_report.go │ ├── openapi_report_test.go │ ├── options_report.go │ ├── owasp.go │ ├── report.go │ ├── report_test.go │ ├── reporter.go │ ├── reporter_test.go │ └── test/ │ ├── issue.yaml │ └── issue_nil_classifications.yaml ├── scan/ │ ├── broken_authentication/ │ │ ├── authentication_bypass/ │ │ │ ├── authentication_bypass.go │ │ │ └── authentication_bypass_test.go │ │ └── jwt/ │ │ ├── alg_none/ │ │ │ ├── alg_none.go │ │ │ ├── alg_none_test.go │ │ │ └── methods.go │ │ ├── blank_secret/ │ │ │ ├── blank_secret.go │ │ │ └── blank_secret_test.go │ │ ├── kid_injection/ │ │ │ ├── kid_injection.go │ │ │ └── kid_injection_test.go │ │ ├── not_verified/ │ │ │ ├── not_verified.go │ │ │ └── not_verified_test.go │ │ ├── null_signature/ │ │ │ ├── null_signature.go │ │ │ └── null_signature_test.go │ │ └── weak_secret/ │ │ ├── weak_secret.go │ │ └── weak_secret_test.go │ ├── discover/ │ │ ├── accept_unauthenticated/ │ │ │ ├── accept_unauthenticated_operation.go │ │ │ └── accept_unauthenticated_operation_test.go │ │ ├── discoverable_graphql/ │ │ │ ├── discoverable_graphql.go │ │ │ └── discoverable_graphql_test.go │ │ ├── discoverable_openapi/ │ │ │ ├── discoverable_openapi.go │ │ │ └── discoverable_openapi_test.go │ │ ├── exposed_files/ │ │ │ ├── exposed_files.go │ │ │ └── exposed_files_test.go │ │ ├── fingerprint/ │ │ │ ├── fingerprint.go │ │ │ └── fingerprint_test.go │ │ ├── healthcheck/ │ │ │ ├── healthcheck.go │ │ │ └── healthcheck_test.go │ │ ├── utils.go │ │ ├── utils_test.go │ │ └── well-known/ │ │ ├── well_known.go │ │ └── well_known_test.go │ ├── graphql/ │ │ └── introspection_enabled/ │ │ ├── introspection_enabled.go │ │ └── introspection_enabled_test.go │ ├── misconfiguration/ │ │ ├── http_cookies/ │ │ │ ├── http_cookies.go │ │ │ └── http_cookies_test.go │ │ ├── http_headers/ │ │ │ ├── http_headers.go │ │ │ └── http_headers_test.go │ │ ├── http_method_override/ │ │ │ ├── http_method_override.go │ │ │ └── http_method_override_test.go │ │ ├── http_trace/ │ │ │ ├── http_trace_method.go │ │ │ └── http_trace_method_test.go │ │ └── http_track/ │ │ ├── http_track_method.go │ │ └── http_track_method_test.go │ ├── operation_scan.go │ ├── operation_scan_test.go │ ├── scan.go │ └── scan_test.go ├── scenario/ │ ├── discover_api.go │ ├── discover_api_test.go │ ├── discover_domain.go │ ├── graphql.go │ ├── graphql_test.go │ ├── openapi.go │ ├── openapi_test.go │ ├── scans.go │ ├── url.go │ ├── url_test.go │ └── utils.go ├── seclist/ │ ├── lists/ │ │ ├── exposed-paths.txt │ │ ├── graphql.txt │ │ ├── healthcheck.txt │ │ ├── jwt-secrets.txt │ │ ├── swagger.txt │ │ └── well-known.txt │ ├── seclist.go │ └── seclist_test.go ├── test/ │ └── stub/ │ ├── basic_http_bearer.openapi.json │ ├── basic_http_bearer_jwt.openapi.json │ ├── complex.openapi.json │ ├── petstore.openapi.json │ ├── simple_api_key.openapi.json │ ├── simple_http_basic.openapi.json │ ├── simple_http_bearer.openapi.json │ ├── simple_http_bearer_jwt.openapi.json │ ├── simple_http_bearer_jwt.openapi.yaml │ └── simple_no_scheme.openapi.json └── vulnapi.rb
SYMBOL INDEX (894 symbols across 155 files)
FILE: api/curl.go
type NewURLScanRequest (line 13) | type NewURLScanRequest struct
method ScanURL (line 21) | func (h *Handler) ScanURL(ctx *gin.Context) {
FILE: api/graphql.go
type NewGraphQLScanRequest (line 13) | type NewGraphQLScanRequest struct
method ScanGraphQL (line 19) | func (h *Handler) ScanGraphQL(ctx *gin.Context) {
FILE: api/handler.go
type Handler (line 7) | type Handler struct
function NewHandler (line 9) | func NewHandler() *Handler {
function Routes (line 13) | func Routes(r *gin.Engine, h *Handler) {
FILE: api/openapi.go
type NewOpenAPIScanRequest (line 14) | type NewOpenAPIScanRequest struct
method ScanOpenAPI (line 23) | func (h *Handler) ScanOpenAPI(ctx *gin.Context) {
FILE: api/request.go
type ScanOptions (line 9) | type ScanOptions struct
function parseScanOptions (line 17) | func parseScanOptions(opts *ScanOptions) request.NewClientOptions {
FILE: api/response.go
type HTTPResponseReports (line 7) | type HTTPResponseReports struct
FILE: api/response_test.go
function TestMarshalHTTPResponseReports (line 15) | func TestMarshalHTTPResponseReports(t *testing.T) {
FILE: cmd/discover/api.go
function NewAPICmd (line 19) | func NewAPICmd() (apiCmd *cobra.Command) {
FILE: cmd/discover/domain.go
function NewDomainCmd (line 18) | func NewDomainCmd() (domainCmd *cobra.Command) {
FILE: cmd/discover/root.go
constant otelName (line 9) | otelName = "github.com/cerberauth/vulnapi/cmd/discover"
constant otelErrorReasonAttributeKey (line 11) | otelErrorReasonAttributeKey = attribute.Key("error_reason")
function NewDiscoverCmd (line 14) | func NewDiscoverCmd() (discoverCmd *cobra.Command) {
FILE: cmd/discover/root_test.go
function TestNewDiscoverCmd (line 10) | func TestNewDiscoverCmd(t *testing.T) {
FILE: cmd/jwt/root.go
constant otelName (line 18) | otelName = "github.com/cerberauth/vulnapi/cmd/discover"
constant otelErrorReasonAttributeKey (line 20) | otelErrorReasonAttributeKey = attribute.Key("error_reason")
constant algorithmAttributeKey (line 21) | algorithmAttributeKey = attribute.Key("algorithm")
type Algorithm (line 24) | type Algorithm
constant None (line 27) | None Algorithm = "NONE"
constant HS256 (line 28) | HS256 Algorithm = "HS256"
constant HS384 (line 29) | HS384 Algorithm = "HS384"
constant HS512 (line 30) | HS512 Algorithm = "HS512"
constant RS256 (line 31) | RS256 Algorithm = "RS256"
constant RS384 (line 32) | RS384 Algorithm = "RS384"
constant RS512 (line 33) | RS512 Algorithm = "RS512"
constant ES256 (line 34) | ES256 Algorithm = "ES256"
constant ES384 (line 35) | ES384 Algorithm = "ES384"
function GetAlgorithm (line 44) | func GetAlgorithm(alg string) (jwtlib.SigningMethod, error) {
function NewJWTCmd (line 69) | func NewJWTCmd() (cmd *cobra.Command) {
FILE: cmd/root.go
function NewRootCmd (line 24) | func NewRootCmd(projectVersion, commit, date string) (cmd *cobra.Command) {
function Execute (line 61) | func Execute(projectVersion, commit, date string) {
FILE: cmd/scan/curl.go
function NewCURLScanCmd (line 23) | func NewCURLScanCmd() (scanCmd *cobra.Command) {
FILE: cmd/scan/graphql.go
function NewGraphQLScanCmd (line 18) | func NewGraphQLScanCmd() (scanCmd *cobra.Command) {
FILE: cmd/scan/openapi.go
function isStdinOpen (line 21) | func isStdinOpen() bool {
function readStdin (line 26) | func readStdin() *string {
function NewOpenAPIScanCmd (line 36) | func NewOpenAPIScanCmd() (scanCmd *cobra.Command) {
FILE: cmd/scan/root.go
constant otelName (line 9) | otelName = "github.com/cerberauth/vulnapi/cmd/scan"
constant otelErrorReasonAttributeKey (line 11) | otelErrorReasonAttributeKey = attribute.Key("error_reason")
constant includeScansAttributeKey (line 12) | includeScansAttributeKey = attribute.Key("include_scans")
constant excludeScansAttributeKey (line 13) | excludeScansAttributeKey = attribute.Key("exclude_scans")
function NewScanCmd (line 16) | func NewScanCmd() (scanCmd *cobra.Command) {
FILE: cmd/scan/root_test.go
function TestNewScanCmd (line 10) | func TestNewScanCmd(t *testing.T) {
FILE: cmd/serve/root.go
function NewServeCmd (line 16) | func NewServeCmd() (serveCmd *cobra.Command) {
FILE: internal/auth/api_key.go
function NewAPIKeySecurityScheme (line 3) | func NewAPIKeySecurityScheme(name string, in SchemeIn, value *string) (*...
function MustNewAPIKeySecurityScheme (line 20) | func MustNewAPIKeySecurityScheme(name string, in SchemeIn, value *string...
FILE: internal/auth/api_key_test.go
function TestNewAPIKeySecurityScheme (line 10) | func TestNewAPIKeySecurityScheme(t *testing.T) {
function TestTestNewAPIKeySecurityScheme_WhenNilValue (line 27) | func TestTestNewAPIKeySecurityScheme_WhenNilValue(t *testing.T) {
function TestNewAuthorizationBearerSecurityScheme_WhenInCooke (line 37) | func TestNewAuthorizationBearerSecurityScheme_WhenInCooke(t *testing.T) {
function TestMustNewAPIKeySecurityScheme (line 47) | func TestMustNewAPIKeySecurityScheme(t *testing.T) {
FILE: internal/auth/basic.go
type HTTPBasicCredentials (line 5) | type HTTPBasicCredentials struct
method GetUsername (line 17) | func (credentials *HTTPBasicCredentials) GetUsername() string {
method GetPassword (line 21) | func (credentials *HTTPBasicCredentials) GetPassword() string {
method Encode (line 25) | func (credentials *HTTPBasicCredentials) Encode() string {
function NewHTTPBasicCredentials (line 10) | func NewHTTPBasicCredentials(username string, password string) *HTTPBasi...
function NewAuthorizationBasicSecurityScheme (line 29) | func NewAuthorizationBasicSecurityScheme(name string, credentials *HTTPB...
function MustNewAuthorizationBasicSecurityScheme (line 46) | func MustNewAuthorizationBasicSecurityScheme(name string, credentials *H...
FILE: internal/auth/basic_test.go
function TestNewAuthorizationBasicSecurityScheme (line 10) | func TestNewAuthorizationBasicSecurityScheme(t *testing.T) {
function TestNewAuthorizationBasicSecurityScheme_WhenNilValue (line 25) | func TestNewAuthorizationBasicSecurityScheme_WhenNilValue(t *testing.T) {
function TestMustNewAuthorizationBasicSecurityScheme (line 35) | func TestMustNewAuthorizationBasicSecurityScheme(t *testing.T) {
FILE: internal/auth/bearer.go
function NewAuthorizationBearerSecurityScheme (line 7) | func NewAuthorizationBearerSecurityScheme(name string, value *string) (*...
function MustNewAuthorizationBearerSecurityScheme (line 34) | func MustNewAuthorizationBearerSecurityScheme(name string, value *string...
FILE: internal/auth/bearer_test.go
function TestNewAuthorizationBearerSecurityScheme (line 10) | func TestNewAuthorizationBearerSecurityScheme(t *testing.T) {
function TestNewAuthorizationBearerSecurityScheme_WhenNilValue (line 27) | func TestNewAuthorizationBearerSecurityScheme_WhenNilValue(t *testing.T) {
function TestNewAuthorizationBearerSecurityScheme_WhenJWTFormatValue (line 38) | func TestNewAuthorizationBearerSecurityScheme_WhenJWTFormatValue(t *test...
function TestMustNewAuthorizationBearerSecurityScheme (line 53) | func TestMustNewAuthorizationBearerSecurityScheme(t *testing.T) {
FILE: internal/auth/headers.go
constant AuthorizationHeader (line 3) | AuthorizationHeader = "Authorization"
constant BearerPrefix (line 4) | BearerPrefix = "Bearer"
constant BasicPrefix (line 5) | BasicPrefix = "Basic"
FILE: internal/auth/no_auth.go
function NewNoAuthSecurityScheme (line 5) | func NewNoAuthSecurityScheme() (*SecurityScheme, error) {
function MustNewNoAuthSecurityScheme (line 9) | func MustNewNoAuthSecurityScheme() *SecurityScheme {
FILE: internal/auth/no_auth_test.go
function TestNewNoAuthSecurityScheme (line 10) | func TestNewNoAuthSecurityScheme(t *testing.T) {
function TestMustNewNoAuthSecurityScheme (line 21) | func TestMustNewNoAuthSecurityScheme(t *testing.T) {
FILE: internal/auth/oauth.go
type OAuthFlow (line 9) | type OAuthFlow
constant AuthorizationCodeFlow (line 12) | AuthorizationCodeFlow OAuthFlow = "authorization_code"
constant ImplicitFlow (line 13) | ImplicitFlow OAuthFlow = "implicit"
constant ClientCredentials (line 14) | ClientCredentials OAuthFlow = "client_credentials"
type OAuthValue (line 17) | type OAuthValue struct
method SetAccessToken (line 33) | func (value *OAuthValue) SetAccessToken(accessToken string) {
method GetAccessToken (line 37) | func (value *OAuthValue) GetAccessToken() string {
function NewOAuthValue (line 24) | func NewOAuthValue(accessToken string, refreshToken *string, expiresIn *...
type OAuthConfig (line 41) | type OAuthConfig struct
function NewOAuthSecurityScheme (line 51) | func NewOAuthSecurityScheme(name string, in *SchemeIn, value *OAuthValue...
function MustNewOAuthSecurityScheme (line 81) | func MustNewOAuthSecurityScheme(name string, in *SchemeIn, value *OAuthV...
FILE: internal/auth/oauth_test.go
function TestNewOAuthSecurityScheme (line 10) | func TestNewOAuthSecurityScheme(t *testing.T) {
function TestNewOAuthSecurityScheme_WhenNilIn (line 30) | func TestNewOAuthSecurityScheme_WhenNilIn(t *testing.T) {
function TestNewOAuthSecurityScheme_WhenQueryIn (line 42) | func TestNewOAuthSecurityScheme_WhenQueryIn(t *testing.T) {
function TestNewOAuthSecurityScheme_WhenNilValue (line 55) | func TestNewOAuthSecurityScheme_WhenNilValue(t *testing.T) {
function TestNewOAuthSecurityScheme_WhenJWTFormatValue (line 66) | func TestNewOAuthSecurityScheme_WhenJWTFormatValue(t *testing.T) {
function TestMustNewOAuthSecurityScheme (line 82) | func TestMustNewOAuthSecurityScheme(t *testing.T) {
FILE: internal/auth/scheme.go
type SchemeName (line 3) | type SchemeName
method String (line 16) | func (s *SchemeName) String() string {
method Type (line 20) | func (e *SchemeName) Type() string {
constant BasicScheme (line 8) | BasicScheme SchemeName = "Basic"
constant BearerScheme (line 9) | BearerScheme SchemeName = "Bearer"
constant DigestScheme (line 10) | DigestScheme SchemeName = "Digest"
constant OAuthScheme (line 11) | OAuthScheme SchemeName = "OAuth"
constant PrivateToken (line 12) | PrivateToken SchemeName = "PrivateToken"
constant NoneScheme (line 13) | NoneScheme SchemeName = "None"
type SchemeIn (line 24) | type SchemeIn
constant InQuery (line 27) | InQuery SchemeIn = "query"
constant InHeader (line 28) | InHeader SchemeIn = "header"
constant InCookie (line 29) | InCookie SchemeIn = "cookie"
type TokenFormat (line 32) | type TokenFormat
constant JWTTokenFormat (line 35) | JWTTokenFormat TokenFormat = "jwt"
constant NoneTokenFormat (line 36) | NoneTokenFormat TokenFormat = "none"
FILE: internal/auth/scheme_test.go
function TestSchemeName_String (line 10) | func TestSchemeName_String(t *testing.T) {
function TestSchemeName_Type (line 15) | func TestSchemeName_Type(t *testing.T) {
function TestSchemeIn (line 20) | func TestSchemeIn(t *testing.T) {
function TestSchemeIn_String (line 25) | func TestSchemeIn_String(t *testing.T) {
FILE: internal/auth/security_scheme.go
function NewErrTokenFormatShouldBeJWT (line 11) | func NewErrTokenFormatShouldBeJWT() error {
type SecurityScheme (line 15) | type SecurityScheme struct
method GetType (line 53) | func (securityScheme *SecurityScheme) GetType() Type {
method GetScheme (line 57) | func (securityScheme *SecurityScheme) GetScheme() SchemeName {
method GetIn (line 61) | func (securityScheme *SecurityScheme) GetIn() *SchemeIn {
method GetToken (line 65) | func (securityScheme *SecurityScheme) GetToken() string {
method SetTokenFormat (line 77) | func (securityScheme *SecurityScheme) SetTokenFormat(tokenFormat Token...
method GetTokenFormat (line 86) | func (securityScheme *SecurityScheme) GetTokenFormat() *TokenFormat {
method GetName (line 90) | func (securityScheme *SecurityScheme) GetName() string {
method GetConfig (line 94) | func (securityScheme *SecurityScheme) GetConfig() interface{} {
method validateValue (line 98) | func (securityScheme *SecurityScheme) validateValue(value interface{})...
method SetValidValue (line 155) | func (securityScheme *SecurityScheme) SetValidValue(value interface{})...
method GetValidValue (line 169) | func (securityScheme *SecurityScheme) GetValidValue() interface{} {
method HasValidValue (line 173) | func (securityScheme *SecurityScheme) HasValidValue() bool {
method SetAttackValue (line 177) | func (securityScheme *SecurityScheme) SetAttackValue(value interface{}...
method GetAttackValue (line 191) | func (securityScheme *SecurityScheme) GetAttackValue() interface{} {
method GetHeaders (line 195) | func (securityScheme *SecurityScheme) GetHeaders() http.Header {
method GetCookies (line 233) | func (securityScheme *SecurityScheme) GetCookies() []*http.Cookie {
type SecuritySchemesMap (line 27) | type SecuritySchemesMap
function NewSecurityScheme (line 33) | func NewSecurityScheme(name string, config interface{}, t Type, scheme S...
FILE: internal/auth/security_scheme_test.go
function TestNewSecurityScheme (line 14) | func TestNewSecurityScheme(t *testing.T) {
function TestSetValidValue (line 88) | func TestSetValidValue(t *testing.T) {
function TestSetTokenFormat (line 205) | func TestSetTokenFormat(t *testing.T) {
function TestSetAttackValue (line 261) | func TestSetAttackValue(t *testing.T) {
function TestGetHeaders (line 378) | func TestGetHeaders(t *testing.T) {
FILE: internal/auth/type.go
type Type (line 3) | type Type
constant HttpType (line 6) | HttpType Type = "http"
constant OAuth2 (line 7) | OAuth2 Type = "oauth2"
constant OpenIdConnect (line 8) | OpenIdConnect Type = "openIdConnect"
constant ApiKey (line 9) | ApiKey Type = "apiKey"
constant MutualTLS (line 10) | MutualTLS Type = "mutualTLS"
constant None (line 11) | None Type = "none"
FILE: internal/auth/uniq_name.go
function GetSecuritySchemeUniqueName (line 3) | func GetSecuritySchemeUniqueName(securityScheme *SecurityScheme) string {
FILE: internal/auth/uniq_name_test.go
function TestGetSecuritySchemeUniqueName (line 10) | func TestGetSecuritySchemeUniqueName(t *testing.T) {
FILE: internal/cmd/args.go
function AddCommonArgs (line 21) | func AddCommonArgs(cmd *cobra.Command) {
function FilterScans (line 44) | func FilterScans(scans []string) []string {
function GetIncludeScans (line 54) | func GetIncludeScans() []string {
function GetExcludeScans (line 58) | func GetExcludeScans() []string {
function GetReportFormat (line 62) | func GetReportFormat() string {
function GetReportTransport (line 66) | func GetReportTransport() string {
function GetNoProgress (line 70) | func GetNoProgress() bool {
function GetSeverityThreshold (line 74) | func GetSeverityThreshold() float64 {
function SetReportFile (line 78) | func SetReportFile(f string) {
function SetReportURL (line 82) | func SetReportURL(u string) {
function SetSeverityThreshold (line 86) | func SetSeverityThreshold(t float64) {
function ClearValues (line 90) | func ClearValues() {
FILE: internal/cmd/args_test.go
function TestAddCommonArgs (line 11) | func TestAddCommonArgs(t *testing.T) {
FILE: internal/cmd/http.go
function NewHTTPClientFromCmd (line 9) | func NewHTTPClientFromCmd(cmd *cobra.Command) (*request.Client, error) {
FILE: internal/cmd/printtable/fingerprint_table.go
function FingerprintScanReport (line 11) | func FingerprintScanReport(reporter *report.Reporter) {
FILE: internal/cmd/printtable/printttable.go
function CreateTable (line 12) | func CreateTable(headers []string) *tablewriter.Table {
function DisplayUnexpectedErrorMessage (line 49) | func DisplayUnexpectedErrorMessage() {
FILE: internal/cmd/printtable/report_table.go
type ScanIssueReport (line 14) | type ScanIssueReport struct
type SortByPathAndSeverity (line 21) | type SortByPathAndSeverity
method Len (line 23) | func (a SortByPathAndSeverity) Len() int { return len(a) }
method Swap (line 24) | func (a SortByPathAndSeverity) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
method Less (line 25) | func (a SortByPathAndSeverity) Less(i, j int) bool {
function NewScanIssueReports (line 37) | func NewScanIssueReports(r *report.ScanReport) []*ScanIssueReport {
function NewFullScanIssueReports (line 52) | func NewFullScanIssueReports(reports []*report.ScanReport) []*ScanIssueR...
function severityTableColor (line 63) | func severityTableColor(v *report.IssueReport) color.Attribute {
function DisplayReportSummaryTable (line 78) | func DisplayReportSummaryTable(r *report.Reporter) {
function DisplayReportTable (line 104) | func DisplayReportTable(r *report.Reporter) {
FILE: internal/cmd/printtable/report_table_test.go
function TestNewScanIssueReports (line 12) | func TestNewScanIssueReports(t *testing.T) {
function TestNewFullScanIssueReports (line 50) | func TestNewFullScanIssueReports(t *testing.T) {
FILE: internal/cmd/printtable/wellknown_paths_table.go
function wellKnownPathsFromReport (line 16) | func wellKnownPathsFromReport(r *report.ScanReport, header string) [][]s...
function WellKnownPathsScanReport (line 30) | func WellKnownPathsScanReport(reporter *report.Reporter) {
FILE: internal/cmd/progressbar.go
function NewProgressBar (line 7) | func NewProgressBar(max int) *progressbar.ProgressBar {
FILE: internal/cmd/report.go
function PrintOrExportReport (line 13) | func PrintOrExportReport(format string, transport string, report *report...
function PrintTable (line 57) | func PrintTable(report *report.Reporter) {
function ExportJSON (line 64) | func ExportJSON(report *report.Reporter) ([]byte, error) {
function ExportYAML (line 68) | func ExportYAML(report *report.Reporter) ([]byte, error) {
function exportWithTransport (line 72) | func exportWithTransport(transport string, output []byte) error {
function writeFile (line 89) | func writeFile(path string, output []byte) error {
function sendHTTP (line 104) | func sendHTTP(outputURL string, output []byte) error {
FILE: internal/operation/operation.go
function GenerateOperationID (line 21) | func GenerateOperationID(method string, path string) string {
type Operation (line 40) | type Operation struct
method IsReachable (line 105) | func (operation *Operation) IsReachable() error {
method WithOpenapiOperation (line 151) | func (operation *Operation) WithOpenapiOperation(docPath string, opena...
method WithHeader (line 162) | func (operation *Operation) WithHeader(header http.Header) *Operation {
method WithCookies (line 167) | func (operation *Operation) WithCookies(cookies []*http.Cookie) *Opera...
method NewRequest (line 172) | func (operation *Operation) NewRequest() (*request.Request, error) {
method GetSecuritySchemes (line 183) | func (operation *Operation) GetSecuritySchemes() []*auth.SecurityScheme {
method GetSecurityScheme (line 190) | func (operation *Operation) GetSecurityScheme() *auth.SecurityScheme {
method SetSecuritySchemes (line 197) | func (operation *Operation) SetSecuritySchemes(securitySchemes []*auth...
method GetPath (line 202) | func (operation *Operation) GetPath() string {
method GetOpenAPIDocPath (line 206) | func (operation *Operation) GetOpenAPIDocPath() *string {
method SetID (line 210) | func (operation *Operation) SetID(id string) *Operation {
method GenerateID (line 215) | func (operation *Operation) GenerateID() *Operation {
method GetID (line 220) | func (operation *Operation) GetID() string {
method Clone (line 224) | func (o *Operation) Clone() (*Operation, error) {
function getBody (line 54) | func getBody(body io.Reader) ([]byte, error) {
function NewOperation (line 69) | func NewOperation(method string, operationUrl string, body io.Reader, cl...
function MustNewOperation (line 97) | func MustNewOperation(method string, operationUrl string, body *bytes.Bu...
function NewOperationFromRequest (line 138) | func NewOperationFromRequest(r *request.Request) (*Operation, error) {
FILE: internal/operation/operation_test.go
function TestNewOperation (line 18) | func TestNewOperation(t *testing.T) {
function TestMustNewOperation (line 31) | func TestMustNewOperation(t *testing.T) {
function TestOperation_IsReachable (line 56) | func TestOperation_IsReachable(t *testing.T) {
function TestOperation_IsReachableWhenNotReachable (line 69) | func TestOperation_IsReachableWhenNotReachable(t *testing.T) {
function TestOperation_IsReachableWhenHTTPs (line 78) | func TestOperation_IsReachableWhenHTTPs(t *testing.T) {
function TestOperation_IsReachableWhenHTTPsAndNoPort (line 87) | func TestOperation_IsReachableWhenHTTPsAndNoPort(t *testing.T) {
function TestOperation_IsReachableWhenHTTPAndNoPort (line 96) | func TestOperation_IsReachableWhenHTTPAndNoPort(t *testing.T) {
function TestOperation_IsReachableWhenUnsupportedScheme (line 105) | func TestOperation_IsReachableWhenUnsupportedScheme(t *testing.T) {
function TestNewOperationFromRequest (line 114) | func TestNewOperationFromRequest(t *testing.T) {
function TestNewOperationFromRequest_WithBody (line 134) | func TestNewOperationFromRequest_WithBody(t *testing.T) {
function TestOperation_GetSecurityScheme (line 149) | func TestOperation_GetSecurityScheme(t *testing.T) {
function TestOperationCloneWithSecuritySchemes (line 171) | func TestOperationCloneWithSecuritySchemes(t *testing.T) {
function TestOperation_WithOpenapiOperation (line 185) | func TestOperation_WithOpenapiOperation(t *testing.T) {
function TestOperation_WithOpenapiOperation_WithoutOperationID (line 196) | func TestOperation_WithOpenapiOperation_WithoutOperationID(t *testing.T) {
function TestOperation_WithOpenapiOperation_WithoutOperationIDAndParameters (line 205) | func TestOperation_WithOpenapiOperation_WithoutOperationIDAndParameters(...
function TestOperation_WithHeader (line 214) | func TestOperation_WithHeader(t *testing.T) {
function TestOperation_WithCookies (line 225) | func TestOperation_WithCookies(t *testing.T) {
function TestOperation_GenerateID (line 237) | func TestOperation_GenerateID(t *testing.T) {
function TestOperation_SetId (line 263) | func TestOperation_SetId(t *testing.T) {
function TestMarshalJSON (line 271) | func TestMarshalJSON(t *testing.T) {
FILE: internal/operation/operations.go
type Operations (line 3) | type Operations
method Len (line 5) | func (o Operations) Len() int { return len(o) }
method Swap (line 6) | func (o Operations) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
method Less (line 7) | func (o Operations) Less(i, j int) bool {
method GetByID (line 15) | func (o Operations) GetByID(id string) *Operation {
FILE: internal/operation/operations_test.go
function TestOperations_Less (line 11) | func TestOperations_Less(t *testing.T) {
function TestOperations_GetByID (line 61) | func TestOperations_GetByID(t *testing.T) {
FILE: internal/request/client.go
function GetDefaultClient (line 17) | func GetDefaultClient() *Client {
function SetDefaultClient (line 25) | func SetDefaultClient(client *Client) {
type Client (line 29) | type Client struct
method WithHeader (line 102) | func (c *Client) WithHeader(header http.Header) *Client {
method WithCookies (line 107) | func (c *Client) WithCookies(cookies []*http.Cookie) *Client {
method ClearSecurityScheme (line 121) | func (c *Client) ClearSecurityScheme(securityScheme *auth.SecuritySche...
method ClearSecuritySchemes (line 139) | func (c *Client) ClearSecuritySchemes(securitySchemes []*auth.Security...
type NewClientOptions (line 35) | type NewClientOptions struct
function NewClientFromHTTPClient (line 45) | func NewClientFromHTTPClient(httpClient *http.Client, limiter *rate.Limi...
function NewClient (line 57) | func NewClient(opts NewClientOptions) *Client {
function removeCookie (line 112) | func removeCookie(cookies []*http.Cookie, cookie *http.Cookie) []*http.C...
FILE: internal/request/client_test.go
function TestNewClient_DefaultOptions (line 13) | func TestNewClient_DefaultOptions(t *testing.T) {
function TestNewClient_CustomOptions (line 25) | func TestNewClient_CustomOptions(t *testing.T) {
function TestGetClient (line 45) | func TestGetClient(t *testing.T) {
function TestSetClient (line 50) | func TestSetClient(t *testing.T) {
function TestClient_WithHeader (line 56) | func TestClient_WithHeader(t *testing.T) {
function TestClient_WithCookies (line 64) | func TestClient_WithCookies(t *testing.T) {
function TestClient_ClearHeaderWithSecurityScheme (line 72) | func TestClient_ClearHeaderWithSecurityScheme(t *testing.T) {
function TestClient_ClearSecuritySchemes (line 96) | func TestClient_ClearSecuritySchemes(t *testing.T) {
FILE: internal/request/error.go
function NilResponseError (line 5) | func NilResponseError() error {
FILE: internal/request/request.go
type Request (line 13) | type Request struct
method WithHeader (line 68) | func (r *Request) WithHeader(header http.Header) *Request {
method WithCookies (line 75) | func (r *Request) WithCookies(cookies []*http.Cookie) *Request {
method WithSecurityScheme (line 82) | func (r *Request) WithSecurityScheme(securityScheme *auth.SecuritySche...
method GetID (line 94) | func (r *Request) GetID() string {
method GetMethod (line 98) | func (r *Request) GetMethod() string {
method GetHeader (line 102) | func (r *Request) GetHeader() http.Header {
method SetHeader (line 106) | func (r *Request) SetHeader(key string, value string) *Request {
method AddHeader (line 111) | func (r *Request) AddHeader(key string, value string) *Request {
method GetCookies (line 116) | func (r *Request) GetCookies() []*http.Cookie {
method AddCookie (line 120) | func (r *Request) AddCookie(cookie *http.Cookie) *Request {
method GetURL (line 125) | func (r *Request) GetURL() string {
method GetBody (line 129) | func (r *Request) GetBody() []byte {
method SetBody (line 137) | func (r *Request) SetBody(body io.Reader) *Request {
method Do (line 149) | func (r *Request) Do() (*Response, error) {
function getBody (line 21) | func getBody(body io.Reader) ([]byte, error) {
function NewRequest (line 36) | func NewRequest(method string, url string, body io.Reader, client *Clien...
FILE: internal/request/request_test.go
function TestNewRequest (line 15) | func TestNewRequest(t *testing.T) {
function TestWithHeader (line 29) | func TestWithHeader(t *testing.T) {
function TestWithHTTPCookies (line 43) | func TestWithHTTPCookies(t *testing.T) {
function TestWithSecurityScheme (line 59) | func TestWithSecurityScheme(t *testing.T) {
function TestGetID (line 72) | func TestGetID(t *testing.T) {
function TestGetMethod (line 83) | func TestGetMethod(t *testing.T) {
function TestGetMethodWithPost (line 93) | func TestGetMethodWithPost(t *testing.T) {
function TestGetHeader (line 103) | func TestGetHeader(t *testing.T) {
function TestSetHeader (line 116) | func TestSetHeader(t *testing.T) {
function TestAddHeader (line 129) | func TestAddHeader(t *testing.T) {
function TestGetBody (line 145) | func TestGetBody(t *testing.T) {
function TestSetBody (line 158) | func TestSetBody(t *testing.T) {
function TestGetCookies (line 186) | func TestGetCookies(t *testing.T) {
function TestAddCookie (line 206) | func TestAddCookie(t *testing.T) {
function TestGetURL (line 222) | func TestGetURL(t *testing.T) {
function TestGetURLWithQueryParams (line 232) | func TestGetURLWithQueryParams(t *testing.T) {
function TestDo (line 244) | func TestDo(t *testing.T) {
function TestDoWithHeaders (line 269) | func TestDoWithHeaders(t *testing.T) {
function TestDoWithClientHeaders (line 297) | func TestDoWithClientHeaders(t *testing.T) {
function TestDoWithBody (line 329) | func TestDoWithBody(t *testing.T) {
function TestDoWithSecuritySchemeHeaders (line 355) | func TestDoWithSecuritySchemeHeaders(t *testing.T) {
function TestDoWithHeadersSecuritySchemeHeaders (line 382) | func TestDoWithHeadersSecuritySchemeHeaders(t *testing.T) {
function TestDoWithCookiesSecuritySchemeHeaders (line 415) | func TestDoWithCookiesSecuritySchemeHeaders(t *testing.T) {
function TestDoWithCookies (line 449) | func TestDoWithCookies(t *testing.T) {
FILE: internal/request/response.go
type Response (line 9) | type Response struct
method GetStatusCode (line 38) | func (response *Response) GetStatusCode() int {
method GetBody (line 42) | func (response *Response) GetBody() *bytes.Buffer {
method GetHeader (line 46) | func (response *Response) GetHeader() http.Header {
method GetCookies (line 50) | func (response *Response) GetCookies() []*http.Cookie {
function NewResponse (line 14) | func NewResponse(response *http.Response) (*Response, error) {
FILE: internal/request/response_test.go
function TestNewResponse (line 13) | func TestNewResponse(t *testing.T) {
function TestNewResponseNil (line 31) | func TestNewResponseNil(t *testing.T) {
function TestNewResponseNilBody (line 37) | func TestNewResponseNilBody(t *testing.T) {
FILE: internal/scan/attempt.go
type IssueScanAttemptStatus (line 8) | type IssueScanAttemptStatus
method String (line 10) | func (attemptStatus IssueScanAttemptStatus) String() string {
constant IssueScanAttemptStatusPassed (line 15) | IssueScanAttemptStatusPassed IssueScanAttemptStatus = "passed"
constant IssueScanAttemptStatusFailed (line 16) | IssueScanAttemptStatusFailed IssueScanAttemptStatus = "failed"
constant IssueScanAttemptStatusNone (line 17) | IssueScanAttemptStatusNone IssueScanAttemptStatus = "none"
type IssueScanAttempt (line 20) | type IssueScanAttempt struct
method WithBooleanStatus (line 39) | func (scanAttempt *IssueScanAttempt) WithBooleanStatus(status bool) *I...
method Fail (line 46) | func (scanAttempt *IssueScanAttempt) Fail() *IssueScanAttempt {
method Pass (line 51) | func (scanAttempt *IssueScanAttempt) Pass() *IssueScanAttempt {
method HasPassed (line 56) | func (scanAttempt *IssueScanAttempt) HasPassed() bool {
method HasFailed (line 60) | func (scanAttempt *IssueScanAttempt) HasFailed() bool {
function NewIssueScanAttempt (line 28) | func NewIssueScanAttempt(operation *operation.Operation, req *request.Re...
FILE: internal/scan/attempt_test.go
function TestNewIssueScanAttempt (line 12) | func TestNewIssueScanAttempt(t *testing.T) {
function TestIssueScanAttempt_WithBooleanStatus (line 27) | func TestIssueScanAttempt_WithBooleanStatus(t *testing.T) {
function TestIssueScanAttempt_Fail (line 42) | func TestIssueScanAttempt_Fail(t *testing.T) {
function TestIssueScanAttempt_Pass (line 54) | func TestIssueScanAttempt_Pass(t *testing.T) {
function TestIssueScanAttempt_HasPassed (line 66) | func TestIssueScanAttempt_HasPassed(t *testing.T) {
function TestIssueScanAttempt_HasFailed (line 79) | func TestIssueScanAttempt_HasFailed(t *testing.T) {
FILE: internal/scan/scan_url.go
function ScanURL (line 8) | func ScanURL(operation *operation.Operation, securityScheme *auth.Securi...
FILE: internal/scan/utils.go
function IsUnauthorizedStatusCodeOrSimilar (line 9) | func IsUnauthorizedStatusCodeOrSimilar(res *request.Response) bool {
FILE: internal/scan/utils_test.go
function TestIsUnauthorizedStatusCodeOrSimilar (line 14) | func TestIsUnauthorizedStatusCodeOrSimilar(t *testing.T) {
FILE: main.go
function main (line 11) | func main() {
FILE: openapi/base_url.go
method BaseUrl (line 7) | func (openapi *OpenAPI) BaseUrl() *url.URL {
method SetBaseUrl (line 33) | func (openapi *OpenAPI) SetBaseUrl(baseUrl *url.URL) *OpenAPI {
FILE: openapi/base_url_test.go
function TestBaseUrl (line 12) | func TestBaseUrl(t *testing.T) {
function TestBaseUrlWithInvalidURL (line 24) | func TestBaseUrlWithInvalidURL(t *testing.T) {
function TestBaseUrlWithBasePath (line 35) | func TestBaseUrlWithBasePath(t *testing.T) {
FILE: openapi/loader.go
function newLoader (line 12) | func newLoader(ctx context.Context) *openapi3.Loader {
function LoadFromData (line 22) | func LoadFromData(ctx context.Context, data []byte) (*OpenAPI, error) {
function LoadOpenAPI (line 31) | func LoadOpenAPI(ctx context.Context, urlOrPath string) (*OpenAPI, error) {
FILE: openapi/loader_test.go
function TestLoadOpenAPIWithEmptyURLOrPath (line 15) | func TestLoadOpenAPIWithEmptyURLOrPath(t *testing.T) {
function TestLoadOpenAPIWithInvalidURL (line 22) | func TestLoadOpenAPIWithInvalidURL(t *testing.T) {
function TestLoadOpenAPIWithValidURL (line 29) | func TestLoadOpenAPIWithValidURL(t *testing.T) {
function TestLoadOpenAPIWithNonExistentFile (line 43) | func TestLoadOpenAPIWithNonExistentFile(t *testing.T) {
function TestLoadOpenAPIWithValidFilePath (line 50) | func TestLoadOpenAPIWithValidFilePath(t *testing.T) {
FILE: openapi/openapi.go
constant otelName (line 11) | otelName = "github.com/cerberauth/vulnapi/openapi"
constant otelErrorReasonAttributeKey (line 13) | otelErrorReasonAttributeKey = attribute.Key("error_reason")
type OpenAPI (line 16) | type OpenAPI struct
function NewOpenAPI (line 22) | func NewOpenAPI(doc *openapi3.T) *OpenAPI {
FILE: openapi/operation.go
constant otelMethodAttributeKey (line 20) | otelMethodAttributeKey = attribute.Key("method")
constant otelMediaTypeAttributeKey (line 21) | otelMediaTypeAttributeKey = attribute.Key("media_type")
constant otelSecuritySchemesTypesAttributeKey (line 22) | otelSecuritySchemesTypesAttributeKey = attribute.Key("security_schemes")
function getOperationSecuritySchemes (line 25) | func getOperationSecuritySchemes(securityRequirements *openapi3.Security...
function GetOperationPath (line 48) | func GetOperationPath(p string, params openapi3.Parameters) (string, err...
method Operations (line 60) | func (openapi *OpenAPI) Operations(ctx context.Context, client *request....
FILE: openapi/param.go
constant maximumDepth (line 12) | maximumDepth = 4
constant FloatParamType (line 15) | FloatParamType = "float"
constant DoubleParamType (line 16) | DoubleParamType = "double"
constant Int32ParamFormat (line 17) | Int32ParamFormat = "int32"
constant Int64ParamFormat (line 18) | Int64ParamFormat = "int64"
function NewErrNoSupportedBodyMediaType (line 21) | func NewErrNoSupportedBodyMediaType() error {
function getParameterValue (line 25) | func getParameterValue(param *openapi3.Parameter) string {
function mapRequestBodyFakeValueToJSON (line 48) | func mapRequestBodyFakeValueToJSON(schema *openapi3.Schema, fakeValue in...
function getRequestBodyValue (line 84) | func getRequestBodyValue(requestBody *openapi3.RequestBody) (*bytes.Buff...
function parseSchemaExample (line 99) | func parseSchemaExample(schema *openapi3.Schema) (interface{}, error) {
function getSchemaValue (line 149) | func getSchemaValue(schema *openapi3.Schema, depth int) interface{} {
FILE: openapi/param_test.go
function TestGetSchemaValue_WhenNoParameters (line 11) | func TestGetSchemaValue_WhenNoParameters(t *testing.T) {
function TestGetSchemaValue_WhenHeaderParametersWithExample (line 26) | func TestGetSchemaValue_WhenHeaderParametersWithExample(t *testing.T) {
function TestGetSchemaValue_WhenHeaderParametersWithoutExample (line 43) | func TestGetSchemaValue_WhenHeaderParametersWithoutExample(t *testing.T) {
function TestGetSchemaValue_WhenHeaderParametersNotRequired (line 60) | func TestGetSchemaValue_WhenHeaderParametersNotRequired(t *testing.T) {
function TestGetSchemaValue_WhenCookieParametersWithExample (line 75) | func TestGetSchemaValue_WhenCookieParametersWithExample(t *testing.T) {
function TestGetSchemaValue_WhenCookieParametersWithoutExample (line 92) | func TestGetSchemaValue_WhenCookieParametersWithoutExample(t *testing.T) {
function TestGetSchemaValue_WhenCookieParametersNotRequired (line 109) | func TestGetSchemaValue_WhenCookieParametersNotRequired(t *testing.T) {
function TestGetSchemaValue_WhenPathParametersWithExample (line 124) | func TestGetSchemaValue_WhenPathParametersWithExample(t *testing.T) {
function TestGetSchemaValue_WhenPathParametersWithoutExample (line 141) | func TestGetSchemaValue_WhenPathParametersWithoutExample(t *testing.T) {
function TestGetSchemaValue_WhenRequestBodyParametersWithExample (line 157) | func TestGetSchemaValue_WhenRequestBodyParametersWithExample(t *testing....
function TestGetSchemaValue_WhenRequestBodyParametersWithMultiMediaTypes (line 176) | func TestGetSchemaValue_WhenRequestBodyParametersWithMultiMediaTypes(t *...
function TestGetSchemaValue_WhenRequestBodyParametersWithoutExample (line 192) | func TestGetSchemaValue_WhenRequestBodyParametersWithoutExample(t *testi...
function TestGetSchemaValue_WhenRequestBodyParametersIsString (line 209) | func TestGetSchemaValue_WhenRequestBodyParametersIsString(t *testing.T) {
function TestGetSchemaValue_RequestBodyParameters (line 226) | func TestGetSchemaValue_RequestBodyParameters(t *testing.T) {
function TestGetSchemaValue_RequestBodyParametersAndExample (line 313) | func TestGetSchemaValue_RequestBodyParametersAndExample(t *testing.T) {
function TestRecursiveParameters (line 398) | func TestRecursiveParameters(t *testing.T) {
FILE: openapi/security_scheme.go
constant HttpSchemeType (line 16) | HttpSchemeType string = "http"
constant OAuth2SchemeType (line 17) | OAuth2SchemeType string = "oauth2"
constant OpenIdConnectSchemeType (line 18) | OpenIdConnectSchemeType string = "openidconnect"
constant ApiKeySchemeType (line 19) | ApiKeySchemeType string = "apikey"
constant BasicScheme (line 21) | BasicScheme string = "basic"
constant BearerScheme (line 22) | BearerScheme string = "bearer"
constant otelSchemeTypeAttributeKey (line 32) | otelSchemeTypeAttributeKey = attribute.Key("scheme_type")
constant otelSchemeSchemeAttributeKey (line 33) | otelSchemeSchemeAttributeKey = attribute.Key("scheme_scheme")
constant otelSchemeInAttributeKey (line 34) | otelSchemeInAttributeKey = attribute.Key("scheme_in")
constant otelSchemeBearerFormatKey (line 35) | otelSchemeBearerFormatKey = attribute.Key("scheme_bearer_format")
function NewErrUnsupportedBearerFormat (line 38) | func NewErrUnsupportedBearerFormat(bearerFormat string) error {
function NewErrUnsupportedScheme (line 42) | func NewErrUnsupportedScheme(scheme string) error {
function NewErrUnsupportedSecuritySchemeType (line 46) | func NewErrUnsupportedSecuritySchemeType(schemeType string) error {
function mapHTTPSchemeType (line 50) | func mapHTTPSchemeType(name string, scheme *openapi3.SecuritySchemeRef, ...
function mapAPIKeySchemeType (line 77) | func mapAPIKeySchemeType(name string, scheme *openapi3.SecuritySchemeRef...
function mapOAuth2SchemeType (line 81) | func mapOAuth2SchemeType(name string, scheme *openapi3.SecuritySchemeRef...
method SecuritySchemeMap (line 108) | func (openapi *OpenAPI) SecuritySchemeMap(ctx context.Context, values *S...
FILE: openapi/security_scheme_test.go
function TestSecuritySchemeMap_WithoutSecurityComponents (line 13) | func TestSecuritySchemeMap_WithoutSecurityComponents(t *testing.T) {
function TestSecuritySchemeMap_WithUnknownSchemeType (line 25) | func TestSecuritySchemeMap_WithUnknownSchemeType(t *testing.T) {
function TestSecuritySchemeMap_WithUnknownScheme (line 39) | func TestSecuritySchemeMap_WithUnknownScheme(t *testing.T) {
function TestSecuritySchemeMap_WithUnknownBearerFormat (line 53) | func TestSecuritySchemeMap_WithUnknownBearerFormat(t *testing.T) {
function TestSecuritySchemeMap_WithHTTPJWTBearer (line 67) | func TestSecuritySchemeMap_WithHTTPJWTBearer(t *testing.T) {
function TestSecuritySchemeMap_WithHTTPBearer (line 82) | func TestSecuritySchemeMap_WithHTTPBearer(t *testing.T) {
function TestSecuritySchemeMap_WithoutHTTPJWTBearerAndDefaultValue (line 96) | func TestSecuritySchemeMap_WithoutHTTPJWTBearerAndDefaultValue(t *testin...
function TestSecuritySchemeMap_WithAPIKeyInHeader (line 113) | func TestSecuritySchemeMap_WithAPIKeyInHeader(t *testing.T) {
function TestSecuritySchemeMap_WithInvalidValueType (line 127) | func TestSecuritySchemeMap_WithInvalidValueType(t *testing.T) {
function TestSecuritySchemeMap_WithOAuth (line 142) | func TestSecuritySchemeMap_WithOAuth(t *testing.T) {
function TestSecuritySchemeMap_WithOAuthAndAuthorizationCodeFlow (line 156) | func TestSecuritySchemeMap_WithOAuthAndAuthorizationCodeFlow(t *testing....
function TestSecuritySchemeMap_WithOAuthAndImplicitFlow (line 174) | func TestSecuritySchemeMap_WithOAuthAndImplicitFlow(t *testing.T) {
function TestSecuritySchemeMap_WithOAuthAndClientCredentialsFlow (line 192) | func TestSecuritySchemeMap_WithOAuthAndClientCredentialsFlow(t *testing....
function TestSecuritySchemeMap_WithOpenIDConnect (line 210) | func TestSecuritySchemeMap_WithOpenIDConnect(t *testing.T) {
FILE: openapi/security_scheme_values.go
type SecuritySchemeValues (line 3) | type SecuritySchemeValues struct
method WithDefault (line 20) | func (s *SecuritySchemeValues) WithDefault(defaultValue interface{}) *...
method GetDefault (line 25) | func (s *SecuritySchemeValues) GetDefault() interface{} {
method Get (line 29) | func (s *SecuritySchemeValues) Get(key string) interface{} {
method Set (line 36) | func (s *SecuritySchemeValues) Set(key string, value interface{}) {
function NewSecuritySchemeValues (line 8) | func NewSecuritySchemeValues(values map[string]interface{}) *SecuritySch...
function NewEmptySecuritySchemeValues (line 15) | func NewEmptySecuritySchemeValues() *SecuritySchemeValues {
FILE: openapi/security_scheme_values_test.go
function TestNewSecuritySchemeValues (line 10) | func TestNewSecuritySchemeValues(t *testing.T) {
function TestNewEmptySecuritySchemeValues (line 21) | func TestNewEmptySecuritySchemeValues(t *testing.T) {
function TestSecuritySchemeValues_WithDefault (line 29) | func TestSecuritySchemeValues_WithDefault(t *testing.T) {
function TestSecuritySchemeValues_GetDefault (line 36) | func TestSecuritySchemeValues_GetDefault(t *testing.T) {
function TestSecuritySchemeValues_Get (line 43) | func TestSecuritySchemeValues_Get(t *testing.T) {
function TestSecuritySchemeValues_Get_WhenNotExist (line 51) | func TestSecuritySchemeValues_Get_WhenNotExist(t *testing.T) {
function TestSecuritySchemeValues_Set (line 58) | func TestSecuritySchemeValues_Set(t *testing.T) {
FILE: openapi/validate.go
method Validate (line 8) | func (openapi *OpenAPI) Validate(ctx context.Context) error {
FILE: openapi/validate_test.go
function TestValidate (line 11) | func TestValidate(t *testing.T) {
function TestValidateWithoutServers (line 22) | func TestValidateWithoutServers(t *testing.T) {
function TestValidateWithInvalidBaseUrl (line 34) | func TestValidateWithInvalidBaseUrl(t *testing.T) {
FILE: report/capec.go
type CAPEC (line 3) | type CAPEC
constant CAPEC_31_Manipulating_HTTP_Cookies (line 6) | CAPEC_31_Manipulating_HTTP_Cookies CAPEC = "CAPEC-31: Accessing/Intercep...
FILE: report/curl_report.go
type CurlReport (line 9) | type CurlReport struct
method AddReport (line 40) | func (cr *CurlReport) AddReport(r *ScanReport) {
function NewCurlReport (line 21) | func NewCurlReport(method string, url string, data interface{}, header h...
FILE: report/curl_report_test.go
function TestNewCurlReport (line 15) | func TestNewCurlReport(t *testing.T) {
function Test_CurlReport_AddReport (line 40) | func Test_CurlReport_AddReport(t *testing.T) {
function TestAddReport_WhenScanReportHasNoFailedIssueReport (line 69) | func TestAddReport_WhenScanReportHasNoFailedIssueReport(t *testing.T) {
FILE: report/cwe.go
type CWE (line 3) | type CWE
constant CWE_16_Configuration (line 6) | CWE_16_Configuration CWE = "CWE-16: Configuration"
constant CWE_287_Improper_Authentication (line 8) | CWE_287_Improper_Authentication CWE = "CWE-287: Imprope...
constant CWE_345_Insufficient_Verification_Authenticity (line 9) | CWE_345_Insufficient_Verification_Authenticity CWE = "CWE-345: Insuffi...
constant CWE_489_Active_Debug_Code (line 10) | CWE_489_Active_Debug_Code CWE = "CWE-489: Active ...
constant CWE_613_Insufficient_Session_Expiration (line 11) | CWE_613_Insufficient_Session_Expiration CWE = "CWE-613: Insuffi...
constant CWE_614_Sensitive_Cookie_Without_Secure_Flag (line 12) | CWE_614_Sensitive_Cookie_Without_Secure_Flag CWE = "CWE-614: Sensiti...
constant CWE_942_Overly_Permissive_CORS_Policy (line 13) | CWE_942_Overly_Permissive_CORS_Policy CWE = "CWE-942: Permiss...
constant CWE_1004_Sensitive_Cookie_Without_Http_Only (line 14) | CWE_1004_Sensitive_Cookie_Without_Http_Only CWE = "CWE-1004: Sensit...
constant CWE_1021_Improper_Restriction_Rendered_UI (line 15) | CWE_1021_Improper_Restriction_Rendered_UI CWE = "CWE-1021: Improp...
constant CWE_1275_Sensitive_Cookie_With_Improper_SameSite (line 16) | CWE_1275_Sensitive_Cookie_With_Improper_SameSite CWE = "CWE-1275: Sensit...
FILE: report/graphql_report.go
type GraphQLOperationReport (line 5) | type GraphQLOperationReport struct
function NewGraphQLOperationReport (line 13) | func NewGraphQLOperationReport() GraphQLOperationReport {
type GraphQLOperationsMethods (line 21) | type GraphQLOperationsMethods
type GraphQLReport (line 22) | type GraphQLReport struct
method AddReport (line 41) | func (gr *GraphQLReport) AddReport(r *ScanReport) {
function NewGraphQLReport (line 29) | func NewGraphQLReport(url string, securitySchemes []*auth.SecurityScheme...
FILE: report/issue.go
type Classifications (line 3) | type Classifications struct
type CVSS (line 9) | type CVSS struct
type Issue (line 15) | type Issue struct
FILE: report/issue_report.go
type IssueReportStatus (line 15) | type IssueReportStatus
method String (line 17) | func (vrs IssueReportStatus) String() string {
constant IssueReportStatusPassed (line 22) | IssueReportStatusPassed IssueReportStatus = "passed"
constant IssueReportStatusFailed (line 23) | IssueReportStatusFailed IssueReportStatus = "failed"
constant IssueReportStatusSkipped (line 24) | IssueReportStatusSkipped IssueReportStatus = "skipped"
constant IssueReportStatusNone (line 25) | IssueReportStatusNone IssueReportStatus = "none"
type IssueScanReport (line 35) | type IssueScanReport struct
method GetStatus (line 47) | func (issueScanReport *IssueScanReport) GetStatus() scan.IssueScanAtte...
method HasFailed (line 51) | func (issueScanReport *IssueScanReport) HasFailed() bool {
method HasPassed (line 55) | func (issueScanReport *IssueScanReport) HasPassed() bool {
function NewIssueScanReport (line 40) | func NewIssueScanReport(id string, status *scan.IssueScanAttemptStatus) ...
type IssueReport (line 59) | type IssueReport struct
method WithOperation (line 88) | func (vr *IssueReport) WithOperation(operation *operation.Operation) *...
method WithSecurityScheme (line 93) | func (vr *IssueReport) WithSecurityScheme(securityScheme *auth.Securit...
method WithStatus (line 98) | func (vr *IssueReport) WithStatus(status IssueReportStatus) *IssueRepo...
method WithBooleanStatus (line 107) | func (vr *IssueReport) WithBooleanStatus(status bool) *IssueReport {
method Fail (line 114) | func (vr *IssueReport) Fail() *IssueReport {
method HasFailed (line 118) | func (vr *IssueReport) HasFailed() bool {
method Pass (line 122) | func (vr *IssueReport) Pass() *IssueReport {
method HasPassed (line 126) | func (vr *IssueReport) HasPassed() bool {
method Skip (line 130) | func (vr *IssueReport) Skip() *IssueReport {
method HasBeenSkipped (line 134) | func (vr *IssueReport) HasBeenSkipped() bool {
method IsInfoRiskSeverity (line 138) | func (vr *IssueReport) IsInfoRiskSeverity() bool {
method IsLowRiskSeverity (line 142) | func (vr *IssueReport) IsLowRiskSeverity() bool {
method IsMediumRiskSeverity (line 146) | func (vr *IssueReport) IsMediumRiskSeverity() bool {
method IsHighRiskSeverity (line 150) | func (vr *IssueReport) IsHighRiskSeverity() bool {
method IsCriticalRiskSeverity (line 154) | func (vr *IssueReport) IsCriticalRiskSeverity() bool {
method WithScanAttempt (line 158) | func (vr *IssueReport) WithScanAttempt(attempt *scan.IssueScanAttempt)...
method AddScanAttempt (line 162) | func (vr *IssueReport) AddScanAttempt(attempt *scan.IssueScanAttempt) ...
method String (line 167) | func (vr *IssueReport) String() string {
method SeverityLevelString (line 171) | func (vr *IssueReport) SeverityLevelString() string {
method Clone (line 188) | func (vr *IssueReport) Clone() *IssueReport {
constant otelIssueIdAttribute (line 71) | otelIssueIdAttribute = attribute.Key("issue_id")
constant otelIssueReportStatusAttribute (line 72) | otelIssueReportStatusAttribute = attribute.Key("issue_report_status")
function NewIssueReport (line 75) | func NewIssueReport(issue Issue) *IssueReport {
FILE: report/issue_report_test.go
function TestNewIssueScanReport (line 15) | func TestNewIssueScanReport(t *testing.T) {
function TestIssueScanReport_GetStatus (line 24) | func TestIssueScanReport_GetStatus(t *testing.T) {
function TestIssueScanReport_HasFailed (line 32) | func TestIssueScanReport_HasFailed(t *testing.T) {
function TestIssueScanReport_HasPassed (line 40) | func TestIssueScanReport_HasPassed(t *testing.T) {
function TestNewIssueReport (line 48) | func TestNewIssueReport(t *testing.T) {
function TestIssueReport_WithOperation (line 64) | func TestIssueReport_WithOperation(t *testing.T) {
function TestIssueReport_WithSecurityScheme (line 80) | func TestIssueReport_WithSecurityScheme(t *testing.T) {
function TestIssueReport_WithStatus (line 97) | func TestIssueReport_WithStatus(t *testing.T) {
function TestIssueReport_WithBooleanStatus_WhenFalse (line 111) | func TestIssueReport_WithBooleanStatus_WhenFalse(t *testing.T) {
function TestIssueReport_WithBooleanStatus_WhenTrue (line 125) | func TestIssueReport_WithBooleanStatus_WhenTrue(t *testing.T) {
function TestIssueReport_Fail (line 139) | func TestIssueReport_Fail(t *testing.T) {
function TestIssueReport_HasFailed (line 153) | func TestIssueReport_HasFailed(t *testing.T) {
function TestIssueReport_Pass (line 167) | func TestIssueReport_Pass(t *testing.T) {
function TestIssueReport_HasPassed (line 181) | func TestIssueReport_HasPassed(t *testing.T) {
function TestIssueReport_Skip (line 195) | func TestIssueReport_Skip(t *testing.T) {
function TestIssueReport_HasBeenSkipped (line 209) | func TestIssueReport_HasBeenSkipped(t *testing.T) {
function TestIssueReport_IsInfoRiskSeverity (line 223) | func TestIssueReport_IsInfoRiskSeverity(t *testing.T) {
function TestIssueReport_IsLowRiskSeverity (line 232) | func TestIssueReport_IsLowRiskSeverity(t *testing.T) {
function TestIssueReport_IsMediumRiskSeverity (line 241) | func TestIssueReport_IsMediumRiskSeverity(t *testing.T) {
function TestIssueReport_IsHighRiskSeverity (line 250) | func TestIssueReport_IsHighRiskSeverity(t *testing.T) {
function TestIssueReport_IsCriticalRiskSeverity (line 259) | func TestIssueReport_IsCriticalRiskSeverity(t *testing.T) {
function TestIssueReport_WithScanAttempt (line 268) | func TestIssueReport_WithScanAttempt(t *testing.T) {
function TestIssueReport_AddScanAttempt (line 288) | func TestIssueReport_AddScanAttempt(t *testing.T) {
function TestIssueReport_String (line 308) | func TestIssueReport_String(t *testing.T) {
function TestIssueReport_SeverityLevelString (line 320) | func TestIssueReport_SeverityLevelString(t *testing.T) {
function TestIssueReport_Clone (line 346) | func TestIssueReport_Clone(t *testing.T) {
FILE: report/openapi_report.go
function findOperationByMethodAndPath (line 8) | func findOperationByMethodAndPath(operations operation.Operations, metho...
type OpenAPIReportOperation (line 18) | type OpenAPIReportOperation struct
function NewOpenAPIReportOperation (line 27) | func NewOpenAPIReportOperation(operation *openapi3.Operation, requestOpe...
type OpenAPIReportMethods (line 43) | type OpenAPIReportMethods
type OpenAPIReportPaths (line 44) | type OpenAPIReportPaths
type OpenAPIReport (line 45) | type OpenAPIReport struct
method AddReport (line 77) | func (or *OpenAPIReport) AddReport(r *ScanReport) {
function NewOpenAPIReport (line 49) | func NewOpenAPIReport(doc *openapi3.T, operations operation.Operations) ...
FILE: report/openapi_report_test.go
function TestNewOpenAPIReportOperation (line 12) | func TestNewOpenAPIReportOperation(t *testing.T) {
function TestNewOpenAPIReport (line 28) | func TestNewOpenAPIReport(t *testing.T) {
function Test_OpenAPIReport_AddReport (line 42) | func Test_OpenAPIReport_AddReport(t *testing.T) {
function Test_OpenAPIReport_AddReport_NoFailedIssue (line 65) | func Test_OpenAPIReport_AddReport_NoFailedIssue(t *testing.T) {
FILE: report/options_report.go
type OptionsReport (line 3) | type OptionsReport struct
function NewOptionsReport (line 5) | func NewOptionsReport() OptionsReport {
FILE: report/owasp.go
type OWASP (line 3) | type OWASP
constant OWASP_2023_BOLA (line 6) | OWASP_2023_BOLA OWASP = "API1:2023 Broken Obj...
constant OWASP_2023_BrokenAuthentication (line 7) | OWASP_2023_BrokenAuthentication OWASP = "API2:2023 Broken Aut...
constant OWASP_2023_BOPL (line 8) | OWASP_2023_BOPL OWASP = "API3:2023 Broken Obj...
constant OWASP_2023_UnrestrictedResourceConsumption (line 9) | OWASP_2023_UnrestrictedResourceConsumption OWASP = "API4:2023 Unrestrict...
constant OWASP_2023_BFLA (line 10) | OWASP_2023_BFLA OWASP = "API5:2023 Broken Fun...
constant OWASP_2023_UnrestrictedAccessBusiness (line 11) | OWASP_2023_UnrestrictedAccessBusiness OWASP = "API6:2023 Unrestrict...
constant OWASP_2023_SSRF (line 12) | OWASP_2023_SSRF OWASP = "API7:2023 Server Sid...
constant OWASP_2023_SecurityMisconfiguration (line 13) | OWASP_2023_SecurityMisconfiguration OWASP = "API8:2023 Security M...
constant OWASP_2023_ImproperInventory (line 14) | OWASP_2023_ImproperInventory OWASP = "API9:2023 Improper I...
constant OWASP_2023_UnsafeConsumption (line 15) | OWASP_2023_UnsafeConsumption OWASP = "API10:2023 Unsafe Co...
FILE: report/report.go
type OperationSecurityScheme (line 16) | type OperationSecurityScheme struct
function NewOperationSecurityScheme (line 25) | func NewOperationSecurityScheme(securityScheme *auth.SecurityScheme) Ope...
type ScanReportRequest (line 41) | type ScanReportRequest struct
type ScanReportResponse (line 50) | type ScanReportResponse struct
type ScanReportScan (line 57) | type ScanReportScan struct
type ScanReportOperation (line 64) | type ScanReportOperation struct
type ScanReport (line 68) | type ScanReport struct
method Start (line 123) | func (r *ScanReport) Start() *ScanReport {
method End (line 128) | func (r *ScanReport) End() *ScanReport {
method WithData (line 134) | func (r *ScanReport) WithData(data interface{}) *ScanReport {
method GetData (line 139) | func (r *ScanReport) GetData() interface{} {
method HasData (line 143) | func (r *ScanReport) HasData() bool {
method AddScanAttempt (line 147) | func (r *ScanReport) AddScanAttempt(attempt *scan.IssueScanAttempt) *S...
method GetScanAttempts (line 192) | func (r *ScanReport) GetScanAttempts() []ScanReportScan {
method AddIssueReport (line 196) | func (r *ScanReport) AddIssueReport(vr *IssueReport) *ScanReport {
method GetIssueReports (line 205) | func (r *ScanReport) GetIssueReports() []*IssueReport {
method GetErrors (line 209) | func (r *ScanReport) GetErrors() []error {
method GetFailedIssueReports (line 219) | func (r *ScanReport) GetFailedIssueReports() []*IssueReport {
method HasFailedIssueReport (line 229) | func (r *ScanReport) HasFailedIssueReport() bool {
constant otelName (line 87) | otelName = "github.com/cerberauth/vulnapi/report"
constant otelScanReportIdAttribute (line 89) | otelScanReportIdAttribute = attribute.Key("id")
function NewScanReport (line 92) | func NewScanReport(id string, name string, operation *operation.Operatio...
FILE: report/report_test.go
function TestNewOperationSecurityScheme (line 18) | func TestNewOperationSecurityScheme(t *testing.T) {
function TestNewScanReport (line 60) | func TestNewScanReport(t *testing.T) {
function TestScanReport_Start (line 69) | func TestScanReport_Start(t *testing.T) {
function TestScanReport_End (line 78) | func TestScanReport_End(t *testing.T) {
function TestScanReport_WithData (line 87) | func TestScanReport_WithData(t *testing.T) {
function TestScanReport_GetData (line 97) | func TestScanReport_GetData(t *testing.T) {
function TestScanReport_HasData (line 107) | func TestScanReport_HasData(t *testing.T) {
function TestScanReport_AddScanAttempt (line 119) | func TestScanReport_AddScanAttempt(t *testing.T) {
function TestScanReport_AddIssueReport (line 143) | func TestScanReport_AddIssueReport(t *testing.T) {
function TestScanReport_HasFailedIssueReport (line 152) | func TestScanReport_HasFailedIssueReport(t *testing.T) {
function TestScanReport_HasOnlyFailedIssueReport (line 165) | func TestScanReport_HasOnlyFailedIssueReport(t *testing.T) {
function TestScanReport_HasOnlyPassedIssueReport (line 178) | func TestScanReport_HasOnlyPassedIssueReport(t *testing.T) {
function TestScanReport_GetErrors (line 191) | func TestScanReport_GetErrors(t *testing.T) {
function TestMarshalJSON (line 203) | func TestMarshalJSON(t *testing.T) {
function TestScanReport_GetIssueReports (line 218) | func TestScanReport_GetIssueReports(t *testing.T) {
FILE: report/reporter.go
constant reporterSchema (line 11) | reporterSchema = "https://schemas.cerberauth.com/vulnapi/draft/2024-10/r...
type Reporter (line 13) | type Reporter struct
method AddReport (line 62) | func (rr *Reporter) AddReport(r *ScanReport) {
method GetScanReports (line 74) | func (rr *Reporter) GetScanReports() []*ScanReport {
method GetScanReportByID (line 78) | func (rr *Reporter) GetScanReportByID(id string) *ScanReport {
method GetReportsByIssueStatus (line 88) | func (rr *Reporter) GetReportsByIssueStatus(status IssueReportStatus) ...
method GetErrors (line 102) | func (rr *Reporter) GetErrors() []error {
method HasIssue (line 111) | func (rr *Reporter) HasIssue() bool {
method GetIssueReports (line 121) | func (rr *Reporter) GetIssueReports() []*IssueReport {
method GetFailedIssueReports (line 129) | func (rr *Reporter) GetFailedIssueReports() []*IssueReport {
method HasHighRiskOrHigherSeverityIssue (line 137) | func (rr *Reporter) HasHighRiskOrHigherSeverityIssue() bool {
method HasHigherThanSeverityThresholdIssue (line 147) | func (rr *Reporter) HasHigherThanSeverityThresholdIssue(threshold floa...
function NewReporter (line 23) | func NewReporter() *Reporter {
function NewReporterWithCurl (line 32) | func NewReporterWithCurl(method string, url string, data interface{}, he...
function NewReporterWithOpenAPIDoc (line 42) | func NewReporterWithOpenAPIDoc(openapi *openapi3.T, operations operation...
function NewReporterWithGraphQL (line 52) | func NewReporterWithGraphQL(url string, securitySchemes []*auth.Security...
FILE: report/reporter_test.go
function TestNewReporterWithCurl (line 15) | func TestNewReporterWithCurl(t *testing.T) {
function TestNewReporterWithCurl_AddReport (line 48) | func TestNewReporterWithCurl_AddReport(t *testing.T) {
function TestNewReporterWithOpenAPIDoc (line 76) | func TestNewReporterWithOpenAPIDoc(t *testing.T) {
function TestReporterWithOpenAPIDoc_AddReport (line 88) | func TestReporterWithOpenAPIDoc_AddReport(t *testing.T) {
function TestReporter_NoHasHighRiskOrHigherSeverityVulnerability_WhenNoReport (line 110) | func TestReporter_NoHasHighRiskOrHigherSeverityVulnerability_WhenNoRepor...
function TestReporter_NoHasVulnerability_WhenNoFailedReport (line 115) | func TestReporter_NoHasVulnerability_WhenNoFailedReport(t *testing.T) {
function TestReporter_HasVulnerability_WhenFailedReport (line 129) | func TestReporter_HasVulnerability_WhenFailedReport(t *testing.T) {
function TestReporters_HasHighRiskOrHigherSeverityVulnerability_WhenLowRiskReport (line 143) | func TestReporters_HasHighRiskOrHigherSeverityVulnerability_WhenLowRiskR...
function TestReporters_HasHighRiskOrHigherSeverityVulnerability_WhenHighRiskReport (line 160) | func TestReporters_HasHighRiskOrHigherSeverityVulnerability_WhenHighRisk...
function TestReporters_HasHighRiskOrHigherSeverityVulnerability_WhenCriticalRiskReport (line 177) | func TestReporters_HasHighRiskOrHigherSeverityVulnerability_WhenCritical...
function TestReporter_HasHigherThanSeverityThresholdIssue_WhenNoReports (line 194) | func TestReporter_HasHigherThanSeverityThresholdIssue_WhenNoReports(t *t...
function TestReporter_HasHigherThanSeverityThresholdIssue_WhenBelowThreshold (line 199) | func TestReporter_HasHigherThanSeverityThresholdIssue_WhenBelowThreshold...
function TestReporter_HasHigherThanSeverityThresholdIssue_WhenAtThreshold (line 216) | func TestReporter_HasHigherThanSeverityThresholdIssue_WhenAtThreshold(t ...
function TestReporter_HasHigherThanSeverityThresholdIssue_WhenAboveThreshold (line 233) | func TestReporter_HasHigherThanSeverityThresholdIssue_WhenAboveThreshold...
function TestReporter_GetReportsByIssueStatus_NoReports (line 250) | func TestReporter_GetReportsByIssueStatus_NoReports(t *testing.T) {
function TestReporter_GetReportsByIssueStatus_NoMatchingStatus (line 256) | func TestReporter_GetReportsByIssueStatus_NoMatchingStatus(t *testing.T) {
function TestReporter_GetReportsByIssueStatus_MatchingStatus (line 271) | func TestReporter_GetReportsByIssueStatus_MatchingStatus(t *testing.T) {
function TestReporter_GetReportsByIssueStatus_MultipleReports (line 288) | func TestReporter_GetReportsByIssueStatus_MultipleReports(t *testing.T) {
function TestReporter_GetIssueReports_NoReports (line 314) | func TestReporter_GetIssueReports_NoReports(t *testing.T) {
function TestReporter_GetIssueReports_SingleReport (line 320) | func TestReporter_GetIssueReports_SingleReport(t *testing.T) {
function TestReporter_GetIssueReports_MultipleReports (line 337) | func TestReporter_GetIssueReports_MultipleReports(t *testing.T) {
function TestReporter_GetScanReportByID_NoReports (line 363) | func TestReporter_GetScanReportByID_NoReports(t *testing.T) {
function TestReporter_GetScanReportByID_SingleReport (line 369) | func TestReporter_GetScanReportByID_SingleReport(t *testing.T) {
function TestReporter_GetScanReportByID_MultipleReports (line 380) | func TestReporter_GetScanReportByID_MultipleReports(t *testing.T) {
function TestReporter_GetScanReportByID_NonexistentID (line 397) | func TestReporter_GetScanReportByID_NonexistentID(t *testing.T) {
FILE: scan/broken_authentication/authentication_bypass/authentication_bypass.go
constant AcceptsUnauthenticatedOperationScanID (line 11) | AcceptsUnauthenticatedOperationScanID = "generic.accept_unauthenticate...
constant AcceptsUnauthenticatedOperationScanName (line 12) | AcceptsUnauthenticatedOperationScanName = "Accept Unauthenticated Operat...
function ScanHandler (line 30) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/broken_authentication/authentication_bypass/authentication_bypass_test.go
function TestAuthenticationByPassScanHandler_Skipped_WhenNoAuthSecurityScheme (line 16) | func TestAuthenticationByPassScanHandler_Skipped_WhenNoAuthSecuritySchem...
function TestAuthenticationByPassScanHandler_Failed_WhenAuthIsByPassed (line 26) | func TestAuthenticationByPassScanHandler_Failed_WhenAuthIsByPassed(t *te...
function TestAuthenticationByPassScanHandler_Passed_WhenAuthIsNotByPassed (line 42) | func TestAuthenticationByPassScanHandler_Passed_WhenAuthIsNotByPassed(t ...
FILE: scan/broken_authentication/jwt/alg_none/alg_none.go
type AlgNoneData (line 14) | type AlgNoneData struct
constant AlgNoneJwtScanID (line 19) | AlgNoneJwtScanID = "jwt.alg_none"
constant AlgNoneJwtScanName (line 20) | AlgNoneJwtScanName = "JWT None Algorithm"
function ShouldBeScanned (line 40) | func ShouldBeScanned(securityScheme *auth.SecurityScheme) bool {
function ScanHandler (line 51) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
function scanWithAlg (line 102) | func scanWithAlg(method jwtlib.SigningMethod, valueWriter *editor.TokenE...
FILE: scan/broken_authentication/jwt/alg_none/alg_none_test.go
function TestAlgNoneJwtScanHandler_WithoutSecurityScheme (line 16) | func TestAlgNoneJwtScanHandler_WithoutSecurityScheme(t *testing.T) {
function TestAlgNoneJwtScanHandler_Skipped_WhenNoJWT (line 31) | func TestAlgNoneJwtScanHandler_Skipped_WhenNoJWT(t *testing.T) {
function TestAlgNoneJwtScanHandler_Passed_WhenUnauthorizedResponse (line 48) | func TestAlgNoneJwtScanHandler_Passed_WhenUnauthorizedResponse(t *testin...
function TestAlgNoneJwtScanHandler_Failed_WhenValidValueUseNoneAlg (line 65) | func TestAlgNoneJwtScanHandler_Failed_WhenValidValueUseNoneAlg(t *testin...
function TestAlgNoneJwtScanHandler_Failed_WhenOKResponse (line 77) | func TestAlgNoneJwtScanHandler_Failed_WhenOKResponse(t *testing.T) {
function TestAlgNoneJwtScanHandler_Failed_WhenOKResponseAndAlgNone (line 97) | func TestAlgNoneJwtScanHandler_Failed_WhenOKResponseAndAlgNone(t *testin...
FILE: scan/broken_authentication/jwt/alg_none/methods.go
type signingMethodNone (line 3) | type signingMethodNone struct
method SetAlg (line 7) | func (m *signingMethodNone) SetAlg(alg string) {
method Alg (line 11) | func (m *signingMethodNone) Alg() string {
method Verify (line 14) | func (m *signingMethodNone) Verify(signingString string, sig []byte, k...
method Sign (line 17) | func (m *signingMethodNone) Sign(signingString string, key interface{}...
FILE: scan/broken_authentication/jwt/blank_secret/blank_secret.go
constant BlankSecretVulnerabilityScanID (line 12) | BlankSecretVulnerabilityScanID = "jwt.blank_secret"
constant BlankSecretVulnerabilityScanName (line 13) | BlankSecretVulnerabilityScanName = "JWT Blank Secret"
function ShouldBeScanned (line 33) | func ShouldBeScanned(securityScheme *auth.SecurityScheme) bool {
function ScanHandler (line 37) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/broken_authentication/jwt/blank_secret/blank_secret_test.go
function TestBlankSecretScanHandler_WithoutSecurityScheme (line 16) | func TestBlankSecretScanHandler_WithoutSecurityScheme(t *testing.T) {
function TestBlankSecretScanHandler_Skipped_WhenNoJWT (line 26) | func TestBlankSecretScanHandler_Skipped_WhenNoJWT(t *testing.T) {
function TestBlankSecretScanHandler_Passed_WhenUnauthorizedResponse (line 42) | func TestBlankSecretScanHandler_Passed_WhenUnauthorizedResponse(t *testi...
function TestBlankSecretScanHandler_Failed_WhenOKResponse (line 59) | func TestBlankSecretScanHandler_Failed_WhenOKResponse(t *testing.T) {
FILE: scan/broken_authentication/jwt/kid_injection/kid_injection.go
constant KidInjectionScanID (line 12) | KidInjectionScanID = "jwt.kid_injection"
constant KidInjectionScanName (line 13) | KidInjectionScanName = "JWT KID Injection"
type KidInjectionType (line 16) | type KidInjectionType
constant KidInjectionTypeSQLInjection (line 19) | KidInjectionTypeSQLInjection KidInjectionType = "sql_injection"
constant KidInjectionTypePathTraversal (line 20) | KidInjectionTypePathTraversal KidInjectionType = "path_traversal"
type KidInjectionData (line 23) | type KidInjectionData struct
function ShouldBeScanned (line 44) | func ShouldBeScanned(securityScheme *auth.SecurityScheme) bool {
function ScanHandler (line 48) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/broken_authentication/jwt/kid_injection/kid_injection_test.go
function TestKidInjectionScanHandler_WithoutSecurityScheme (line 16) | func TestKidInjectionScanHandler_WithoutSecurityScheme(t *testing.T) {
function TestKidInjectionScanHandler_Skipped_WhenNoJWT (line 26) | func TestKidInjectionScanHandler_Skipped_WhenNoJWT(t *testing.T) {
function TestKidInjectionScanHandler_Passed_WhenUnauthorizedResponse (line 42) | func TestKidInjectionScanHandler_Passed_WhenUnauthorizedResponse(t *test...
function TestKidInjectionScanHandler_Failed_WhenSQLInjectionOKResponse (line 59) | func TestKidInjectionScanHandler_Failed_WhenSQLInjectionOKResponse(t *te...
function TestKidInjectionScanHandler_Failed_WhenPathTraversalOKResponse (line 79) | func TestKidInjectionScanHandler_Failed_WhenPathTraversalOKResponse(t *t...
FILE: scan/broken_authentication/jwt/not_verified/not_verified.go
constant NotVerifiedJwtScanID (line 12) | NotVerifiedJwtScanID = "jwt.not_verified"
constant NotVerifiedJwtScanName (line 13) | NotVerifiedJwtScanName = "JWT Not Verified"
function ShouldBeScanned (line 32) | func ShouldBeScanned(securityScheme *auth.SecurityScheme) bool {
function ScanHandler (line 36) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/broken_authentication/jwt/not_verified/not_verified_test.go
function TestNotVerifiedScanHandler_WithoutSecurityScheme (line 16) | func TestNotVerifiedScanHandler_WithoutSecurityScheme(t *testing.T) {
function TestNotVerifiedScanHandler_Skipped_WhenNoJWTAndUnauthorizedResponse (line 26) | func TestNotVerifiedScanHandler_Skipped_WhenNoJWTAndUnauthorizedResponse...
function TestNotVerifiedScanHandler_Failed_WhenUnauthorizedThenOK (line 36) | func TestNotVerifiedScanHandler_Failed_WhenUnauthorizedThenOK(t *testing...
function TestNotVerifiedScanHandler_Skipped_WhenOKFirstRequest (line 58) | func TestNotVerifiedScanHandler_Skipped_WhenOKFirstRequest(t *testing.T) {
function TestNotVerifiedScanHandler_Failed_WhenUnauthorizedThenUnauthorized (line 80) | func TestNotVerifiedScanHandler_Failed_WhenUnauthorizedThenUnauthorized(...
FILE: scan/broken_authentication/jwt/null_signature/null_signature.go
constant NullSignatureScanID (line 12) | NullSignatureScanID = "jwt.null_signature"
constant NullSignatureScanName (line 13) | NullSignatureScanName = "JWT Null Signature"
function ShouldBeScanned (line 33) | func ShouldBeScanned(securityScheme *auth.SecurityScheme) bool {
function ScanHandler (line 37) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/broken_authentication/jwt/null_signature/null_signature_test.go
function TestNullSignatureScanHandler_WithoutSecurityScheme (line 16) | func TestNullSignatureScanHandler_WithoutSecurityScheme(t *testing.T) {
function TestNullSignatureScanHandler_Skipped_WhenNoJWT (line 26) | func TestNullSignatureScanHandler_Skipped_WhenNoJWT(t *testing.T) {
function TestNullSignatureScanHandler_Passed_WhenUnauthorizedResponse (line 42) | func TestNullSignatureScanHandler_Passed_WhenUnauthorizedResponse(t *tes...
function TestNullSignatureScanHandler_Failed_WhenOKResponse (line 59) | func TestNullSignatureScanHandler_Failed_WhenOKResponse(t *testing.T) {
FILE: scan/broken_authentication/jwt/weak_secret/weak_secret.go
type WeakSecretData (line 11) | type WeakSecretData struct
constant WeakSecretVulnerabilityScanID (line 16) | WeakSecretVulnerabilityScanID = "jwt.weak_secret"
constant WeakSecretVulnerabilityScanName (line 17) | WeakSecretVulnerabilityScanName = "JWT Weak Secret"
function ShouldBeScanned (line 37) | func ShouldBeScanned(securityScheme *auth.SecurityScheme) bool {
constant jwtSecretDictionarySeclistUrl (line 53) | jwtSecretDictionarySeclistUrl = "https://raw.githubusercontent.com/cerbe...
function ScanHandler (line 55) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
function bruteForceSecret (line 92) | func bruteForceSecret(currentToken string, jwtSecretDictionary []string,...
FILE: scan/broken_authentication/jwt/weak_secret/weak_secret_test.go
function TestWeakHMACSecretScanHandler_WithoutSecurityScheme (line 14) | func TestWeakHMACSecretScanHandler_WithoutSecurityScheme(t *testing.T) {
function TestWeakHMACSecretScanHandler_WithJWTUsingOtherAlg (line 24) | func TestWeakHMACSecretScanHandler_WithJWTUsingOtherAlg(t *testing.T) {
function TestWeakHMACSecretScanHandler_WithoutJWT (line 35) | func TestWeakHMACSecretScanHandler_WithoutJWT(t *testing.T) {
function TestWeakHMACSecretScanHandler_Failed_WithWeakJWT (line 45) | func TestWeakHMACSecretScanHandler_Failed_WithWeakJWT(t *testing.T) {
function TestWeakHMACSecretScanHandler_Failed_WithExpiredJWTSignedWithWeakSecret (line 59) | func TestWeakHMACSecretScanHandler_Failed_WithExpiredJWTSignedWithWeakSe...
function TestWeakHMACSecretScanHandler_Passed_WithStrongerJWT (line 73) | func TestWeakHMACSecretScanHandler_Passed_WithStrongerJWT(t *testing.T) {
function TestWeakHMACSecretScanHandler_Failed_WithUnorderedClaims (line 85) | func TestWeakHMACSecretScanHandler_Failed_WithUnorderedClaims(t *testing...
FILE: scan/discover/accept_unauthenticated/accept_unauthenticated_operation.go
constant NoAuthOperationScanID (line 10) | NoAuthOperationScanID = "discover.accept_unauthenticated"
constant NoAuthOperationScanName (line 11) | NoAuthOperationScanName = "Accept Unauthenticated Operation"
function ScanHandler (line 29) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/discover/accept_unauthenticated/accept_unauthenticated_operation_test.go
function TestAcceptUnauthenticatedScanHandler_Failed_WhenNoAuthSecurityScheme (line 14) | func TestAcceptUnauthenticatedScanHandler_Failed_WhenNoAuthSecuritySchem...
function TestCheckNoAuthOperationScanHandler_Passed_WhenAuthConfigured (line 24) | func TestCheckNoAuthOperationScanHandler_Passed_WhenAuthConfigured(t *te...
FILE: scan/discover/discoverable_graphql/discoverable_graphql.go
constant DiscoverableGraphQLPathScanID (line 11) | DiscoverableGraphQLPathScanID = "discover.graphql"
constant DiscoverableGraphQLPathScanName (line 12) | DiscoverableGraphQLPathScanName = "Discoverable GraphQL Path"
function ScanHandler (line 34) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/discover/discoverable_graphql/discoverable_graphql_test.go
function TestDiscoverableScanner_Passed_WhenNoDiscoverableGraphqlPathFound (line 16) | func TestDiscoverableScanner_Passed_WhenNoDiscoverableGraphqlPathFound(t...
function TestDiscoverableScanner_Failed_WhenOneGraphQLPathFound (line 34) | func TestDiscoverableScanner_Failed_WhenOneGraphQLPathFound(t *testing.T) {
FILE: scan/discover/discoverable_openapi/discoverable_openapi.go
constant DiscoverableOpenAPIScanID (line 11) | DiscoverableOpenAPIScanID = "discover.discoverable_openapi"
constant DiscoverableOpenAPIScanName (line 12) | DiscoverableOpenAPIScanName = "Discoverable OpenAPI"
function ScanHandler (line 34) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/discover/discoverable_openapi/discoverable_openapi_test.go
function TestDiscoverableScanner_Passed_WhenNoDiscoverableGraphqlPathFound (line 16) | func TestDiscoverableScanner_Passed_WhenNoDiscoverableGraphqlPathFound(t...
function TestDiscoverableScanner_Failed_WhenOneOpenAPIFound (line 34) | func TestDiscoverableScanner_Failed_WhenOneOpenAPIFound(t *testing.T) {
FILE: scan/discover/exposed_files/exposed_files.go
constant DiscoverableFilesScanID (line 11) | DiscoverableFilesScanID = "discover.exposed_files"
constant DiscoverableFilesScanName (line 12) | DiscoverableFilesScanName = "Discoverable exposed files"
function ScanHandler (line 34) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/discover/exposed_files/exposed_files_test.go
function TestDiscoverableScanner_Passed_WhenNoDiscoverableGraphqlPathFound (line 16) | func TestDiscoverableScanner_Passed_WhenNoDiscoverableGraphqlPathFound(t...
function TestDiscoverableScanner_Failed_WhenOneGraphQLPathFound (line 34) | func TestDiscoverableScanner_Failed_WhenOneGraphQLPathFound(t *testing.T) {
FILE: scan/discover/fingerprint/fingerprint.go
constant DiscoverFingerPrintScanID (line 12) | DiscoverFingerPrintScanID = "discover.server_signature"
constant DiscoverFingerPrintScanName (line 13) | DiscoverFingerPrintScanName = "Server Signature Discovery"
type FingerPrintApp (line 16) | type FingerPrintApp struct
type FingerPrintData (line 21) | type FingerPrintData struct
function appendIfMissing (line 52) | func appendIfMissing(slice []FingerPrintApp, app FingerPrintApp) []Finge...
function ScanHandler (line 61) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/discover/fingerprint/fingerprint_test.go
function TestCheckSignatureHeader_Failed_WithServerSignatureHeader (line 16) | func TestCheckSignatureHeader_Failed_WithServerSignatureHeader(t *testin...
function TestCheckSignatureHeader_Failed_WithOSSignatureHeader (line 37) | func TestCheckSignatureHeader_Failed_WithOSSignatureHeader(t *testing.T) {
function TestCheckSignatureHeader_Failed_WithHostingSignatureHeader (line 58) | func TestCheckSignatureHeader_Failed_WithHostingSignatureHeader(t *testi...
function TestCheckSignatureHeader_Failed_WithAuthenticationSignatureHeader (line 79) | func TestCheckSignatureHeader_Failed_WithAuthenticationSignatureHeader(t...
function TestCheckSignatureHeader_Failed_WithCDNSignatureHeader (line 100) | func TestCheckSignatureHeader_Failed_WithCDNSignatureHeader(t *testing.T) {
function TestCheckSignatureHeader_Failed_WithLanguageSignatureHeader (line 121) | func TestCheckSignatureHeader_Failed_WithLanguageSignatureHeader(t *test...
function TestCheckSignatureHeader_Failed_WithFrameworkSignatureHeader (line 142) | func TestCheckSignatureHeader_Failed_WithFrameworkSignatureHeader(t *tes...
function TestCheckSignatureHeader_Passed_WithoutDuplicate (line 165) | func TestCheckSignatureHeader_Passed_WithoutDuplicate(t *testing.T) {
function TestCheckSignatureHeader_Passed_WithoutSignatureHeader (line 184) | func TestCheckSignatureHeader_Passed_WithoutSignatureHeader(t *testing.T) {
FILE: scan/discover/healthcheck/healthcheck.go
constant DiscoverableHealthCheckScanID (line 11) | DiscoverableHealthCheckScanID = "discover.healthcheck"
constant DiscoverableHealthCheckScanName (line 12) | DiscoverableHealthCheckScanName = "Discoverable healthcheck endpoint"
function ScanHandler (line 32) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/discover/healthcheck/healthcheck_test.go
function TestDiscoverableScanner_Passed_WhenNoDiscoverableHealthCheckEndpointFound (line 16) | func TestDiscoverableScanner_Passed_WhenNoDiscoverableHealthCheckEndpoin...
function TestDiscoverableScanner_Failed_WhenOneHealthCheckEndpointFound (line 34) | func TestDiscoverableScanner_Failed_WhenOneHealthCheckEndpointFound(t *t...
FILE: scan/discover/utils.go
type DiscoverData (line 15) | type DiscoverData
function ExtractBaseURL (line 19) | func ExtractBaseURL(inputURL *url.URL) *url.URL {
function ScanURLs (line 26) | func ScanURLs(scanUrls []string, op *operation.Operation, securityScheme...
function DownloadAndScanURLs (line 92) | func DownloadAndScanURLs(name string, seclistUrl string, r *report.ScanR...
FILE: scan/discover/utils_test.go
function TestExtractBaseURL (line 17) | func TestExtractBaseURL(t *testing.T) {
function TestDownloadAndScanURLs_Failed_WhenNotFoundSeclist (line 45) | func TestDownloadAndScanURLs_Failed_WhenNotFoundSeclist(t *testing.T) {
function TestDownloadAndScanURLs_Passed_WhenNotFoundURLs (line 65) | func TestDownloadAndScanURLs_Passed_WhenNotFoundURLs(t *testing.T) {
function TestDownloadAndScanURLs_Failed_WhenFoundExposedURLs (line 97) | func TestDownloadAndScanURLs_Failed_WhenFoundExposedURLs(t *testing.T) {
FILE: scan/discover/well-known/well_known.go
constant DiscoverableWellKnownScanID (line 11) | DiscoverableWellKnownScanID = "discover.well-known"
constant DiscoverableWellKnownScanName (line 12) | DiscoverableWellKnownScanName = "Discoverable well-known path"
function ScanHandler (line 32) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/discover/well-known/well_known_test.go
function TestDiscoverableScanner_Passed_WhenNoDiscoverableWellKnownPathFound (line 16) | func TestDiscoverableScanner_Passed_WhenNoDiscoverableWellKnownPathFound...
function TestDiscoverableScanner_Failed_WhenOneWellKnownPathFound (line 34) | func TestDiscoverableScanner_Failed_WhenOneWellKnownPathFound(t *testing...
FILE: scan/graphql/introspection_enabled/introspection_enabled.go
constant GraphqlIntrospectionScanID (line 16) | GraphqlIntrospectionScanID = "graphql.introspection_enabled"
constant GraphqlIntrospectionScanName (line 17) | GraphqlIntrospectionScanName = "GraphQL Introspection Enabled"
constant graphqlQuery (line 36) | graphqlQuery = `query{__schema{queryType{name}}}`
function newPostGraphqlIntrospectionRequest (line 38) | func newPostGraphqlIntrospectionRequest(client *request.Client, endpoint...
function newGetGraphqlIntrospectionRequest (line 49) | func newGetGraphqlIntrospectionRequest(client *request.Client, endpoint ...
function ScanHandler (line 63) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/graphql/introspection_enabled/introspection_enabled_test.go
function TestGraphqlIntrospectionScanHandler_Failed_WhenRespondHTTPStatusIsOK (line 16) | func TestGraphqlIntrospectionScanHandler_Failed_WhenRespondHTTPStatusIsO...
function TestGraphqlIntrospectionScanHandler_Failed_WhenRespond_GETMethodOnly_HTTPStatusIsOK (line 33) | func TestGraphqlIntrospectionScanHandler_Failed_WhenRespond_GETMethodOnl...
function TestGraphqlIntrospectionScanHandler_Passed_WhenBadRequestStatus (line 50) | func TestGraphqlIntrospectionScanHandler_Passed_WhenBadRequestStatus(t *...
function TestGraphqlIntrospectionScanHandler_Passed_WhenOKStatusButNoQuery (line 66) | func TestGraphqlIntrospectionScanHandler_Passed_WhenOKStatusButNoQuery(t...
FILE: scan/misconfiguration/http_cookies/http_cookies.go
constant HTTPCookiesScanID (line 13) | HTTPCookiesScanID = "misconfiguration.http_cookies"
constant HTTPCookiesScanName (line 14) | HTTPCookiesScanName = "HTTP Cookies Misconfiguration"
function ScanHandler (line 106) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/misconfiguration/http_cookies/http_cookies_test.go
function TestHTTPCookiesScanHandler_Skipped_WhenNoCookies (line 17) | func TestHTTPCookiesScanHandler_Skipped_WhenNoCookies(t *testing.T) {
function TestHTTPCookiesScanHandler_Passed_WhenNoUnsecurePractices (line 35) | func TestHTTPCookiesScanHandler_Passed_WhenNoUnsecurePractices(t *testin...
function TestHTTPCookiesScanHandler_Failed_WhenNotHttpOnly (line 64) | func TestHTTPCookiesScanHandler_Failed_WhenNotHttpOnly(t *testing.T) {
function TestHTTPCookiesScanHandlerFailed_WhenNotSecure (line 93) | func TestHTTPCookiesScanHandlerFailed_WhenNotSecure(t *testing.T) {
function TestHTTPCookiesScanHandler_Failed_WhenSameSiteNone (line 122) | func TestHTTPCookiesScanHandler_Failed_WhenSameSiteNone(t *testing.T) {
function TestHTTPCookiesScanHandler_Failed_WhithoutSameSite (line 151) | func TestHTTPCookiesScanHandler_Failed_WhithoutSameSite(t *testing.T) {
function TestHTTPCookiesScanHandler_Failed_WhenExpiresNotSet (line 179) | func TestHTTPCookiesScanHandler_Failed_WhenExpiresNotSet(t *testing.T) {
FILE: scan/misconfiguration/http_headers/http_headers.go
constant CSPHTTPHeader (line 13) | CSPHTTPHeader = "Content-Security-Policy"
constant HSTSHTTPHeader (line 14) | HSTSHTTPHeader = "Strict-Transport-Security"
constant CORSOriginHTTPHeader (line 15) | CORSOriginHTTPHeader = "Access-Control-Allow-Origin"
constant XContentTypeOptionsHTTPHeader (line 16) | XContentTypeOptionsHTTPHeader = "X-Content-Type-Options"
constant XFrameOptionsHTTPHeader (line 17) | XFrameOptionsHTTPHeader = "X-Frame-Options"
constant HTTPHeadersScanID (line 21) | HTTPHeadersScanID = "misconfiguration.http_headers"
constant HTTPHeadersScanName (line 22) | HTTPHeadersScanName = "HTTP Headers Misconfiguration"
function CheckCSPFrameAncestors (line 144) | func CheckCSPFrameAncestors(cspHeader string) bool {
function ScanHandler (line 159) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/misconfiguration/http_headers/http_headers_test.go
function getValidHTTPHeaders (line 16) | func getValidHTTPHeaders(_ *operation.Operation) http.Header {
function TestHTTPHeadersScanHandler_Passed (line 27) | func TestHTTPHeadersScanHandler_Passed(t *testing.T) {
function TestHTTPHeadersBestPracticesWithoutCSPScanHandler (line 51) | func TestHTTPHeadersBestPracticesWithoutCSPScanHandler(t *testing.T) {
function TestHTTPHeadersBestPracticesWithoutFrameAncestorsCSPDirectiveScanHandler (line 70) | func TestHTTPHeadersBestPracticesWithoutFrameAncestorsCSPDirectiveScanHa...
function TestHTTPHeadersBestPracticesWithNotNoneFrameAncestorsCSPDirectiveScanHandler (line 89) | func TestHTTPHeadersBestPracticesWithNotNoneFrameAncestorsCSPDirectiveSc...
function TestHTTPHeadersBestPracticesWithoutCORSScanHandler (line 108) | func TestHTTPHeadersBestPracticesWithoutCORSScanHandler(t *testing.T) {
function TestHTTPHeadersBestPracticesWithPermissiveCORSScanHandler (line 127) | func TestHTTPHeadersBestPracticesWithPermissiveCORSScanHandler(t *testin...
function TestHTTPHeadersBestPracticesWithoutHSTSScanHandler (line 146) | func TestHTTPHeadersBestPracticesWithoutHSTSScanHandler(t *testing.T) {
function TestHTTPHeadersBestPracticesWithoutXContentTypeOptionsScanHandler (line 165) | func TestHTTPHeadersBestPracticesWithoutXContentTypeOptionsScanHandler(t...
function TestHTTPHeadersBestPracticesWithoutXFrameOptionsScanHandler (line 184) | func TestHTTPHeadersBestPracticesWithoutXFrameOptionsScanHandler(t *test...
FILE: scan/misconfiguration/http_method_override/http_method_override.go
constant HTTPMethodOverrideScanID (line 13) | HTTPMethodOverrideScanID = "misconfiguration.http_method_override"
constant HTTPMethodOverrideScanName (line 14) | HTTPMethodOverrideScanName = "HTTP Method Override Misconfiguration"
function ScanHandler (line 75) | func ScanHandler(op *operation.Operation, securityScheme *auth.SecurityS...
FILE: scan/misconfiguration/http_method_override/http_method_override_test.go
function TestHTTPMethodOverrideScanHandler (line 19) | func TestHTTPMethodOverrideScanHandler(t *testing.T) {
function TestHTTPMethodOverrideScanHandler_When_Error (line 58) | func TestHTTPMethodOverrideScanHandler_When_Error(t *testing.T) {
function TestHTTPMethodOverrideScanHandler_Passed (line 77) | func TestHTTPMethodOverrideScanHandler_Passed(t *testing.T) {
function TestHTTPMethodOverrideScanHandler_Failed_With_Header (line 98) | func TestHTTPMethodOverrideScanHandler_Failed_With_Header(t *testing.T) {
function TestHTTPMethodOverrideScanHandler_Failed_With_Query_Parameter (line 124) | func TestHTTPMethodOverrideScanHandler_Failed_With_Query_Parameter(t *te...
function TestHTTPMethodOverrideScanHandler_Authentication_ByPass_Passed (line 151) | func TestHTTPMethodOverrideScanHandler_Authentication_ByPass_Passed(t *t...
function TestHTTPMethodOverrideScanHandler_Authentication_ByPass_Failed (line 182) | func TestHTTPMethodOverrideScanHandler_Authentication_ByPass_Failed(t *t...
FILE: scan/misconfiguration/http_trace/http_trace_method.go
constant HTTPTraceScanID (line 13) | HTTPTraceScanID = "misconfiguration.http_trace"
constant HTTPTraceScanName (line 14) | HTTPTraceScanName = "HTTP TRACE Method Misconfiguration"
function ScanHandler (line 34) | func ScanHandler(operation *operation.Operation, securityScheme *auth.Se...
FILE: scan/misconfiguration/http_trace/http_trace_method_test.go
function TestHTTPTraceMethodScanHandler_Passed_WhenNotOKResponse (line 16) | func TestHTTPTraceMethodScanHandler_Passed_WhenNotOKResponse(t *testing....
function TestHTTPTraceMethodScanHandler_Failed_WhenTraceIsEnabled (line 31) | func TestHTTPTraceMethodScanHandler_Failed_WhenTraceIsEnabled(t *testing...
FILE: scan/misconfiguration/http_track/http_track_method.go
constant HTTPTrackScanID (line 13) | HTTPTrackScanID = "misconfiguration.http_track"
constant HTTPTrackScanName (line 14) | HTTPTrackScanName = "HTTP TRACK Method Misconfiguration"
constant TrackMethod (line 34) | TrackMethod = "TRACK"
function ScanHandler (line 36) | func ScanHandler(operation *operation.Operation, securityScheme *auth.Se...
FILE: scan/misconfiguration/http_track/http_track_method_test.go
function TestHTTPTrackMethodScanHandler_Passed_WhenNotOKResponse (line 16) | func TestHTTPTrackMethodScanHandler_Passed_WhenNotOKResponse(t *testing....
function TestHTTPTrackMethodScanHandler_Failed_WhenTrackIsEnabled (line 31) | func TestHTTPTrackMethodScanHandler_Failed_WhenTrackIsEnabled(t *testing...
FILE: scan/operation_scan.go
type OperationScanHandlerFunc (line 9) | type OperationScanHandlerFunc
type OperationScanHandler (line 11) | type OperationScanHandler struct
type OperationScan (line 16) | type OperationScan struct
function NewOperationScanHandler (line 21) | func NewOperationScanHandler(id string, handler OperationScanHandlerFunc...
FILE: scan/operation_scan_test.go
function TestNewOperationScanHandler (line 13) | func TestNewOperationScanHandler(t *testing.T) {
FILE: scan/scan.go
type ScanOptions (line 15) | type ScanOptions struct
type Scan (line 21) | type Scan struct
method GetOperationsScans (line 72) | func (s *Scan) GetOperationsScans() []OperationScan {
method AddOperationScanHandler (line 76) | func (s *Scan) AddOperationScanHandler(handler *OperationScanHandler) ...
method AddScanHandler (line 95) | func (s *Scan) AddScanHandler(handler *OperationScanHandler) *Scan {
method Execute (line 112) | func (s *Scan) Execute(ctx context.Context, scanCallback func(operatio...
method shouldAddScan (line 139) | func (s *Scan) shouldAddScan(scanID string) bool {
constant otelName (line 32) | otelName = "github.com/cerberauth/vulnapi/scan"
constant otelScanIncludeScansAttribute (line 34) | otelScanIncludeScansAttribute = attribute.Key("include_scans")
constant otelScanExcludeScansAttribute (line 35) | otelScanExcludeScansAttribute = attribute.Key("exclude_scans")
constant otelScanHandlerIdAttribute (line 36) | otelScanHandlerIdAttribute = attribute.Key("id")
function NewScan (line 39) | func NewScan(operations operation.Operations, opts *ScanOptions) (*Scan,...
function contains (line 153) | func contains(slice []string, item string) bool {
FILE: scan/scan_test.go
function TestNewScanWithNoOperations (line 16) | func TestNewScanWithNoOperations(t *testing.T) {
function TestNewScan (line 22) | func TestNewScan(t *testing.T) {
function TestNewScanWithOptions (line 42) | func TestNewScanWithOptions(t *testing.T) {
function TestScanGetOperationsScansWhenEmpty (line 63) | func TestScanGetOperationsScansWhenEmpty(t *testing.T) {
function TestScanGetOperationsScans (line 73) | func TestScanGetOperationsScans(t *testing.T) {
function TestScanExecuteWithNoHandlers (line 86) | func TestScanExecuteWithNoHandlers(t *testing.T) {
function TestScanExecuteWithHandler (line 98) | func TestScanExecuteWithHandler(t *testing.T) {
function TestScanExecuteWithIncludeScans (line 115) | func TestScanExecuteWithIncludeScans(t *testing.T) {
function TestScanExecuteWithEmptyStringIncludeScans (line 134) | func TestScanExecuteWithEmptyStringIncludeScans(t *testing.T) {
function TestScanExecuteWithMatchStringIncludeScans (line 153) | func TestScanExecuteWithMatchStringIncludeScans(t *testing.T) {
function TestScanExecuteWithWrongMatchStringIncludeScans (line 172) | func TestScanExecuteWithWrongMatchStringIncludeScans(t *testing.T) {
function TestScanExecuteWithExcludeScans (line 190) | func TestScanExecuteWithExcludeScans(t *testing.T) {
function TestScanExecuteWithMatchStringExcludeScans (line 208) | func TestScanExecuteWithMatchStringExcludeScans(t *testing.T) {
function TestScanExecuteWithWrongMatchStringExcludeScans (line 226) | func TestScanExecuteWithWrongMatchStringExcludeScans(t *testing.T) {
FILE: scenario/discover_api.go
function NewDiscoverAPIScan (line 18) | func NewDiscoverAPIScan(method string, u *url.URL, client *request.Clien...
FILE: scenario/discover_api_test.go
function TestNewDiscoverScan (line 14) | func TestNewDiscoverScan(t *testing.T) {
function TestNewDiscoverScanWithoutURLProto (line 28) | func TestNewDiscoverScanWithoutURLProto(t *testing.T) {
function TestNewDiscoverScanWhenNotReachable (line 43) | func TestNewDiscoverScanWhenNotReachable(t *testing.T) {
FILE: scenario/discover_domain.go
function searchByCommonHostnames (line 19) | func searchByCommonHostnames(domain string) []string {
function searchByLookupIP (line 44) | func searchByLookupIP(rootDomain string) ([]string, error) {
function getAllFQDNs (line 80) | func getAllFQDNs(domain string) []string {
function testFqdnReachable (line 98) | func testFqdnReachable(fqdn string, client *request.Client) (*operation....
function NewDiscoverDomainsScan (line 120) | func NewDiscoverDomainsScan(rootDomain string, client *request.Client, o...
FILE: scenario/graphql.go
function NewGraphQLScan (line 16) | func NewGraphQLScan(u *url.URL, client *request.Client, opts *scan.ScanO...
FILE: scenario/graphql_test.go
function TestNewGraphQLScan (line 16) | func TestNewGraphQLScan(t *testing.T) {
function TestNewGraphQLScanWithoutURLProto (line 31) | func TestNewGraphQLScanWithoutURLProto(t *testing.T) {
function TestNewGraphQLScanWhenNotReachable (line 47) | func TestNewGraphQLScanWhenNotReachable(t *testing.T) {
function TestNewGraphQLScanWithUpperCaseAuthorizationHeader (line 56) | func TestNewGraphQLScanWithUpperCaseAuthorizationHeader(t *testing.T) {
function TestNewGraphQLScanWithUpperCaseAuthorizationAndLowerCaseBearerHeader (line 78) | func TestNewGraphQLScanWithUpperCaseAuthorizationAndLowerCaseBearerHeade...
function TestNewGraphQLScanWithLowerCaseAuthorizationHeader (line 98) | func TestNewGraphQLScanWithLowerCaseAuthorizationHeader(t *testing.T) {
FILE: scenario/openapi.go
function NewOpenAPIScan (line 12) | func NewOpenAPIScan(ctx context.Context, openapi *openapi.OpenAPI, secur...
FILE: scenario/openapi_test.go
function TestMain (line 20) | func TestMain(m *testing.M) {
function TestNewOpenAPIScanWithHttpBearer (line 45) | func TestNewOpenAPIScanWithHttpBearer(t *testing.T) {
function TestNewOpenAPIScanWithJWTHttpBearer (line 61) | func TestNewOpenAPIScanWithJWTHttpBearer(t *testing.T) {
function TestNewOpenAPIScanWithMultipleOperations (line 78) | func TestNewOpenAPIScanWithMultipleOperations(t *testing.T) {
function TestNewOpenAPIScanWithoutParamsExample (line 97) | func TestNewOpenAPIScanWithoutParamsExample(t *testing.T) {
FILE: scenario/scans.go
function WithAllCommonScans (line 21) | func WithAllCommonScans(s *scan.Scan) *scan.Scan {
FILE: scenario/url.go
function NewURLScan (line 15) | func NewURLScan(method string, u *url.URL, data string, client *request....
FILE: scenario/url_test.go
function TestNewURLScan (line 16) | func TestNewURLScan(t *testing.T) {
function TestNewURLScanWithUpperCaseAuthorizationHeader (line 31) | func TestNewURLScanWithUpperCaseAuthorizationHeader(t *testing.T) {
function TestNewURLScanWithUpperCaseAuthorizationAndLowerCaseBearerHeader (line 53) | func TestNewURLScanWithUpperCaseAuthorizationAndLowerCaseBearerHeader(t ...
function TestNewURLScanWithLowerCaseAuthorizationHeader (line 73) | func TestNewURLScanWithLowerCaseAuthorizationHeader(t *testing.T) {
function TestNewURLScanWithAPIKeyInHeader (line 93) | func TestNewURLScanWithAPIKeyInHeader(t *testing.T) {
function TestNewURLScanWithHTTPBasic (line 139) | func TestNewURLScanWithHTTPBasic(t *testing.T) {
FILE: scenario/utils.go
constant bearerPrefix (line 13) | bearerPrefix = auth.BearerPrefix + " "
constant basicPrefix (line 14) | basicPrefix = auth.BasicPrefix + " "
function detectAuthorizationHeader (line 18) | func detectAuthorizationHeader(header http.Header) string {
function getBearerToken (line 30) | func getBearerToken(authHeader string) string {
function getHttpBasicAuthUser (line 43) | func getHttpBasicAuthUser(authHeader string) string {
function detectAPIKeyHeader (line 56) | func detectAPIKeyHeader(header http.Header) (string, string) {
function decodeBasicAuth (line 68) | func decodeBasicAuth(encodedUser string) (string, string) {
function detectSecurityScheme (line 82) | func detectSecurityScheme(header http.Header) (*auth.SecurityScheme, err...
function addDefaultProtocolWhenMissing (line 102) | func addDefaultProtocolWhenMissing(u *url.URL) *url.URL {
FILE: seclist/seclist.go
type SecList (line 19) | type SecList struct
method loadFile (line 54) | func (s *SecList) loadFile(file fs.File) error {
method loadFromEmbeddedFile (line 68) | func (s *SecList) loadFromEmbeddedFile(filepath string) error {
method loadFromTmpFile (line 86) | func (s *SecList) loadFromTmpFile(filepath string) error {
method DownloadFromURL (line 96) | func (s *SecList) DownloadFromURL(url string) error {
function fileNameFromURL (line 24) | func fileNameFromURL(url string) string {
function hasSecList (line 28) | func hasSecList(name string) bool {
function NewSecList (line 33) | func NewSecList(name string) *SecList {
function NewSecListFromURL (line 40) | func NewSecListFromURL(name, url string) (*SecList, error) {
function NewSecListFromEmbeddedFile (line 77) | func NewSecListFromEmbeddedFile(name, filename string) (*SecList, error) {
FILE: seclist/seclist_test.go
function TestNewSecListFromURL (line 12) | func TestNewSecListFromURL(t *testing.T) {
function TestNewSecListFromURLWhenResponseNotOk (line 29) | func TestNewSecListFromURLWhenResponseNotOk(t *testing.T) {
function TestNewSecListFromURLWhenEmbeddedFileExists (line 40) | func TestNewSecListFromURLWhenEmbeddedFileExists(t *testing.T) {
FILE: vulnapi.rb
class Vulnapi (line 5) | class Vulnapi < Formula
method install (line 16) | def install
method install (line 24) | def install
method install (line 34) | def install
method install (line 41) | def install
Condensed preview — 234 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,046K chars).
[
{
"path": ".cobra.yaml",
"chars": 72,
"preview": "author: Emmanuel Gautier <emmanuel@cerberauth.com>\nlicense: MIT License\n"
},
{
"path": ".docker/Dockerfile-build",
"chars": 405,
"preview": "FROM golang:1.26-bookworm AS builder\n\nWORKDIR /go/src/github.com/cerberauth/vulnapi\n\nCOPY go.mod go.mod\nCOPY go.sum go.s"
},
{
"path": ".docker/Dockerfile-goreleaser",
"chars": 241,
"preview": "FROM gcr.io/distroless/static-debian12:nonroot\n\nARG TARGETOS\nARG TARGETARCH\nARG TARGETVARIANT\n\nCOPY --chown=nonroot:nonr"
},
{
"path": ".github/CODEOWNERS",
"chars": 37,
"preview": "* @cerberauth @emmanuelgautier\n"
},
{
"path": ".github/FUNDING.yml",
"chars": 59,
"preview": "github: [emmanuelgautier]\nbuy_me_a_coffee: emmanuelgautier\n"
},
{
"path": ".github/workflows/ci.yml",
"chars": 1268,
"preview": "name: CI\n\non:\n push:\n tags:\n - \"v*.*.*\"\n branches:\n - main\n pull_request:\n branches:\n - main\n\n"
},
{
"path": ".github/workflows/scans.yml",
"chars": 13832,
"preview": "name: Scans\n\non:\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n\nenv:\n GO_VERSION: \"1.26"
},
{
"path": ".github/workflows/stale.yml",
"chars": 251,
"preview": "name: \"Close Stale Issues\"\n\non:\n workflow_dispatch: {}\n schedule:\n - cron: \"0 0 * * *\"\n\njobs:\n stale:\n uses: ce"
},
{
"path": ".gitignore",
"chars": 519,
"preview": "# If you prefer the allow list template instead of the deny list, see community template:\n# https://github.com/github/gi"
},
{
"path": ".golangci.yml",
"chars": 533,
"preview": "version: \"2\"\nlinters:\n enable:\n - goconst\n - gocritic\n - gosec\n settings:\n gosec:\n excludes:\n "
},
{
"path": ".goreleaser.yaml",
"chars": 6013,
"preview": "# yaml-language-server: $schema=https://goreleaser.com/static/schema.json\n# vim: set ts=2 sw=2 tw=0 fo=jcroql\nversion: 2"
},
{
"path": ".vscode/launch.json",
"chars": 492,
"preview": "{\n // Use IntelliSense to learn about possible attributes.\n // Hover to view descriptions of existing attributes.\n"
},
{
"path": ".whitesource",
"chars": 275,
"preview": "{\n \"scanSettings\": {\n \"baseBranches\": []\n },\n \"checkRunSettings\": {\n \"vulnerableCheckRunConclusionLevel\": \"fail"
},
{
"path": "LICENSE",
"chars": 1067,
"preview": "MIT License\n\nCopyright (c) 2023 CerberAuth\n\nPermission is hereby granted, free of charge, to any person obtaining a copy"
},
{
"path": "README.md",
"chars": 10633,
"preview": "<p align=\"center\">\n <img src=\"https://vulnapi.cerberauth.com/logo-ascii-text-art.png\" height=\"150\" alt=\"vulnapi logo\""
},
{
"path": "api/curl.go",
"chars": 1425,
"preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n\t\"github.com/cerberauth/"
},
{
"path": "api/graphql.go",
"chars": 1339,
"preview": "package api\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n\t\"github.com/cerberauth/"
},
{
"path": "api/handler.go",
"chars": 315,
"preview": "package api\n\nimport (\n\t\"github.com/gin-gonic/gin\"\n)\n\ntype Handler struct{}\n\nfunc NewHandler() *Handler {\n\treturn &Handle"
},
{
"path": "api/openapi.go",
"chars": 2064,
"preview": "package api\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n\t\"github.com/cerbe"
},
{
"path": "api/request.go",
"chars": 602,
"preview": "package api\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n)\n\ntype ScanOptions struct {\n\tRateLi"
},
{
"path": "api/response.go",
"chars": 148,
"preview": "package api\n\nimport (\n\t\"github.com/cerberauth/vulnapi/report\"\n)\n\ntype HTTPResponseReports struct {\n\tReports []*report.Sc"
},
{
"path": "api/response_test.go",
"chars": 703,
"preview": "package api_test\n\nimport (\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cerberauth/vulnapi/api\"\n\t\"githu"
},
{
"path": "cmd/discover/api.go",
"chars": 2972,
"preview": "package discover\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/cerberauth/cobracurl\"\n\tinternalCmd \"github.com/ce"
},
{
"path": "cmd/discover/domain.go",
"chars": 2846,
"preview": "package discover\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/cerberauth/cobracurl\"\n\tinternalCmd \"github.com/cerberauth/vulnapi"
},
{
"path": "cmd/discover/root.go",
"chars": 504,
"preview": "package discover\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"go.opentelemetry.io/otel/attribute\"\n)\n\nconst (\n\totelName = \"githu"
},
{
"path": "cmd/discover/root_test.go",
"chars": 350,
"preview": "package discover_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/cmd/discover\"\n\t\"github.com/stretchr/testify/"
},
{
"path": "cmd/jwt/root.go",
"chars": 3888,
"preview": "package jwt\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/cerberauth/jwtop/jwt/editor\"\n\t\"github.com/cerbera"
},
{
"path": "cmd/root.go",
"chars": 2137,
"preview": "package cmd\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/spf13/cobra\"\n\n\t\"github.com/cerberauth/vulnapi/cmd/discover\"\n"
},
{
"path": "cmd/scan/curl.go",
"chars": 5039,
"preview": "package scan\n\nimport (\n\t\"io\"\n\t\"log\"\n\n\t\"github.com/cerberauth/cobracurl\"\n\tinternalCmd \"github.com/cerberauth/vulnapi/inte"
},
{
"path": "cmd/scan/graphql.go",
"chars": 4513,
"preview": "package scan\n\nimport (\n\t\"log\"\n\n\t\"github.com/cerberauth/cobracurl\"\n\tinternalCmd \"github.com/cerberauth/vulnapi/internal/c"
},
{
"path": "cmd/scan/openapi.go",
"chars": 4897,
"preview": "package scan\n\nimport (\n\t\"bufio\"\n\t\"log\"\n\t\"os\"\n\n\t\"github.com/cerberauth/cobracurl\"\n\tinternalCmd \"github.com/cerberauth/vul"
},
{
"path": "cmd/scan/root.go",
"chars": 600,
"preview": "package scan\n\nimport (\n\t\"github.com/spf13/cobra\"\n\t\"go.opentelemetry.io/otel/attribute\"\n)\n\nconst (\n\totelName = \"github.co"
},
{
"path": "cmd/scan/root_test.go",
"chars": 330,
"preview": "package scan_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/cmd/scan\"\n\t\"github.com/stretchr/testify/assert\"\n"
},
{
"path": "cmd/serve/root.go",
"chars": 659,
"preview": "package serve\n\nimport (\n\t\"log\"\n\n\t\"github.com/cerberauth/vulnapi/api\"\n\t\"github.com/gin-contrib/requestid\"\n\t\"github.com/gi"
},
{
"path": "demo.cast",
"chars": 15305,
"preview": "{\"version\": 2, \"width\": 190, \"height\": 46, \"timestamp\": 1729172921, \"env\": {\"SHELL\": \"/usr/bin/zsh\", \"TERM\": \"xterm-256c"
},
{
"path": "docs/best-practices/_meta.yml",
"chars": 22,
"preview": "label: Best Practices\n"
},
{
"path": "docs/best-practices/jwt.mdx",
"chars": 4889,
"preview": "---\ntitle: JWT Security Best Practices\ndescription: Best practices for securely implementing JSON Web Tokens (JWTs) in y"
},
{
"path": "docs/best-practices/security-headers.mdx",
"chars": 4351,
"preview": "---\ntitle: API Security HTTP Headers\ndescription: Learn about the importance of HTTP security headers for API security a"
},
{
"path": "docs/first-scan.mdx",
"chars": 3260,
"preview": "---\ntitle: Your First Scan\ndescription: Run your first API security scan in under a minute using vulnapi scan curl.\nside"
},
{
"path": "docs/getting-started.mdx",
"chars": 1244,
"preview": "---\ntitle: Getting Started\ndescription: Get your first API security scan running in under five minutes.\nsidebar:\n order"
},
{
"path": "docs/github-action.mdx",
"chars": 1489,
"preview": "---\ntitle: GitHub Action\ndescription: VulnAPI can be integrated into your CI/CD pipeline using GitHub Actions. This allo"
},
{
"path": "docs/index.mdx",
"chars": 5503,
"preview": "---\ntitle: Introduction\ndescription: VulnAPI is an Open Source DAST that scans APIs for vulnerabilities and security ris"
},
{
"path": "docs/installation.mdx",
"chars": 3179,
"preview": "---\ntitle: Installation\ndescription: Learn how to install VulnAPI.\nsidebar:\n order: 3\n---\n\nYou can installed pre-built "
},
{
"path": "docs/labs.mdx",
"chars": 2262,
"preview": "---\ntitle: API Vulnerabilities Labs\ndescription: Open Source vulnerability labs for practicing API security testing with"
},
{
"path": "docs/reference/_meta.yml",
"chars": 17,
"preview": "label: Reference\n"
},
{
"path": "docs/reference/cli/_meta.yml",
"chars": 11,
"preview": "label: CLI\n"
},
{
"path": "docs/reference/cli/discover-api.mdx",
"chars": 1017,
"preview": "---\ntitle: discover api\ndescription: Discover well-known paths and technology fingerprints for an API.\n---\n\nProbe a base"
},
{
"path": "docs/reference/cli/discover-domain.mdx",
"chars": 699,
"preview": "---\ntitle: discover domain\ndescription: Discover API endpoints across common subdomains of a domain.\n---\n\nEnumerate comm"
},
{
"path": "docs/reference/cli/index.mdx",
"chars": 3209,
"preview": "---\ntitle: CLI reference\ndescription: Overview of all VulnAPI CLI commands.\nsidebar:\n order: 1\n---\n\nimport { LinkCard, "
},
{
"path": "docs/reference/cli/jwt-generate.mdx",
"chars": 1319,
"preview": "---\ntitle: jwt generate\ndescription: Generate a new JWT from an existing token, re-signed with a different algorithm or "
},
{
"path": "docs/reference/cli/scan-curl.mdx",
"chars": 1423,
"preview": "---\ntitle: scan curl\ndescription: Scan a single API endpoint using curl-style options.\n---\n\nScan a single API endpoint b"
},
{
"path": "docs/reference/cli/scan-graphql.mdx",
"chars": 1081,
"preview": "---\ntitle: scan graphql\ndescription: Scan a GraphQL endpoint for security vulnerabilities.\n---\n\nRun security scans again"
},
{
"path": "docs/reference/cli/scan-openapi.mdx",
"chars": 1559,
"preview": "---\ntitle: scan openapi\ndescription: Scan all endpoints defined in an OpenAPI specification.\n---\n\nLoad an OpenAPI specif"
},
{
"path": "docs/reference/cli/serve.mdx",
"chars": 630,
"preview": "---\ntitle: serve\ndescription: Start the VulnAPI HTTP server in REST API mode.\n---\n\nStart VulnAPI as an HTTP server that "
},
{
"path": "docs/reference/output-formats.mdx",
"chars": 3206,
"preview": "---\ntitle: Output Formats\ndescription: Reference for VulnAPI's table, JSON, and YAML output formats and CI/CD severity t"
},
{
"path": "docs/reference/scan-ids.mdx",
"chars": 3637,
"preview": "---\ntitle: Scan IDs Reference\ndescription: Complete reference table of all VulnAPI scan IDs, categories, and severity sc"
},
{
"path": "docs/vampi.mdx",
"chars": 2921,
"preview": "---\ntitle: VulnAPI Training on VAmPI (Vulnerable REST API)\ndescription: Train yourself on how to use VulnAPI to scan RES"
},
{
"path": "docs/vulnerabilities/_meta.yml",
"chars": 23,
"preview": "label: Vulnerabilities\n"
},
{
"path": "docs/vulnerabilities/broken-authentication/_meta.yml",
"chars": 29,
"preview": "label: Broken Authentication\n"
},
{
"path": "docs/vulnerabilities/broken-authentication/authentication-bypass.mdx",
"chars": 4017,
"preview": "---\ntitle: Authentication Bypass\ndescription: A vulnerability where an endpoint that requires authentication accepts req"
},
{
"path": "docs/vulnerabilities/broken-authentication/brute-force-attack.mdx",
"chars": 2711,
"preview": "---\ntitle: API Authentication Brute Force Attack\ndescription: A brute force attack, in the context of API authentication"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-alg-none.mdx",
"chars": 5626,
"preview": "---\ntitle: JWT None Algorithm\ndescription: Accepting the \"none\" algorithm in a JSON Web Token (JWT) occurs when a JWT is"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-blank-secret.mdx",
"chars": 3578,
"preview": "---\ntitle: JWT Blank Secret\ndescription: A vulnerability occurs when a JSON Web Token (JWT) is signed with an empty secr"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.excalidraw",
"chars": 30262,
"preview": "{\n \"type\": \"excalidraw\",\n \"version\": 2,\n \"source\": \"https://excalidraw.com\",\n \"elements\": [\n {\n \"type\": \"tex"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-cross-service-relay-attack.mdx",
"chars": 6836,
"preview": "---\ntitle: Token Cross Service Relay Attack\ndescription: A vulnerability arises when a Token is forged by the same servi"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-kid-injection.mdx",
"chars": 3617,
"preview": "---\ntitle: JWT KID Header Injection\ndescription: JWT KID Header Injection occurs when the `kid` (Key ID) header paramete"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-not-verified.mdx",
"chars": 4091,
"preview": "---\ntitle: JWT Signature Not Verified\ndescription: A vulnerability where the server accepts JWTs without verifying their"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-null-signature.mdx",
"chars": 3856,
"preview": "---\ntitle: JWT Null Signature\ndescription: JWT Null Signature vulnerability occurs when a JSON Web Token (JWT) lacks a s"
},
{
"path": "docs/vulnerabilities/broken-authentication/jwt-weak-secret.mdx",
"chars": 4269,
"preview": "---\ntitle: JWT Weak Secret\ndescription: A vulnerability occurs when a JSON Web Token (JWT) is signed with a common, a we"
},
{
"path": "docs/vulnerabilities/security-misconfiguration/_meta.yml",
"chars": 33,
"preview": "label: Security Misconfiguration\n"
},
{
"path": "docs/vulnerabilities/security-misconfiguration/graphql-introspection.mdx",
"chars": 3011,
"preview": "---\ntitle: GraphQL Introspection Enabled\ndescription: GraphQL introspection is a feature that allows clients to query th"
},
{
"path": "docs/vulnerabilities/security-misconfiguration/http-cookies.mdx",
"chars": 4662,
"preview": "---\ntitle: HTTP Cookies Misconfiguration\ndescription: Missing or misconfigured cookie security flags expose session toke"
},
{
"path": "docs/vulnerabilities/security-misconfiguration/http-method-allow-override.mdx",
"chars": 3371,
"preview": "---\ntitle: HTTP Method Override Enabled\ndescription: HTTP Method Override is a feature that allows clients to override t"
},
{
"path": "docs/vulnerabilities/security-misconfiguration/http-trace-track.mdx",
"chars": 4326,
"preview": "---\ntitle: HTTP TRACE and TRACK Methods Enabled\ndescription: HTTP TRACE and TRACK methods echo back the incoming request"
},
{
"path": "docs/vulnerabilities/security-misconfiguration/tls.mdx",
"chars": 3702,
"preview": "---\ntitle: TLS Missing or Misconfigured\ndescription: TLS (Transport Layer Security) is a protocol that provides secure c"
},
{
"path": "docs/vulnerabilities.mdx",
"chars": 6006,
"preview": "---\ntitle: API Vulnerabilities\ndescription: List of all vulnerabilities detected by VulnAPI.\nsidebar:\n order: 100\n---\n\n"
},
{
"path": "go.mod",
"chars": 4415,
"preview": "module github.com/cerberauth/vulnapi\n\ngo 1.26.0\n\nrequire (\n\tgithub.com/brianvoe/gofakeit/v7 v7.14.1\n\tgithub.com/cerberau"
},
{
"path": "go.sum",
"chars": 20690,
"preview": "github.com/brianvoe/gofakeit/v7 v7.14.1 h1:a7fe3fonbj0cW3wgl5VwIKfZtiH9C3cLnwcIXWT7sow=\ngithub.com/brianvoe/gofakeit/v7 "
},
{
"path": "internal/auth/api_key.go",
"chars": 646,
"preview": "package auth\n\nfunc NewAPIKeySecurityScheme(name string, in SchemeIn, value *string) (*SecurityScheme, error) {\n\ttokenFor"
},
{
"path": "internal/auth/api_key_test.go",
"chars": 1964,
"preview": "package auth_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/stretchr/testify/ass"
},
{
"path": "internal/auth/basic.go",
"chars": 1385,
"preview": "package auth\n\nimport \"encoding/base64\"\n\ntype HTTPBasicCredentials struct {\n\tUsername string `json:\"username\" yaml:\"usern"
},
{
"path": "internal/auth/basic_test.go",
"chars": 1643,
"preview": "package auth_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/stretchr/testify/ass"
},
{
"path": "internal/auth/bearer.go",
"chars": 916,
"preview": "package auth\n\nimport (\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n)\n\nfunc NewAuthorizationBearerSecurityScheme(name string"
},
{
"path": "internal/auth/bearer_test.go",
"chars": 2392,
"preview": "package auth_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/stretchr/testify/ass"
},
{
"path": "internal/auth/headers.go",
"chars": 116,
"preview": "package auth\n\nconst AuthorizationHeader = \"Authorization\"\nconst BearerPrefix = \"Bearer\"\nconst BasicPrefix = \"Basic\"\n"
},
{
"path": "internal/auth/no_auth.go",
"chars": 322,
"preview": "package auth\n\nvar defaultName = \"no_auth\"\n\nfunc NewNoAuthSecurityScheme() (*SecurityScheme, error) {\n\treturn NewSecurity"
},
{
"path": "internal/auth/no_auth_test.go",
"chars": 856,
"preview": "package auth_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/stretchr/testify/ass"
},
{
"path": "internal/auth/oauth.go",
"chars": 2096,
"preview": "package auth\n\nimport (\n\t\"time\"\n\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n)\n\ntype OAuthFlow string\n\nconst (\n\tAuthorizatio"
},
{
"path": "internal/auth/oauth_test.go",
"chars": 3379,
"preview": "package auth_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/stretchr/testify/ass"
},
{
"path": "internal/auth/scheme.go",
"chars": 760,
"preview": "package auth\n\ntype SchemeName string\n\n// Values are registred in the IANA Authentication Scheme registry\n// https://www."
},
{
"path": "internal/auth/scheme_test.go",
"chars": 583,
"preview": "package auth_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/stretchr/testify/ass"
},
{
"path": "internal/auth/security_scheme.go",
"chars": 5876,
"preview": "package auth\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n\t\"github.com/cerberauth/jwtop/jwt/ed"
},
{
"path": "internal/auth/security_scheme_test.go",
"chars": 14328,
"preview": "package auth_test\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n\t\"github.com/cerbera"
},
{
"path": "internal/auth/type.go",
"chars": 234,
"preview": "package auth\n\ntype Type string\n\nconst (\n\tHttpType Type = \"http\"\n\tOAuth2 Type = \"oauth2\"\n\tOpenIdConnect Type "
},
{
"path": "internal/auth/uniq_name.go",
"chars": 338,
"preview": "package auth\n\nfunc GetSecuritySchemeUniqueName(securityScheme *SecurityScheme) string {\n\tif securityScheme == nil {\n\t\tre"
},
{
"path": "internal/auth/uniq_name_test.go",
"chars": 1038,
"preview": "package auth_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/stretchr/testify/ass"
},
{
"path": "internal/cmd/args.go",
"chars": 2475,
"preview": "package cmd\n\nimport (\n\t\"github.com/cerberauth/cobracurl\"\n\t\"github.com/spf13/cobra\"\n)\n\nvar (\n\tincludeScans []string\n\texcl"
},
{
"path": "internal/cmd/args_test.go",
"chars": 2338,
"preview": "package cmd_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/cmd\"\n\t\"github.com/spf13/cobra\"\n\t\"github."
},
{
"path": "internal/cmd/http.go",
"chars": 726,
"preview": "package cmd\n\nimport (\n\t\"github.com/cerberauth/cobracurl\"\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n\t\"github.com/"
},
{
"path": "internal/cmd/printtable/fingerprint_table.go",
"chars": 1969,
"preview": "package printtable\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cerberauth/vulnapi/report\"\n\t\"github.com/cerberauth/vulnapi/scan/discov"
},
{
"path": "internal/cmd/printtable/printttable.go",
"chars": 1262,
"preview": "package printtable\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/olekukonko/tablewriter\"\n\t\"github.com/olekukonko/tablewriter/rend"
},
{
"path": "internal/cmd/printtable/report_table.go",
"chars": 3376,
"preview": "package printtable\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/cerberauth/vulnapi/report\"\n\t\"github.com/fatih/color"
},
{
"path": "internal/cmd/printtable/report_table_test.go",
"chars": 2967,
"preview": "package printtable_test\n\nimport (\n\t\"testing\"\n\n\tprinttable \"github.com/cerberauth/vulnapi/internal/cmd/printtable\"\n\t\"gith"
},
{
"path": "internal/cmd/printtable/wellknown_paths_table.go",
"chars": 2082,
"preview": "package printtable\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/cerberauth/vulnapi/report\"\n\t\"github.com/cerberauth/vulnapi/scan/discov"
},
{
"path": "internal/cmd/progressbar.go",
"chars": 311,
"preview": "package cmd\n\nimport (\n\t\"github.com/schollz/progressbar/v3\"\n)\n\nfunc NewProgressBar(max int) *progressbar.ProgressBar {\n\tr"
},
{
"path": "internal/cmd/report.go",
"chars": 2297,
"preview": "package cmd\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"os\"\n\n\tprinttable \"github.com/cerberauth/vulnapi/internal/cmd/printtable\""
},
{
"path": "internal/operation/operation.go",
"chars": 6226,
"preview": "package operation\n\nimport (\n\t\"bytes\"\n\t\"crypto/tls\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"regexp\"\n\t\"strings\"\n\n\t\"gi"
},
{
"path": "internal/operation/operation_test.go",
"chars": 8129,
"preview": "package operation_test\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"git"
},
{
"path": "internal/operation/operations.go",
"chars": 456,
"preview": "package operation\n\ntype Operations []*Operation\n\nfunc (o Operations) Len() int { return len(o) }\nfunc (o Operations"
},
{
"path": "internal/operation/operations_test.go",
"chars": 2137,
"preview": "package operation_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/operation\"\n\t\"github.co"
},
{
"path": "internal/request/client.go",
"chars": 2962,
"preview": "package request\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t"
},
{
"path": "internal/request/client_test.go",
"chars": 3480,
"preview": "package request_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.c"
},
{
"path": "internal/request/error.go",
"chars": 106,
"preview": "package request\n\nimport \"errors\"\n\nfunc NilResponseError() error {\n\treturn errors.New(\"response is nil\")\n}\n"
},
{
"path": "internal/request/request.go",
"chars": 3007,
"preview": "package request\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github"
},
{
"path": "internal/request/request_test.go",
"chars": 14129,
"preview": "package request_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"g"
},
{
"path": "internal/request/response.go",
"chars": 932,
"preview": "package request\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n)\n\ntype Response struct {\n\tBody *bytes.Buffer\n\tHttpResponse"
},
{
"path": "internal/request/response_test.go",
"chars": 1214,
"preview": "package request_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n"
},
{
"path": "internal/scan/attempt.go",
"chars": 1593,
"preview": "package scan\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/operation\"\n\t\"github.com/cerberauth/vulnapi/internal/requ"
},
{
"path": "internal/scan/attempt_test.go",
"chars": 2708,
"preview": "package scan\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/operation\"\n\t\"github.com/cerberau"
},
{
"path": "internal/scan/scan_url.go",
"chars": 533,
"preview": "package scan\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/internal/operation"
},
{
"path": "internal/scan/utils.go",
"chars": 421,
"preview": "package scan\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n)\n\nfunc IsUnauthorizedStatusCodeOr"
},
{
"path": "internal/scan/utils_test.go",
"chars": 826,
"preview": "package scan_test\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/request\"\n\t\"g"
},
{
"path": "logo-text-art.txt",
"chars": 619,
"preview": ".-----------------------------------------------------------.\n| "
},
{
"path": "main.go",
"chars": 175,
"preview": "package main\n\nimport \"github.com/cerberauth/vulnapi/cmd\"\n\nvar (\n\tversion = \"dev\"\n\tcommit = \"none\"\n\tdate = \"unknown\"\n"
},
{
"path": "openapi/base_url.go",
"chars": 593,
"preview": "package openapi\n\nimport (\n\t\"net/url\"\n)\n\nfunc (openapi *OpenAPI) BaseUrl() *url.URL {\n\tif openapi.baseUrl != nil {\n\t\tretu"
},
{
"path": "openapi/base_url_test.go",
"chars": 1278,
"preview": "package openapi_test\n\nimport (\n\t\"context\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/openapi\"\n\t\"github.com/s"
},
{
"path": "openapi/loader.go",
"chars": 1201,
"preview": "package openapi\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/url\"\n\n\t\"github.com/cerberauth/x/fsx\"\n\t\"github.com/getkin/kin-openap"
},
{
"path": "openapi/loader_test.go",
"chars": 1421,
"preview": "package openapi_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/openap"
},
{
"path": "openapi/openapi.go",
"chars": 380,
"preview": "package openapi\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/getkin/kin-openapi/openapi3\"\n\t\"go.opentelemetry.io/otel/attribute\"\n)\n"
},
{
"path": "openapi/operation.go",
"chars": 5089,
"preview": "package openapi\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"net/http\"\n\t\"path\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"gith"
},
{
"path": "openapi/param.go",
"chars": 5720,
"preview": "package openapi\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"strconv\"\n\n\t\"github.com/brianvoe/gofakeit/v7\"\n\t\"github.com/getkin/kin-openapi"
},
{
"path": "openapi/param_test.go",
"chars": 22441,
"preview": "package openapi_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/openapi\"\n\t\"github.com/stretchr/tes"
},
{
"path": "openapi/security_scheme.go",
"chars": 5614,
"preview": "package openapi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerb"
},
{
"path": "openapi/security_scheme_test.go",
"chars": 10946,
"preview": "package openapi_test\n\nimport (\n\t\"testing\"\n\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n\t\"github.com/cerberauth/vulnapi/inte"
},
{
"path": "openapi/security_scheme_values.go",
"chars": 848,
"preview": "package openapi\n\ntype SecuritySchemeValues struct {\n\tDefault interface{}\n\tValues map[string]interface{}\n}\n\nfunc NewSecu"
},
{
"path": "openapi/security_scheme_values_test.go",
"chars": 1886,
"preview": "package openapi_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/openapi\"\n\t\"github.com/stretchr/testify/assert"
},
{
"path": "openapi/validate.go",
"chars": 230,
"preview": "package openapi\n\nimport (\n\t\"context\"\n\t\"fmt\"\n)\n\nfunc (openapi *OpenAPI) Validate(ctx context.Context) error {\n\tif openapi"
},
{
"path": "openapi/validate_test.go",
"chars": 1390,
"preview": "package openapi_test\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/openapi\"\n\t\"github.com/stretchr/tes"
},
{
"path": "renovate.json",
"chars": 70,
"preview": "{\n \"extends\": [\n \"github>cerberauth/renovate-config\"\n ]\n}"
},
{
"path": "report/capec.go",
"chars": 147,
"preview": "package report\n\ntype CAPEC string\n\nconst (\n\tCAPEC_31_Manipulating_HTTP_Cookies CAPEC = \"CAPEC-31: Accessing/Intercepting"
},
{
"path": "report/curl_report.go",
"chars": 1293,
"preview": "package report\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n)\n\ntype CurlReport struct {\n\tMethod"
},
{
"path": "report/curl_report_test.go",
"chars": 3288,
"preview": "package report_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n\t\"github.com/cerberauth/v"
},
{
"path": "report/cwe.go",
"chars": 1082,
"preview": "package report\n\ntype CWE string\n\nconst (\n\tCWE_16_Configuration CWE = \"CWE-16: Configuration\"\n\n\tCWE_287_Improper_Authenti"
},
{
"path": "report/graphql_report.go",
"chars": 1414,
"preview": "package report\n\nimport \"github.com/cerberauth/vulnapi/internal/auth\"\n\ntype GraphQLOperationReport struct {\n\tID string "
},
{
"path": "report/issue.go",
"chars": 686,
"preview": "package report\n\ntype Classifications struct {\n\tOWASP OWASP `json:\"owasp,omitempty\" yaml:\"owasp,omitempty\"`\n\tCWE CWE "
},
{
"path": "report/issue_report.go",
"chars": 4998,
"preview": "package report\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulna"
},
{
"path": "report/issue_report_test.go",
"chars": 8811,
"preview": "package report_test\n\nimport (\n\t\"testing\"\n\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n\t\"github.com/cerberauth/vulnapi/inter"
},
{
"path": "report/openapi_report.go",
"chars": 2577,
"preview": "package report\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/operation\"\n\t\"github.com/getkin/kin-openapi/openapi3\"\n)"
},
{
"path": "report/openapi_report_test.go",
"chars": 3205,
"preview": "package report_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/openapi\"\n\t\"github.com/cerberauth/v"
},
{
"path": "report/options_report.go",
"chars": 119,
"preview": "package report\n\ntype OptionsReport struct{} // TODO\n\nfunc NewOptionsReport() OptionsReport {\n\treturn OptionsReport{}\n}\n"
},
{
"path": "report/owasp.go",
"chars": 1014,
"preview": "package report\n\ntype OWASP string\n\nconst (\n\tOWASP_2023_BOLA OWASP = \"API1:2023 Broken Object "
},
{
"path": "report/report.go",
"chars": 6911,
"preview": "package report\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cer"
},
{
"path": "report/report_test.go",
"chars": 7207,
"preview": "package report_test\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cerberauth/vulnapi"
},
{
"path": "report/reporter.go",
"chars": 3634,
"preview": "package report\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/int"
},
{
"path": "report/reporter_test.go",
"chars": 13950,
"preview": "package report_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerber"
},
{
"path": "report/test/issue.yaml",
"chars": 294,
"preview": "id: expectedID\nname: expectedName\nurl: expectedURL\n\ncvss:\n version: 4.0\n vector: AV:N/AC:L/Au:N/C:P/I:N/A:N\n score: 7"
},
{
"path": "report/test/issue_nil_classifications.yaml",
"chars": 124,
"preview": "id: expectedID\nname: expectedName\nurl: expectedURL\n\ncvss:\n version: 4.0\n vector: AV:N/AC:L/Au:N/C:P/I:N/A:N\n score: 7"
},
{
"path": "scan/broken_authentication/authentication_bypass/authentication_bypass.go",
"chars": 1563,
"preview": "package authenticationbypass\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/in"
},
{
"path": "scan/broken_authentication/authentication_bypass/authentication_bypass_test.go",
"chars": 2486,
"preview": "package authenticationbypass_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"git"
},
{
"path": "scan/broken_authentication/jwt/alg_none/alg_none.go",
"chars": 3157,
"preview": "package algnone\n\nimport (\n\t\"strings\"\n\n\t\"github.com/cerberauth/jwtop/jwt/editor\"\n\t\"github.com/cerberauth/vulnapi/internal"
},
{
"path": "scan/broken_authentication/jwt/alg_none/alg_none_test.go",
"chars": 5375,
"preview": "package algnone_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerbe"
},
{
"path": "scan/broken_authentication/jwt/alg_none/methods.go",
"chars": 416,
"preview": "package algnone\n\ntype signingMethodNone struct {\n\talg string\n}\n\nfunc (m *signingMethodNone) SetAlg(alg string) {\n\tm.alg "
},
{
"path": "scan/broken_authentication/jwt/blank_secret/blank_secret.go",
"chars": 2318,
"preview": "package blanksecret\n\nimport (\n\t\"github.com/cerberauth/jwtop/jwt/editor\"\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t"
},
{
"path": "scan/broken_authentication/jwt/blank_secret/blank_secret_test.go",
"chars": 3173,
"preview": "package blanksecret_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/c"
},
{
"path": "scan/broken_authentication/jwt/kid_injection/kid_injection.go",
"chars": 3127,
"preview": "package kidinjection\n\nimport (\n\t\"github.com/cerberauth/jwtop/jwt/exploit\"\n\t\"github.com/cerberauth/vulnapi/internal/auth\""
},
{
"path": "scan/broken_authentication/jwt/kid_injection/kid_injection_test.go",
"chars": 4386,
"preview": "package kidinjection_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/"
},
{
"path": "scan/broken_authentication/jwt/not_verified/not_verified.go",
"chars": 2630,
"preview": "package notverified\n\nimport (\n\t\"github.com/cerberauth/jwtop/jwt/editor\"\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t"
},
{
"path": "scan/broken_authentication/jwt/not_verified/not_verified_test.go",
"chars": 4175,
"preview": "package notverified_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/c"
},
{
"path": "scan/broken_authentication/jwt/null_signature/null_signature.go",
"chars": 2274,
"preview": "package nullsignature\n\nimport (\n\t\"github.com/cerberauth/jwtop/jwt/editor\"\n\t\"github.com/cerberauth/vulnapi/internal/auth\""
},
{
"path": "scan/broken_authentication/jwt/null_signature/null_signature_test.go",
"chars": 3195,
"preview": "package nullsignature_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com"
},
{
"path": "scan/broken_authentication/jwt/weak_secret/weak_secret.go",
"chars": 3726,
"preview": "package weaksecret\n\nimport (\n\t\"github.com/cerberauth/jwtop/jwt/editor\"\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\""
},
{
"path": "scan/broken_authentication/jwt/weak_secret/weak_secret_test.go",
"chars": 4111,
"preview": "package weaksecret_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/ce"
},
{
"path": "scan/discover/accept_unauthenticated/accept_unauthenticated_operation.go",
"chars": 1084,
"preview": "package acceptunauthenticated\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/i"
},
{
"path": "scan/discover/accept_unauthenticated/accept_unauthenticated_operation_test.go",
"chars": 1281,
"preview": "package acceptunauthenticated_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"gi"
},
{
"path": "scan/discover/discoverable_graphql/discoverable_graphql.go",
"chars": 1282,
"preview": "package discoverablegraphql\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/int"
},
{
"path": "scan/discover/discoverable_graphql/discoverable_graphql_test.go",
"chars": 1923,
"preview": "package discoverablegraphql_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"gith"
},
{
"path": "scan/discover/discoverable_openapi/discoverable_openapi.go",
"chars": 1266,
"preview": "package discoverableopenapi\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/int"
},
{
"path": "scan/discover/discoverable_openapi/discoverable_openapi_test.go",
"chars": 2005,
"preview": "package discoverableopenapi_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"gith"
},
{
"path": "scan/discover/exposed_files/exposed_files.go",
"chars": 1274,
"preview": "package exposedfiles\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/internal/o"
},
{
"path": "scan/discover/exposed_files/exposed_files_test.go",
"chars": 1897,
"preview": "package exposedfiles_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/"
},
{
"path": "scan/discover/fingerprint/fingerprint.go",
"chars": 5477,
"preview": "package fingerprint\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/internal/op"
},
{
"path": "scan/discover/fingerprint/fingerprint_test.go",
"chars": 8410,
"preview": "package fingerprint_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/c"
},
{
"path": "scan/discover/healthcheck/healthcheck.go",
"chars": 1252,
"preview": "package healthcheck\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/internal/op"
},
{
"path": "scan/discover/healthcheck/healthcheck_test.go",
"chars": 1886,
"preview": "package healthcheck_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/c"
},
{
"path": "scan/discover/utils.go",
"chars": 2582,
"preview": "package discover\n\nimport (\n\t\"log\"\n\t\"net/http\"\n\t\"net/url\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/ce"
},
{
"path": "scan/discover/utils_test.go",
"chars": 4898,
"preview": "package discover_test\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"gith"
},
{
"path": "scan/discover/well-known/well_known.go",
"chars": 1224,
"preview": "package wellknown\n\nimport (\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/internal/oper"
},
{
"path": "scan/discover/well-known/well_known_test.go",
"chars": 1891,
"preview": "package wellknown_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cer"
},
{
"path": "scan/graphql/introspection_enabled/introspection_enabled.go",
"chars": 3466,
"preview": "package introspectionenabled\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\""
},
{
"path": "scan/graphql/introspection_enabled/introspection_enabled_test.go",
"chars": 3665,
"preview": "package introspectionenabled_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"git"
},
{
"path": "scan/misconfiguration/http_cookies/http_cookies.go",
"chars": 5137,
"preview": "package httpcookies\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnap"
},
{
"path": "scan/misconfiguration/http_cookies/http_cookies_test.go",
"chars": 7342,
"preview": "package httpcookies_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"gith"
},
{
"path": "scan/misconfiguration/http_headers/http_headers.go",
"chars": 7171,
"preview": "package httpheaders\n\nimport (\n\t\"strings\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi"
},
{
"path": "scan/misconfiguration/http_headers/http_headers_test.go",
"chars": 8651,
"preview": "package httpheaders_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/c"
},
{
"path": "scan/misconfiguration/http_method_override/http_method_override.go",
"chars": 6303,
"preview": "package httpmethodoverride\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth"
},
{
"path": "scan/misconfiguration/http_method_override/http_method_override_test.go",
"chars": 9411,
"preview": "package httpmethodoverride_test\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\tjwtop \"github.com/cerberauth/jwtop/jwt\"\n\t\""
},
{
"path": "scan/misconfiguration/http_trace/http_trace_method.go",
"chars": 1555,
"preview": "package httptrace\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/"
},
{
"path": "scan/misconfiguration/http_trace/http_trace_method_test.go",
"chars": 1647,
"preview": "package httptrace_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cer"
},
{
"path": "scan/misconfiguration/http_track/http_track_method.go",
"chars": 1610,
"preview": "package httptrack\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cerberauth/vulnapi/"
},
{
"path": "scan/misconfiguration/http_track/http_track_method_test.go",
"chars": 1615,
"preview": "package httptrack_test\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/cerberauth/vulnapi/internal/auth\"\n\t\"github.com/cer"
}
]
// ... and 34 more files (download for full content)
About this extraction
This page contains the full source code of the cerberauth/vulnapi GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 234 files (1.8 MB), approximately 740.1k tokens, and a symbol index with 894 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.