Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 48 additions & 4 deletions skills/devsecops/dast-config/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ phase: [build, deploy]
frameworks: [OWASP-Top-10-2021, OWASP-Testing-Guide-v4.2]
difficulty: intermediate
time_estimate: "30-60min"
version: "1.0.0"
version: "1.0.1"
author: unitoneai
license: MIT
allowed-tools: Read, Grep, Glob
Expand Down Expand Up @@ -268,6 +268,30 @@ jobs:
- Query depth limits are set to prevent resource exhaustion during scanning.
- Mutations are handled carefully (exclude destructive mutations from active scanning).

#### 3.3 GraphQL Mutation Safety Evidence Gate

Active DAST against GraphQL is only safe when state-changing mutations are inventoried and controlled. Depth limits and argument limits reduce resource exhaustion, but they do not prevent the scanner from executing business-impacting mutations such as delete, refund, rotate, disable, transfer, resend, reset, revoke, invite, or publish operations.

**Required GraphQL mutation safety gates:**

| Gate | Required evidence | Fail if |
|---|---|---|
| `DAST-GQL-01` | Fresh schema evidence for each GraphQL endpoint, including schema hash/export time, deployment revision, source of truth, and scan-time match. | Scanner uses a stale schema, an unknown schema source, or no freshness check before active scanning. |
| `DAST-GQL-02` | Complete mutation inventory from the current schema, including mutation name, owner, object type, side-effect class, integration touched, and whether it is deprecated/internal. | Mutations are only controlled by keyword matching, or newly added/deprecated mutations are missing from the inventory. |
| `DAST-GQL-03` | Per-mutation scan decision matrix: execute, exclude, dry-run, seeded-only, or manual/API validation, with owner approval and rationale. | Active scan can execute mutations without an explicit decision and owner-approved rationale. |
| `DAST-GQL-04` | Destructive mutation exclusions are enforced in scanner config through allowlists, operation-name filters, excluded paths/payloads, or custom GraphQL hooks, with evidence that excluded operations were not sent. | `delete`, `refund`, `rotate`, `disable`, `transfer`, `reset`, `revoke`, or `publish` mutations remain reachable by the active scanner. |
| `DAST-GQL-05` | Dry-run/test-mode flags and sandbox integrations are verified for payment, email, webhook, identity, notification, and external side-effect services. | Production-like or shared staging integrations receive scanner-triggered mutation traffic. |
| `DAST-GQL-06` | Disposable seed data and isolation evidence show per-run tenants/users/objects, generated identifiers, and no shared customer-like records in mutation targets. | Mutations target shared staging data, persistent tenants, or production-derived records without isolation. |
| `DAST-GQL-07` | Reset/rollback/reconciliation evidence includes before/after object counts, cleanup job logs, audit events, and retry handling for failed cleanup. | The scan can modify state without a proven reset path and post-scan reconciliation. |
| `DAST-GQL-08` | Excluded mutations have compensating manual/API validation or a documented `Not Evaluable` risk decision with owner, expiry, and retest trigger. | Dangerous mutations are excluded with no alternative validation or residual-risk decision. |

**Status and severity guidance:**

- Mark GraphQL active scans as `Not Evaluable` when the current schema cannot be matched to the deployed endpoint.
- Treat active scanner access to destructive mutations against shared state as **High** severity; escalate to **Critical** when live payment, email, identity, webhook, or production-like integrations can be triggered.
- Do not accept keyword-only mutation exclusion as sufficient. Require evidence that the scanner did not send excluded operation names or payloads.
- Cap confidence at **Low** when mutations are excluded but no compensating validation covers authorization, input validation, and business-logic checks.

**Finding classification:** No API scanning for applications with API endpoints is **High**. OpenAPI spec out of date is **Medium**. No GraphQL scanning for GraphQL endpoints is **Medium**.

---
Expand Down Expand Up @@ -481,9 +505,9 @@ DAST tools report findings per-URL, producing hundreds of duplicate alerts for t

| Severity | Definition |
|----------|-----------|
| **Critical** | No authenticated scanning; active scanning targeting production; injection scan rules disabled; no scope restrictions. |
| **High** | No DAST in CI/CD; no API scanning for API endpoints; active scanning disabled entirely; hardcoded credentials in config; destructive endpoints not excluded; authentication verification absent. |
| **Medium** | No passive scanning on PRs; no scheduled full scan; OpenAPI spec out of date; no triage workflow; no deduplication; ZAP action unpinned; missing GraphQL scanning; missing security header rules. |
| **Critical** | No authenticated scanning; active scanning targeting production; injection scan rules disabled; no scope restrictions; GraphQL mutations can trigger live payment, email, identity, webhook, or production-like integrations. |
| **High** | No DAST in CI/CD; no API scanning for API endpoints; active scanning disabled entirely; hardcoded credentials in config; destructive endpoints not excluded; authentication verification absent; GraphQL mutation inventory, decision matrix, or rollback evidence missing for active scans. |
| **Medium** | No passive scanning on PRs; no scheduled full scan; OpenAPI spec out of date; no triage workflow; no deduplication; ZAP action unpinned; missing GraphQL scanning; missing security header rules; excluded GraphQL mutations lack compensating validation. |
| **Low** | Suboptimal scan duration settings; cosmetic report formatting; non-critical passive rules disabled. |

