Skip to content

Commit

Permalink
Merge pull request #215 from cerberauth/improve-graphql-introspection
Browse files Browse the repository at this point in the history
Improve Graphql introspection scan
  • Loading branch information
emmanuelgautier authored Oct 30, 2024
2 parents 919ea57 + ec38526 commit ea66b5d
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 6 deletions.
46 changes: 46 additions & 0 deletions .github/workflows/scans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,49 @@ jobs:
- 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@v4

- name: Login to GitHub Container Registry
uses: docker/login-action@v3
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@v5
with:
go-version: ${{ env.GO_VERSION }}

- name: Build
run: go build -v ./...

- name: VulnAPI
id: vulnapi
continue-on-error: true
run: |
go run main.go scan graphql ${{ matrix.url }} --scans "${{ matrix.challenge }}"
- 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)
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func ScanHandler(op *operation.Operation, securityScheme auth.SecurityScheme) (*
}
r.AddScanAttempt(attempt)

if attempt.Response.GetStatusCode() == http.StatusOK { // TODO: check the GraphQL response
if attempt.Response.GetStatusCode() == http.StatusOK && strings.Contains(attempt.Response.GetBody().String(), "queryType") {
r.AddIssueReport(vulnReport.Fail()).End()
return r, nil
}
Expand Down
43 changes: 38 additions & 5 deletions scan/graphql/introspection_enabled/introspection_enabled_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestGraphqlIntrospectionScanHandler_Failed_WhenRespondHTTPStatusIsOK(t *tes
httpmock.ActivateNonDefault(client.Client)
defer httpmock.DeactivateAndReset()

operation := operation.MustNewOperation(http.MethodGet, "http://localhost:8080/", nil, client)
operation := operation.MustNewOperation(http.MethodPost, "http://localhost:8080/", nil, client)
resBody := []byte(`{"data": {"__schema": {"queryType": {"name": "Query"}}}}`)
httpmock.RegisterResponder(http.MethodPost, operation.URL.String(), httpmock.NewBytesResponder(http.StatusOK, resBody))
httpmock.RegisterResponder(http.MethodGet, operation.URL.String(), httpmock.NewBytesResponder(http.StatusOK, resBody))
Expand All @@ -30,14 +30,47 @@ func TestGraphqlIntrospectionScanHandler_Failed_WhenRespondHTTPStatusIsOK(t *tes
assert.True(t, report.Issues[0].HasFailed())
}

func TestGraphqlIntrospectionScanHandler_Passed_WhenNotFoundStatus(t *testing.T) {
func TestGraphqlIntrospectionScanHandler_Failed_WhenRespond_GETMethodOnly_HTTPStatusIsOK(t *testing.T) {
client := request.GetDefaultClient()
httpmock.ActivateNonDefault(client.Client)
defer httpmock.DeactivateAndReset()

operation := operation.MustNewOperation(http.MethodGet, "http://localhost:8080/", nil, client)
httpmock.RegisterResponder(http.MethodPost, operation.URL.String(), httpmock.NewBytesResponder(http.StatusNoContent, nil))
httpmock.RegisterResponder(http.MethodGet, operation.URL.String(), httpmock.NewBytesResponder(http.StatusNoContent, nil))
operation := operation.MustNewOperation(http.MethodPost, "http://localhost:8080/", nil, client)
resBody := []byte(`{"data": {"__schema": {"queryType": {"name": "Query"}}}}`)
httpmock.RegisterResponder(http.MethodPost, operation.URL.String(), httpmock.NewBytesResponder(http.StatusBadRequest, nil))
httpmock.RegisterResponder(http.MethodGet, operation.URL.String(), httpmock.NewBytesResponder(http.StatusOK, resBody))

report, err := introspectionenabled.ScanHandler(operation, auth.NewNoAuthSecurityScheme())

require.NoError(t, err)
assert.Equal(t, 2, httpmock.GetTotalCallCount())
assert.True(t, report.Issues[0].HasFailed())
}

func TestGraphqlIntrospectionScanHandler_Passed_WhenBadRequestStatus(t *testing.T) {
client := request.GetDefaultClient()
httpmock.ActivateNonDefault(client.Client)
defer httpmock.DeactivateAndReset()

operation := operation.MustNewOperation(http.MethodPost, "http://localhost:8080/", nil, client)
httpmock.RegisterResponder(http.MethodPost, operation.URL.String(), httpmock.NewBytesResponder(http.StatusBadRequest, nil))
httpmock.RegisterResponder(http.MethodGet, operation.URL.String(), httpmock.NewBytesResponder(http.StatusBadRequest, nil))

report, err := introspectionenabled.ScanHandler(operation, auth.NewNoAuthSecurityScheme())

require.NoError(t, err)
assert.Equal(t, 2, httpmock.GetTotalCallCount())
assert.True(t, report.Issues[0].HasPassed())
}

func TestGraphqlIntrospectionScanHandler_Passed_WhenOKStatusButNoQuery(t *testing.T) {
client := request.GetDefaultClient()
httpmock.ActivateNonDefault(client.Client)
defer httpmock.DeactivateAndReset()

operation := operation.MustNewOperation(http.MethodPost, "http://localhost:8080/", nil, client)
httpmock.RegisterResponder(http.MethodPost, operation.URL.String(), httpmock.NewBytesResponder(http.StatusOK, nil))
httpmock.RegisterResponder(http.MethodGet, operation.URL.String(), httpmock.NewBytesResponder(http.StatusOK, nil))

report, err := introspectionenabled.ScanHandler(operation, auth.NewNoAuthSecurityScheme())

Expand Down

0 comments on commit ea66b5d

Please sign in to comment.