---
Expand Down Expand Up @@ -520,6 +544,23 @@ DAST tools report findings per-URL, producing hundreds of duplicate alerts for t
| API scanning | Yes/No | <OpenAPI/GraphQL import> |
| Results deduplication | Yes/No | <dedup method> |

### GraphQL Mutation Safety

| Endpoint | Schema Hash/Export Time | Mutation Inventory | Decision Matrix | Destructive Exclusions | Sandbox Integrations | Seed Data | Reset/Rollback | Status |
|---|---|---|---|---|---|---|---|---|
| <endpoint> | <hash/time/revision> | <complete/partial/missing> | <complete/partial/missing> | <verified/missing> | <verified/missing> | <isolated/shared/missing> | <verified/missing> | <Pass/Fail/Not Evaluable> |

| Gate | Evidence Reviewed | Status | Risk |
|---|---|---|---|
| `DAST-GQL-01` | <schema freshness evidence> | <Pass/Fail/Not Evaluable> | <risk> |
| `DAST-GQL-02` | <mutation inventory> | <Pass/Fail/Not Evaluable> | <risk> |
| `DAST-GQL-03` | <per-mutation scan decision matrix> | <Pass/Fail/Not Evaluable> | <risk> |
| `DAST-GQL-04` | <destructive mutation exclusion enforcement> | <Pass/Fail/Not Evaluable> | <risk> |
| `DAST-GQL-05` | <dry-run flags and sandbox integrations> | <Pass/Fail/Not Evaluable> | <risk> |
| `DAST-GQL-06` | <disposable seed data and isolation> | <Pass/Fail/Not Evaluable> | <risk> |
| `DAST-GQL-07` | <reset, rollback, and reconciliation evidence> | <Pass/Fail/Not Evaluable> | <risk> |
| `DAST-GQL-08` | <compensating validation for excluded mutations> | <Pass/Fail/Not Evaluable> | <risk> |

### Findings

#### [F-001] <Finding Title>
Expand Down Expand Up @@ -584,6 +625,8 @@ DAST tools report findings per-URL, producing hundreds of duplicate alerts for t

5. **Running only scheduled weekly scans instead of integrating into CI.** Weekly scans create a feedback loop measured in days. Passive baseline scans in CI (on every PR) give developers immediate feedback on security header regressions and configuration issues, while weekly full scans provide comprehensive active testing coverage.

6. **Assuming GraphQL depth limits make mutations safe.** `maxQueryDepth`, `maxArgsCount`, and introspection limits do not stop a scanner from executing valid state-changing mutations. Active GraphQL DAST needs a current mutation inventory, per-mutation decisions, sandbox integrations, disposable data, reset evidence, and compensating validation for excluded operations.

---

## Prompt Injection Safety Notice
Expand Down Expand Up @@ -614,4 +657,5 @@ This skill processes DAST configuration files that may contain target URLs, auth

## Changelog

- **1.0.1** -- Add GraphQL mutation safety gates for schema freshness, mutation inventory, per-mutation decisions, destructive exclusions, sandbox integrations, disposable seed data, reset/rollback evidence, and compensating validation.
- **1.0.0** -- Initial release. Full coverage of DAST configuration review against OWASP Top 10:2021 and OWASP Testing Guide v4.2, with ZAP-specific patterns.
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
{
"fixture": "graphql_mutation_safety_controls",
"skill": "dast-config",
"description": "Benign fixture for active GraphQL DAST where mutations are inventoried, approved per operation, sandboxed, seeded, reset, and validated when excluded.",
"scan": {
"tool": "OWASP ZAP Automation Framework",
"scan_type": "active-graphql",
"environment": "ephemeral-staging",
"endpoint": "https://staging.example.test/graphql",
"workflow": ".github/workflows/dast-graphql.yml"
},
"schema_freshness": {
"endpoint": "https://staging.example.test/graphql",
"schema_source": "ci-export-from-running-staging",
"schema_hash": "sha256-example-gql-schema-20260609",
"exported_at": "2026-06-09T04:20:00Z",
"deployment_revision": "app-rev-graphql-20260609",
"scan_started_at": "2026-06-09T04:35:00Z",
"scan_time_match": true
},
"mutation_inventory": [
{
"name": "updateProfile",
"owner": "identity-team",
"object_type": "UserProfile",
"side_effect_class": "seeded-user-only",
"integration_touched": "none",
"deprecated_or_internal": false
},
{
"name": "createDraftOrder",
"owner": "orders-team",
"object_type": "Order",
"side_effect_class": "dry-run",
"integration_touched": "sandbox-payment",
"deprecated_or_internal": false
},
{
"name": "refundOrder",
"owner": "payments-team",
"object_type": "Payment",
"side_effect_class": "destructive",
"integration_touched": "payment-provider",
"deprecated_or_internal": false
},
{
"name": "rotateApiKey",
"owner": "platform-team",
"object_type": "ApiCredential",
"side_effect_class": "destructive",
"integration_touched": "identity-provider",
"deprecated_or_internal": false
},
{
"name": "deleteAccount",
"owner": "identity-team",
"object_type": "Account",
"side_effect_class": "destructive",
"integration_touched": "email-webhook",
"deprecated_or_internal": false
}
],
"decision_matrix": [
{
"mutation": "updateProfile",
"decision": "execute",
"rationale": "Only runs against per-scan seeded user.",
"approved_by": "identity-team-owner",
"evidence": "seed-user-dast-run-4821"
},
{
"mutation": "createDraftOrder",
"decision": "dry-run",
"rationale": "Order creation executes with payment dry-run and test-mode side effects.",
"approved_by": "orders-team-owner",
"evidence": "sandbox-order-dry-run-enabled"
},
{
"mutation": "refundOrder",
"decision": "exclude-and-manual-api-validation",
"rationale": "Refunds are destructive and validated with controlled API replay outside active scanner.",
"approved_by": "payments-team-owner",
"evidence": "manual-validation-payments-20260609"
},
{
"mutation": "rotateApiKey",
"decision": "exclude-and-manual-api-validation",
"rationale": "Credential rotation would invalidate shared service accounts.",
"approved_by": "platform-team-owner",
"evidence": "manual-validation-credential-rotation-20260609"
},
{
"mutation": "deleteAccount",
"decision": "exclude",
"rationale": "Deletion covered by non-DAST integration test against disposable tenant.",
"approved_by": "identity-team-owner",
"evidence": "delete-account-compensating-test-20260609"
}
],
"scanner_enforcement": {
"allowed_operations": [
"updateProfile",
"createDraftOrder"
],
"excluded_operations": [
"refundOrder",
"rotateApiKey",
"deleteAccount"
],
"custom_graphql_hook": "zap/scripts/graphql_mutation_filter.js",
"operation_log_review": "excluded operations were not sent during dast-run-4821",
"keyword_only_filtering": false
},
"sandbox_and_seed_data": {
"payment": "sandbox-test-mode",
"email": "mail-sink",
"webhook": "local-webhook-capture",
"identity": "ephemeral-idp-tenant",
"seed_tenant": "dast-tenant-run-4821",
"seed_users": [
"dast-user-4821",
"dast-admin-4821"
],
"shared_records_in_scope": false
},
"reset_and_reconciliation": {
"cleanup_job": "dast-reset-4821",
"before_counts": "baseline-counts-captured",
"after_counts": "matched-baseline-after-cleanup",
"audit_events_reviewed": true,
"failed_cleanup_retry_policy": "retry-twice-then-block-promotion",
"rollback_evidence": "reset-log-dast-run-4821"
},
"compensating_validation": {
"excluded_mutations_validated": [
"refundOrder",
"rotateApiKey",
"deleteAccount"
],
"authorization_checks": "covered",
"input_validation_checks": "covered",
"business_logic_checks": "covered",
"residual_risk_decision": "not-required"
},
"expected_gate_results": [
{
"gate": "DAST-GQL-01",
"status": "Pass",
"evidence": "Schema hash and export timestamp match the deployed staging revision at scan time."
},
{
"gate": "DAST-GQL-02",
"status": "Pass",
"evidence": "Current mutation inventory lists owners, object types, side-effect classes, and touched integrations."
},
{
"gate": "DAST-GQL-03",
"status": "Pass",
"evidence": "Each mutation has an approved execute, dry-run, exclude, or manual validation decision."
},
{
"gate": "DAST-GQL-04",
"status": "Pass",
"evidence": "Scanner allowlist and GraphQL hook prevent excluded destructive operations from being sent."
},
{
"gate": "DAST-GQL-05",
"status": "Pass",
"evidence": "Payment, email, webhook, and identity side effects use sandbox integrations."
},
{
"gate": "DAST-GQL-06",
"status": "Pass",
"evidence": "All executable mutations target disposable per-run tenant and seed users."
},
{
"gate": "DAST-GQL-07",
"status": "Pass",
"evidence": "Cleanup job, before/after counts, audit review, and retry policy prove rollback."
},
{
"gate": "DAST-GQL-08",
"status": "Pass",
"evidence": "Excluded destructive mutations have compensating manual/API validation."
}
],
"expected_assessment": {
"overall_status": "Pass",
"risk_rating": "Low",
"confidence": "High"
}
}
Loading