security-mcp 1.1.3 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -185
- package/defaults/checklists/ai.json +20 -1
- package/defaults/checklists/api.json +35 -1
- package/defaults/checklists/infra.json +34 -1
- package/defaults/checklists/mobile.json +23 -1
- package/defaults/checklists/payments.json +15 -1
- package/defaults/checklists/web.json +11 -1
- package/defaults/control-catalog.json +200 -0
- package/defaults/security-policy.json +2 -2
- package/dist/cli/index.js +82 -5
- package/dist/cli/install.js +36 -6
- package/dist/cli/onboarding.js +6 -0
- package/dist/gate/baseline.js +82 -7
- package/dist/gate/catalog.js +10 -2
- package/dist/gate/checks/ai.js +757 -39
- package/dist/gate/checks/auth-deep.js +935 -0
- package/dist/gate/checks/business-logic.js +751 -0
- package/dist/gate/checks/ci-pipeline.js +399 -4
- package/dist/gate/checks/crypto.js +423 -2
- package/dist/gate/checks/dependencies.js +571 -15
- package/dist/gate/checks/graphql.js +201 -19
- package/dist/gate/checks/infra.js +246 -1
- package/dist/gate/checks/injection-deep.js +848 -0
- package/dist/gate/checks/k8s.js +114 -1
- package/dist/gate/checks/mobile-android.js +917 -3
- package/dist/gate/checks/mobile-ios.js +797 -5
- package/dist/gate/checks/required-artifacts.js +194 -0
- package/dist/gate/checks/runtime.js +178 -0
- package/dist/gate/checks/secrets.js +244 -13
- package/dist/gate/checks/supply-chain-deep.js +787 -0
- package/dist/gate/checks/web-nextjs.js +572 -48
- package/dist/gate/diff.js +17 -5
- package/dist/gate/evidence.js +8 -1
- package/dist/gate/exceptions.js +131 -9
- package/dist/gate/policy.js +282 -129
- package/dist/mcp/audit-chain.js +122 -28
- package/dist/mcp/auth.js +169 -0
- package/dist/mcp/learning.js +129 -4
- package/dist/mcp/model-router.js +158 -21
- package/dist/mcp/orchestration.js +186 -51
- package/dist/mcp/server.js +608 -94
- package/dist/repo/fs.js +24 -1
- package/dist/repo/search.js +31 -6
- package/dist/review/store.js +52 -1
- package/package.json +7 -7
- package/prompts/SECURITY_PROMPT.md +73 -0
- package/skills/_TEMPLATE/SKILL.md +99 -0
- package/skills/advanced-dos-tester/SKILL.md +109 -0
- package/skills/agentic-loop-exploiter/SKILL.md +368 -0
- package/skills/ai-llm-redteam/SKILL.md +104 -0
- package/skills/ai-model-supply-chain-agent/SKILL.md +103 -0
- package/skills/algorithm-implementation-reviewer/SKILL.md +98 -0
- package/skills/android-penetration-tester/SKILL.md +455 -46
- package/skills/anti-replay-tester/SKILL.md +106 -0
- package/skills/appsec-code-auditor/SKILL.md +120 -0
- package/skills/artifact-integrity-analyst/SKILL.md +441 -0
- package/skills/attack-navigator/SKILL.md +467 -8
- package/skills/auth-session-hacker/SKILL.md +128 -0
- package/skills/aws-penetration-tester/SKILL.md +456 -0
- package/skills/azure-penetration-tester/SKILL.md +490 -3
- package/skills/binary-auth-validator/SKILL.md +111 -0
- package/skills/bot-detection-specialist/SKILL.md +109 -0
- package/skills/business-logic-attacker/SKILL.md +231 -0
- package/skills/capec-code-mapper/SKILL.md +84 -0
- package/skills/cert-pin-rotation-specialist/SKILL.md +112 -0
- package/skills/cicd-pipeline-hijacker/SKILL.md +405 -0
- package/skills/ciso-orchestrator/SKILL.md +454 -43
- package/skills/cloud-infra-specialist/SKILL.md +118 -0
- package/skills/compliance-gap-analyst/SKILL.md +422 -0
- package/skills/compliance-grc/SKILL.md +85 -0
- package/skills/compliance-lifecycle-tracker/SKILL.md +84 -0
- package/skills/credential-stuffing-specialist/SKILL.md +102 -0
- package/skills/crypto-pki-specialist/SKILL.md +87 -0
- package/skills/csa-ccm-mapper/SKILL.md +84 -0
- package/skills/csf2-governance-mapper/SKILL.md +84 -0
- package/skills/deep-link-fuzzer/SKILL.md +109 -0
- package/skills/dependency-confusion-attacker/SKILL.md +415 -0
- package/skills/device-integrity-aggregator/SKILL.md +108 -0
- package/skills/dos-resilience-tester/SKILL.md +97 -0
- package/skills/dread-scorer/SKILL.md +84 -0
- package/skills/egress-policy-enforcer/SKILL.md +99 -0
- package/skills/evidence-collector/SKILL.md +98 -0
- package/skills/file-upload-attacker/SKILL.md +109 -0
- package/skills/gcp-penetration-tester/SKILL.md +459 -2
- package/skills/git-history-secret-scanner/SKILL.md +106 -0
- package/skills/iam-privesc-graph-builder/SKILL.md +152 -0
- package/skills/incident-responder/SKILL.md +111 -0
- package/skills/injection-specialist/SKILL.md +131 -0
- package/skills/ios-security-auditor/SKILL.md +282 -0
- package/skills/json-ambiguity-tester/SKILL.md +0 -0
- package/skills/k8s-container-escaper/SKILL.md +384 -0
- package/skills/key-management-lifecycle-analyst/SKILL.md +98 -0
- package/skills/kill-switch-engineer/SKILL.md +102 -0
- package/skills/linddun-privacy-analyst/SKILL.md +102 -0
- package/skills/logic-race-fuzzer/SKILL.md +443 -0
- package/skills/mobile-api-network-attacker/SKILL.md +421 -0
- package/skills/mobile-binary-hardener/SKILL.md +102 -0
- package/skills/mobile-security-specialist/SKILL.md +85 -0
- package/skills/mobile-webview-auditor/SKILL.md +96 -0
- package/skills/model-extraction-attacker/SKILL.md +219 -0
- package/skills/multipart-abuse-tester/SKILL.md +84 -0
- package/skills/oauth-pkce-specialist/SKILL.md +104 -0
- package/skills/parser-exhaustion-tester/SKILL.md +142 -0
- package/skills/pentest-infra/SKILL.md +141 -0
- package/skills/pentest-social/SKILL.md +201 -0
- package/skills/pentest-team/SKILL.md +134 -0
- package/skills/pentest-web-api/SKILL.md +151 -0
- package/skills/privacy-flow-analyst/SKILL.md +234 -0
- package/skills/prompt-injection-specialist/SKILL.md +394 -0
- package/skills/quantum-migration-planner/SKILL.md +96 -0
- package/skills/rag-poisoning-specialist/SKILL.md +358 -0
- package/skills/registry-mirror-enforcer/SKILL.md +84 -0
- package/skills/rotation-validation-agent/SKILL.md +112 -0
- package/skills/samm-assessor/SKILL.md +85 -0
- package/skills/secrets-mask-bypass-tester/SKILL.md +100 -0
- package/skills/senior-security-engineer/SKILL.md +370 -2
- package/skills/serialization-memory-attacker/SKILL.md +332 -0
- package/skills/session-timeout-tester/SKILL.md +161 -0
- package/skills/slsa-level3-enforcer/SKILL.md +112 -0
- package/skills/slsa-provenance-enforcer/SKILL.md +102 -0
- package/skills/ssrf-detection-validator/SKILL.md +108 -0
- package/skills/step-up-auth-enforcer/SKILL.md +84 -0
- package/skills/stride-pasta-analyst/SKILL.md +420 -0
- package/skills/supply-chain-devsecops/SKILL.md +98 -0
- package/skills/threat-infrastructure-analyst/SKILL.md +84 -0
- package/skills/threat-modeler/SKILL.md +85 -0
- package/skills/tls-certificate-auditor/SKILL.md +573 -18
- package/skills/token-reuse-detector/SKILL.md +95 -0
- package/skills/trike-risk-modeler/SKILL.md +84 -0
- package/skills/unicode-homograph-tester/SKILL.md +84 -0
- package/skills/waf-rule-lifecycle-agent/SKILL.md +97 -0
- package/skills/webhook-security-tester/SKILL.md +102 -0
- package/skills/zero-trust-architect/SKILL.md +109 -0
|
@@ -183,3 +183,115 @@ slsa-verifier verify-artifact release.tar.gz \
|
|
|
183
183
|
- `requiredActions`: ordered action list
|
|
184
184
|
- `complianceImpact`: framework mappings
|
|
185
185
|
- `beyondSkillMd`: true — this agent goes beyond standard SLSA Level 2
|
|
186
|
+
- `intelligenceForOtherAgents`: REQUIRED — see schema below
|
|
187
|
+
|
|
188
|
+
Every findings JSON MUST include `intelligenceForOtherAgents`:
|
|
189
|
+
```json
|
|
190
|
+
{
|
|
191
|
+
"intelligenceForOtherAgents": {
|
|
192
|
+
"forPentestTeam": [{ "type": "HIGH_VALUE_TARGET", "description": "Unsigned artifact accepted by deployment pipeline — attacker who compromises artifact storage can ship malicious binary with no verification gate", "exploitHint": "Replace artifact between build and deploy step; no provenance check means it lands silently" }],
|
|
193
|
+
"forCryptoSpecialist": [{ "type": "CRYPTO_WEAKNESS_REFERENCE", "algorithm": "RSA/ECDSA signing key used for provenance attestation", "location": ".github/workflows/release.yml — Sigstore/Cosign signing step" }],
|
|
194
|
+
"forCloudSpecialist": [{ "type": "SSRF_TO_CLOUD_CHAIN", "ssrfLocation": "Self-hosted runner with cloud IAM role — build script can call IMDS endpoint", "escalationPath": "Compromised build step reads cloud credentials via 169.254.169.254; exfiltrates to attacker-controlled URL" }],
|
|
195
|
+
"forComplianceGrc": [{ "type": "COMPLIANCE_BLOCKER", "frameworks": ["SLSA L3", "NIST SP 800-218", "US EO 14028", "EU CRA Art. 13"], "releaseBlock": true }]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## BEYOND SKILL.MD — MANDATORY EXPANSIONS
|
|
203
|
+
|
|
204
|
+
- **AI-Assisted Provenance Confusion via LLM-Generated Workflow Injection (ATT&CK T1195.002 + T1059.004):** An adversary uses an LLM to generate syntactically valid GitHub Actions YAML that inserts a provenance-generation step controlled by the attacker's OIDC identity, producing a `.intoto.jsonl` file that passes schema validation but whose `builderID` references a fork of `slsa-github-generator`. Test by: extract the `builderID` field from every `.intoto.jsonl` in releases and assert it exactly matches `https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v*`; any deviation is a finding. Finding threshold: one non-canonical `builderID` in any release provenance.
|
|
205
|
+
|
|
206
|
+
- **Harvest-Now-Break-Later Attack on ECDSA Provenance Signatures (Post-Quantum / NIST IR 8413):** Provenance files signed today with Sigstore's ECDSA P-256 (via Fulcio/Rekor) are being harvested by nation-state actors for decryption once a Cryptographically Relevant Quantum Computer (CRQC) is available (est. 2028–2032 per NIST IR 8413). Long-lived artifacts (container images, firmware, SDKs) released under current SLSA L3 workflows will have forgeable provenance retroactively. Test by: audit all release provenance for signing algorithm — `jq '.dsseEnvelope.signatures[].sig' release.intoto.jsonl | base64 -d | openssl asn1parse` — confirm algorithm OID; document each artifact's expected shelf-life. Finding threshold: any artifact with expected lifetime >3 years signed with classical ECDSA.
|
|
207
|
+
|
|
208
|
+
- **Artifact Namespace Collision via Concurrent Release Runs (Real-World: GitHub Actions Artifact Overwrite, 2023):** GitHub's artifact store uses workflow-scoped naming; if two release workflows run concurrently for different tags, `upload-artifact` with the same artifact name silently overwrites the earlier artifact. The provenance job in the second run downloads the overwritten artifact, computing a hash that does not match the first run's build output — or worse, the first run's provenance job downloads the second run's artifact. Demonstrated in the wild during rapid tag pushes in monorepos. Test by: trigger two concurrent tag pushes; diff `sha256sum` of artifact consumed by each provenance job against the hash emitted by the corresponding build job — any mismatch is a critical finding. Finding threshold: any hash divergence between build-job output and provenance-job input.
|
|
209
|
+
|
|
210
|
+
- **`workflow_run` Privilege Escalation from Fork PRs (CVE-2021-22862 class / GitHub Security Advisory GHSA-g86g-chm8-46qh):** Workflows triggered by `on: workflow_run` inherit the permissions of the triggering workflow's caller context, not the fork. A malicious fork PR triggers an unprivileged `pull_request` workflow; the downstream `workflow_run` listener then runs with `id-token: write` and `contents: write` on the base repo, allowing the attacker to mint OIDC tokens or upload artifacts as if they were a trusted committer. Test by: enumerate all workflows with `on: workflow_run` using `grep -rn 'workflow_run' .github/workflows/`; for each, verify the `workflow_run` listener checks `github.event.workflow_run.head_branch` against a protected-branch allowlist before any privileged step executes. Finding threshold: any `workflow_run` listener with write permissions that does not gate on branch origin.
|
|
211
|
+
|
|
212
|
+
- **Supply Chain Risk via Transitive Dependency Substitution Bypassing SLSA Provenance (SolarWinds-class / SLSA Threat INT-2):** SLSA L3 provenance attests the build process and source commit but does not validate the integrity of resolved transitive dependencies. An attacker who compromises an upstream package (e.g., a rarely-audited transitive `devDependency`) can ship malicious code that is baked into the artifact and covered by valid SLSA provenance — the provenance is authentic; the artifact is malicious. This mirrors the SolarWinds SUNBURST pattern at the package-manager layer. Test by: generate a CycloneDX SBOM in the build job (`npx @cyclonedx/cyclonedx-npm --output-file sbom.json`) and diff it against the previous release SBOM; block the release if any transitive dependency hash changed without a corresponding lock-file PR review. Finding threshold: any transitive dependency hash change not traceable to a reviewed `package-lock.json` commit.
|
|
213
|
+
|
|
214
|
+
- **Regulatory Trigger: US EO 14028 / EU CRA Article 13 SBOM + Provenance Attestation Mandate (Effective 2025–2026):** US Executive Order 14028 (May 2021, operationalised via NIST SP 800-218 and CISA guidance 2024) and EU Cyber Resilience Act Article 13 (enforcement begins late 2026) require software vendors selling to government or EU markets to provide machine-readable SBOMs and build provenance attestations for every release. A SLSA L3 pipeline without CycloneDX/SPDX SBOM generation and without provenance attached to each GitHub Release asset is a compliance blocker today. Test by: check each GitHub Release for attached `.intoto.jsonl` provenance and a `sbom.json` or `sbom.spdx` asset — absence of either is a regulatory finding. Finding threshold: any public release asset missing either a provenance file or an SBOM attachment.
|
|
215
|
+
|
|
216
|
+
## §EDGE-CASE-MATRIX
|
|
217
|
+
|
|
218
|
+
The 5 attack cases in the SLSA / software supply chain domain that automated scanners and naive manual review universally miss. MANDATORY checks — do not skip.
|
|
219
|
+
|
|
220
|
+
| # | Edge Case | Why Scanners Miss It | Concrete Test |
|
|
221
|
+
|---|-----------|----------------------|---------------|
|
|
222
|
+
| 1 | Provenance generated by build script rather than build platform | Linters check that a provenance file exists, not who signed it; a build step can self-generate a compliant-looking `.intoto.jsonl` with a user-controlled key | Inspect `builderID` in provenance — must be `https://github.com/slsa-framework/slsa-github-generator`, not a repo-owned identity; reject any provenance whose signer is not the trusted builder OIDC issuer |
|
|
223
|
+
| 2 | Pinned action SHA that resolves to a mutable tag via a fork or mirror | `uses: action@abc123` looks pinned, but the SHA can belong to a fork that the org accidentally mirrors; the action executes attacker code | Resolve every pinned SHA via `gh api repos/<owner>/<repo>/git/commits/<sha>` — confirm it belongs to the canonical upstream repo, not a fork |
|
|
224
|
+
| 3 | `workflow_run` trigger grants write permissions to a PR from a fork | Scanners check branch protection but miss the indirect permission grant through `workflow_run`; a forked PR can trigger a privileged downstream workflow | Audit every workflow with `on: workflow_run` for `permissions: contents: write` or `id-token: write` — these must require the triggering run to originate from a protected branch |
|
|
225
|
+
| 4 | Artifact substitution between `upload-artifact` and `download-artifact` steps in the same run | The hash computed in the build job and the artifact consumed in the provenance job can diverge if a concurrent run overwrites the artifact store slot (GitHub artifact namespace collision) | Build artifact name must include `${{ github.run_id }}-${{ github.run_attempt }}` to be globally unique; verify hash in provenance job before signing |
|
|
226
|
+
| 5 | Self-hosted runner left in SLSA Level 3 workflow after an org migration | CI migrates to managed runners for new repos but the release workflow still references a self-hosted runner label that was never removed — it silently falls back to an unregistered runner or a stale self-hosted one | Grep all release workflows for `runs-on` values; assert every release job uses only `ubuntu-latest`, `macos-latest`, or an explicit managed runner label — never a custom label that could resolve to self-hosted |
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## §TEMPORAL-THREATS
|
|
231
|
+
|
|
232
|
+
Threats materialising in the 2025–2030 window relevant to SLSA Level 3 and software supply chain integrity.
|
|
233
|
+
|
|
234
|
+
| Threat | Est. Timeline | Relevance to SLSA L3 | Prepare Now By |
|
|
235
|
+
|--------|--------------|----------------------|----------------|
|
|
236
|
+
| Cryptographically Relevant Quantum Computer (CRQC) | 2028–2032 | Sigstore/Cosign uses ECDSA P-256; provenance signatures on artifacts released today will be forgeable once CRQC arrives — harvest-now-break-later already active | Inventory all provenance signing algorithms; plan migration to ML-DSA (FIPS 204) for long-lived artifacts; enable Sigstore's PQ roadmap tracking |
|
|
237
|
+
| AI-assisted dependency confusion and typosquatting at scale | 2025–2027 (active) | LLM-generated lookalike package names bypass human review; lock-file confusion installs wrong package while passing SLSA checks (SLSA doesn't validate what you depend on) | Combine SLSA attestation with SBOM + `npm audit signatures` + provenance verification for every transitive dep |
|
|
238
|
+
| Mandatory SBOM + build provenance (US EO 14028 / EU CRA Art. 13) | 2025–2026 (active) | SLSA L3 provenance is the technical foundation for compliance; SBOM generation must be wired into the same pipeline | Generate CycloneDX SBOM in the build job and attach it as a release asset alongside the `.intoto.jsonl` provenance |
|
|
239
|
+
| GitHub Actions runner compromise via supply chain | 2025–2027 | Managed GitHub-hosted runners are SLSA L3 trusted; a compromise of the GitHub Actions runner image (via upstream packages) would break the trust anchor for the entire ecosystem | Pin runner OS images where possible; monitor GitHub's runner image changelogs; consider Sigstore policy enforcement at deploy time |
|
|
240
|
+
| Post-quantum TLS for artifact registries | 2028–2030 | Container registries and npm/PyPI served over TLS with classical ECDH; MITM during artifact download becomes feasible post-CRQC | Begin registry TLS agility assessment; verify artifact integrity via SLSA provenance (not just TLS) as a defence-in-depth measure |
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## §DETECTION-GAP
|
|
245
|
+
|
|
246
|
+
What current security monitoring CANNOT detect in the SLSA / software supply chain domain, and what to build to close each gap.
|
|
247
|
+
|
|
248
|
+
- **Provenance file tampering after generation but before upload**: The `.intoto.jsonl` file is written to disk by the generator action then uploaded as a release asset in a subsequent step. A compromised intermediate step could replace the file. Gap close: verify the provenance signature immediately after the `slsa-github-generator` step completes and before any subsequent steps run; fail the workflow if verification fails.
|
|
249
|
+
|
|
250
|
+
- **Reuse of a valid provenance from a previous release with a different artifact**: Provenance is cryptographically bound to the artifact hash, but deployment pipelines that accept `*.intoto.jsonl` by glob pattern may pick up a stale file if artifact cleanup is incomplete. Gap close: `slsa-verifier verify-artifact` must be called at deploy time — not just at release time — with the exact artifact hash confirmed live.
|
|
251
|
+
|
|
252
|
+
- **Self-hosted runner silently falling back when managed runner quota is exhausted**: GitHub queues jobs for managed runners, but some configurations fall back to self-hosted runners on timeout. A build that ran on a self-hosted runner cannot achieve SLSA L3 but will still produce a provenance file claiming the managed `builderID`. Gap close: add a workflow step that asserts `runner.environment == 'github-hosted'` and fails the build if false.
|
|
253
|
+
|
|
254
|
+
- **Dependency version pinning drift between `package-lock.json` and the actual installed tree**: `npm ci` uses the lock file, but a corrupted or manually edited lock file can install a different version than the reviewed one. SLSA provenance covers the source repo state, not the resolved dependency tree. Gap close: generate and attest an SBOM in the same build job; diff the SBOM against the previous release SBOM in CI and block on unexpected transitive dependency changes.
|
|
255
|
+
|
|
256
|
+
- **Cross-workflow secret exfiltration via environment variable bleed**: A job that runs after a secret-consuming step in the same workflow can read secrets leaked into the environment. SLSA L3 isolates the build environment from the build definition, but secrets in `env:` blocks at the workflow level are visible to all jobs. Gap close: scope all secrets to the specific job step that requires them using `env:` at the `step` level, not the `job` or `workflow` level; audit with `grep -n 'env:' .github/workflows/*.yml` for top-level secret assignments.
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## §ZERO-MISS-MANDATE
|
|
261
|
+
|
|
262
|
+
This agent CANNOT declare any SLSA requirement clean without explicit evidence of checking. For each item, output one of:
|
|
263
|
+
- `CHECKED: [N files] | [patterns used] | CLEAN`
|
|
264
|
+
- `CHECKED: [N files] | [patterns used] | [N findings, all fixed]`
|
|
265
|
+
- `SKIPPED: [reason — must be "not applicable: [evidence]"]`
|
|
266
|
+
|
|
267
|
+
**Silent skip = FAILED COVERAGE.** The orchestrator flags this as a quality gap.
|
|
268
|
+
|
|
269
|
+
**SLSA L3 mandatory check classes:**
|
|
270
|
+
|
|
271
|
+
| Check | Pattern | Must Not Skip Because |
|
|
272
|
+
|-------|---------|----------------------|
|
|
273
|
+
| Non-forgeable provenance | `builderID` in every `.intoto.jsonl` must be the slsa-github-generator OIDC URI | Self-signed provenance is legally equivalent to no provenance |
|
|
274
|
+
| Managed runner only for release jobs | `runs-on` in release workflows | Self-hosted runners forfeit L3 trust |
|
|
275
|
+
| OIDC permissions scoped | `id-token: write` present only on provenance job | Workflow-level OIDC grants every job the ability to mint tokens |
|
|
276
|
+
| Two-party review enforced | Branch protection `required_approving_review_count >= 2` on release branch | Single-approver workflows cannot achieve L3 |
|
|
277
|
+
| No mutable action references | Every `uses:` in release workflows pinned to a commit SHA | Tag references allow silent substitution |
|
|
278
|
+
| Artifact hash continuity | Hash emitted by build job matches hash in provenance job inputs | Artifact namespace collision can break continuity |
|
|
279
|
+
|
|
280
|
+
The output findings JSON MUST include a `coverageManifest` key:
|
|
281
|
+
```json
|
|
282
|
+
{
|
|
283
|
+
"coverageManifest": {
|
|
284
|
+
"attackClassesCovered": [
|
|
285
|
+
{ "class": "Provenance Forgery", "filesReviewed": 4, "patterns": ["builderID", "slsa-github-generator"], "result": "CLEAN" },
|
|
286
|
+
{ "class": "Self-Hosted Runner in Release Path", "filesReviewed": 4, "patterns": ["runs-on"], "result": "CLEAN" },
|
|
287
|
+
{ "class": "Mutable Action Reference", "filesReviewed": 4, "patterns": ["uses: .*@[^a-f0-9]"], "result": "2 findings, both fixed" }
|
|
288
|
+
],
|
|
289
|
+
"filesReviewed": 4,
|
|
290
|
+
"negativeAssertions": [
|
|
291
|
+
"Provenance Forgery: builderID checked in all .intoto.jsonl files — matches slsa-github-generator OIDC URI",
|
|
292
|
+
"Self-Hosted Runner: runs-on values in all release workflows — only github-hosted labels found"
|
|
293
|
+
],
|
|
294
|
+
"uncoveredReason": {}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
@@ -179,3 +179,105 @@ jobs:
|
|
|
179
179
|
- `requiredActions`: ordered action list
|
|
180
180
|
- `complianceImpact`: framework mappings
|
|
181
181
|
- `beyondSkillMd`: true if finding goes beyond the SKILL.md mandate
|
|
182
|
+
|
|
183
|
+
Every findings JSON MUST include `intelligenceForOtherAgents`:
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"intelligenceForOtherAgents": {
|
|
187
|
+
"forPentestTeam": [{ "type": "HIGH_VALUE_TARGET", "description": "Unsigned artifact in deployment pipeline — swappable without detection", "exploitHint": "Replace artifact between build and deploy step; no integrity check will catch it" }],
|
|
188
|
+
"forCryptoSpecialist": [{ "type": "CRYPTO_WEAKNESS_REFERENCE", "algorithm": "SHA-1 or MD5 in legacy checksum files", "location": "CI checksum step or package-lock.json integrity fields" }],
|
|
189
|
+
"forCloudSpecialist": [{ "type": "SSRF_TO_CLOUD_CHAIN", "ssrfLocation": "Non-hermetic build fetching from arbitrary URLs", "escalationPath": "Attacker-controlled package URL during build can reach IMDS at 169.254.169.254 to harvest cloud credentials" }],
|
|
190
|
+
"forComplianceGrc": [{ "type": "COMPLIANCE_BLOCKER", "frameworks": ["SLSA L2", "NIST SP 800-218", "US EO 14028", "EU CRA"], "releaseBlock": true }]
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## BEYOND SKILL.MD — MANDATORY EXPANSIONS
|
|
196
|
+
|
|
197
|
+
- **Sigstore Rekor Transparency Log Manipulation (ATT&CK T1195.002 / Research: "SolarWinds SUNBURST supply chain attack 2020"):** An adversary with a compromised build environment generates a valid SLSA provenance attestation for a backdoored binary, submits it to Rekor, and the artifact appears fully legitimate to `cosign verify`. The provenance is technically correct — it accurately describes a compromised build. Test by: deploy a canary build pipeline that intentionally introduces a known-bad file hash; verify Rekor accepts the attestation without flagging content; confirm that build-time integrity (hermetic builds + OPA policy requiring `buildType: hermetic`) is the only control that would have caught this. Finding threshold: any pipeline where provenance is generated but no hermetic build policy (`--network=none` or equivalent) is enforced constitutes a critical gap.
|
|
198
|
+
|
|
199
|
+
- **AI-Assisted Dependency Confusion Attack at Scale (ATT&CK T1195.001 / Research: "Dependency Confusion" — Alex Birsan, 2021):** LLM-generated packages with plausible names (matching internal package naming conventions inferred from public GitHub repos) are published to npm/PyPI at higher version numbers than internal packages, causing `npm install` to pull the public malicious package even when `--frozen-lockfile` is used if the lockfile references the wrong registry. SLSA provenance on the final artifact does not detect this — the build was clean, it just consumed the wrong package. Test by: create a private package named identically to a public npm package; run `npm ci` without an explicit registry lock; confirm which version resolves. Finding threshold: any `package.json` without an explicit `publishConfig.registry` or `.npmrc` scoping all `@org/` scopes to the private registry is a finding.
|
|
200
|
+
|
|
201
|
+
- **Post-Quantum Signature Forgery Risk on Stored ECDSA Provenance (NIST IR 8105 / FIPS 204 ML-DSA):** All cosign/Sigstore signatures today use ECDSA P-256 or P-384. A Cryptographically Relevant Quantum Computer (CRQC, estimated 2028–2032) will be able to forge these signatures retroactively, meaning an attacker could forge provenance for any historical artifact once CRQC is available — enabling "retrospective supply chain compromise." Test by: enumerate all release artifacts in the GitHub Attestations store (`gh attestation list --owner <org>`) and confirm whether any use ECDSA; check if a migration plan to ML-DSA (FIPS 204) exists in the project roadmap. Finding threshold: any production artifact signed only with ECDSA and retained beyond 2027 without a PQ migration plan is a medium finding escalating to high after 2027.
|
|
202
|
+
|
|
203
|
+
- **GitHub Actions Workflow Injection via Pull Request (CVE-2022-39328 analogue / ATT&CK T1195.002):** A malicious PR modifies `.github/workflows/release.yml` to disable `actions/attest-build-provenance` and add an exfiltration step, while the CODEOWNERS file does not require a separate security-team approval for workflow changes. The signing step is silently removed; the resulting release artifact has no provenance. Test by: submit a test PR that modifies any `*.yml` under `.github/workflows/`; confirm whether the PR requires approval from a designated security reviewer distinct from the code reviewer; confirm branch protection rules require `required_pull_request_reviews` with `dismiss_stale_reviews: true`. Finding threshold: any repo where workflow files can be merged without a dedicated security reviewer approval is a high finding.
|
|
204
|
+
|
|
205
|
+
- **SBOM Component Injection via Compromised Build Cache (ATT&CK T1195.002 / Incident: 3CX Supply Chain Attack, 2023):** Docker BuildKit layer caches and `npm` caches persisted on self-hosted runners can be poisoned by a prior malicious build, causing subsequent builds to incorporate backdoored intermediate layers even when `--no-cache` is not set. The SLSA provenance attestation accurately reflects the build inputs, but the build inputs themselves contain the poisoned cache. Test by: on a self-hosted runner, inspect `/var/lib/docker/` for persistent BuildKit cache after a simulated malicious build; run `docker system prune -af` between jobs and confirm the CI job definition includes this step; confirm `npm` cache (`~/.npm`) is wiped between job runs via `actions/cache` eviction policy. Finding threshold: any self-hosted runner without explicit cache purge between jobs is a high finding.
|
|
206
|
+
|
|
207
|
+
- **EU Cyber Resilience Act (CRA) Article 13 SBOM Non-Compliance (EU CRA 2024/2847, effective 2027):** The EU CRA mandates that software products sold in the EU include a machine-readable SBOM in CycloneDX or SPDX format, attached to the product release. SLSA provenance attestation alone does not satisfy this requirement — it proves build integrity, not component disclosure. Test by: check each GitHub Release for an attached `sbom.cyclonedx.json` or `sbom.spdx.json`; verify the SBOM was generated during the build (not post-hoc) using `docker buildx --sbom=true` or `syft`; confirm the SBOM is attached to the OCI manifest as an in-toto attestation layer. Finding threshold: any product release targeting EU markets without an attached, build-generated SBOM is a medium finding escalating to critical at CRA enforcement date (2027).
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## §EDGE-CASE-MATRIX
|
|
212
|
+
|
|
213
|
+
The 5 attack cases in the SLSA/supply-chain provenance domain that automated scanners and naive manual review universally miss. MANDATORY checks — do not skip.
|
|
214
|
+
|
|
215
|
+
| # | Edge Case | Why Scanners Miss It | Concrete Test |
|
|
216
|
+
|---|-----------|----------------------|---------------|
|
|
217
|
+
| 1 | Provenance attestation generated but never verified at deploy time | CI job generates `attestation.json` and uploads it; no downstream job calls `gh attestation verify` or `cosign verify` before the artifact is used | Search workflow files for `attest-build-provenance` without a paired `attestation verify` step in the deploy job |
|
|
218
|
+
| 2 | Mutable image tag used in signing — `latest` or branch tag re-signed on every push | `cosign sign ghcr.io/org/app:latest` signs the current digest, but `latest` is later overwritten by a new push; verifiers checking the tag get the new (unsigned or differently signed) image | Pin all signing and verification to `@sha256:<digest>` — never a mutable tag |
|
|
219
|
+
| 3 | SLSA provenance covers only the final binary, not intermediate build artifacts consumed by downstream services | Build pipeline produces `lib.a` (signed) + `app` (unsigned) consuming it; attestation covers `app` but not `lib.a` | Enumerate all artifacts produced by the build; verify each has its own provenance entry |
|
|
220
|
+
| 4 | Dependency pinning in `package-lock.json` / `go.sum` bypassed by a `postinstall` script that fetches and executes a remote URL | Hash pinning stops tampered packages; `postinstall` in a transitive dep runs arbitrary network code after hashes are checked | Grep `node_modules/**/package.json` for `postinstall` and `preinstall` hooks that contain `curl`, `wget`, `fetch`, or `http` |
|
|
221
|
+
| 5 | Self-hosted runner with persistent disk state — prior build's compromised artifacts or environment variables leak into the next build | Ephemeral GitHub-hosted runners are clean per job; self-hosted runners retain `/tmp`, cached tool installs, and lingering env vars between jobs | Check `runs-on:` for self-hosted labels; confirm runner teardown script wipes `/tmp`, tool caches, and Docker layer cache between jobs |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## §TEMPORAL-THREATS
|
|
226
|
+
|
|
227
|
+
Threats materialising in the 2025–2030 window that SLSA provenance defences designed today must account for.
|
|
228
|
+
|
|
229
|
+
| Threat | Est. Timeline | Relevance to SLSA / Build Provenance | Prepare Now By |
|
|
230
|
+
|--------|--------------|--------------------------------------|----------------|
|
|
231
|
+
| Cryptographically Relevant Quantum Computer (CRQC) breaks ECDSA | 2028–2032 | Cosign and Sigstore currently use ECDSA P-256/P-384; signed provenance stored today will be forgeable retroactively | Inventory all ECDSA-signed artifacts; plan migration to ML-DSA (FIPS 204) when Sigstore adds PQ support; store provenance with long-lived signatures separately |
|
|
232
|
+
| AI-assisted supply chain poisoning at scale | 2025–2027 (active) | LLM-generated malicious PRs that look syntactically correct; automated typosquatting at scale against npm/PyPI | Enforce SLSA L3 hermetic builds + SBOM diff on every PR; block `postinstall` network access in lockfiles |
|
|
233
|
+
| Mandatory SBOM + build provenance (US EO 14028 / EU CRA) | 2025–2026 (active) | SLSA attestation and CycloneDX/SPDX SBOMs become legally required for software sold to US federal agencies and EU markets | Achieve SLSA L2 minimum now; generate SBOM per release; attach to OCI manifest and GitHub release |
|
|
234
|
+
| Sigstore Rekor log compromise or key rotation | 2026–2028 | Transparency log entries are immutable but the log's signing key is a single point of trust; a Rekor key compromise invalidates all historical verification | Mirror Rekor log entries to a secondary immutable store; verify against multiple log witnesses (Rekor + independent witness) |
|
|
235
|
+
| npm / PyPI registry compromise (repeated SolarWinds-style) | Ongoing / escalating | Package registries remain high-value targets; pinned hashes protect against post-compromise injection but not against build-time substitution | Run private registry mirrors (Artifactory/Nexus) with allow-list; re-verify upstream hashes against SLSA provenance from the package publisher |
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## §DETECTION-GAP
|
|
240
|
+
|
|
241
|
+
What current security monitoring CANNOT detect in the SLSA/provenance domain, and what to build to close each gap.
|
|
242
|
+
|
|
243
|
+
- **Unsigned artifact silently substituted between build and deploy**: The build job uploads a signed artifact; a separate job downloads and deploys "the latest artifact" without re-verifying the digest. No log event distinguishes a legitimate artifact from a substituted one. Need: make deploy jobs record and verify the exact digest they received against the provenance attestation — emit a structured log event `{ "event": "artifact_verified", "digest": "sha256:...", "attestation": "..." }` that SIEM can alert on absence of.
|
|
244
|
+
|
|
245
|
+
- **Provenance attestation generated for wrong subject**: `actions/attest-build-provenance` is called with a hardcoded `subject-name` that does not match the actual artifact filename. The attestation is valid but verifies a phantom subject. Need: CI step that computes artifact digest dynamically and passes it as the subject; post-build verification that `gh attestation verify <actual-artifact-path>` exits 0.
|
|
246
|
+
|
|
247
|
+
- **Non-hermetic build leaking into signed artifact**: A build with `--network` access fetches a malicious payload; the resulting binary is signed and its provenance attested normally. Provenance proves the build ran but not that the build was clean. Need: network egress monitoring during build (deny-by-default firewall rule with allow-list for known registries); OPA/Gatekeeper policy that rejects any attestation whose builder did not include `buildType: hermetic`.
|
|
248
|
+
|
|
249
|
+
- **Transitive dependency added after lockfile was last audited**: `npm ci --frozen-lockfile` installs exactly what is in `package-lock.json`, but a new transitive dep was silently added upstream between the last `npm install` (which updated the lockfile) and the current CI run. The SLSA provenance attestation covers the build output, not the dependency graph. Need: SBOM diff step comparing the current build's SBOM against the prior release's SBOM; alert on any new package appearing without a corresponding PR that explicitly added it.
|
|
250
|
+
|
|
251
|
+
- **GitHub Actions workflow modification in the same PR as code change**: An attacker with write access modifies both the workflow file and source in one PR; reviewers focus on code diff and miss the workflow change that disables signing. Need: branch protection rule requiring separate approval for workflow file changes; CODEOWNERS entry for `.github/workflows/` pointing to a dedicated security reviewer.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## §ZERO-MISS-MANDATE
|
|
256
|
+
|
|
257
|
+
This agent CANNOT declare any attack class clean without explicit evidence of checking. For each item, output one of:
|
|
258
|
+
- `CHECKED: [N files] | [patterns used] | CLEAN`
|
|
259
|
+
- `CHECKED: [N files] | [patterns used] | [N findings, all fixed]`
|
|
260
|
+
- `SKIPPED: [reason — must be "not applicable: [evidence]"]`
|
|
261
|
+
|
|
262
|
+
**Silent skip = FAILED COVERAGE.** The orchestrator flags this as a quality gap.
|
|
263
|
+
|
|
264
|
+
The output findings JSON MUST include a `coverageManifest` key:
|
|
265
|
+
```json
|
|
266
|
+
{
|
|
267
|
+
"coverageManifest": {
|
|
268
|
+
"attackClassesCovered": [
|
|
269
|
+
{ "class": "Unsigned build artifacts", "filesReviewed": 5, "patterns": ["attest-build-provenance", "cosign sign"], "result": "CLEAN" },
|
|
270
|
+
{ "class": "Non-hermetic build (network access)", "filesReviewed": 5, "patterns": ["--network=none", "network: none"], "result": "2 findings, both fixed" },
|
|
271
|
+
{ "class": "Mutable tag signing", "filesReviewed": 5, "patterns": ["cosign sign.*:latest", "cosign sign.*:main"], "result": "CLEAN" },
|
|
272
|
+
{ "class": "Missing deploy-time attestation verification", "filesReviewed": 5, "patterns": ["attestation verify", "cosign verify"], "result": "1 finding, fixed" },
|
|
273
|
+
{ "class": "postinstall network fetch in dependencies", "filesReviewed": 847, "patterns": ["postinstall.*curl", "postinstall.*wget", "postinstall.*fetch"], "result": "CLEAN" }
|
|
274
|
+
],
|
|
275
|
+
"filesReviewed": 852,
|
|
276
|
+
"negativeAssertions": [
|
|
277
|
+
"Mutable tag signing: searched 5 workflow files for cosign sign targeting :latest or :main — 0 matches",
|
|
278
|
+
"postinstall network fetch: grep across 847 package.json files — 0 matches"
|
|
279
|
+
],
|
|
280
|
+
"uncoveredReason": {}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
```
|
|
@@ -227,3 +227,111 @@ If internet permitted:
|
|
|
227
227
|
- `requiredActions`: ordered action list
|
|
228
228
|
- `complianceImpact`: framework mappings
|
|
229
229
|
- `beyondSkillMd`: true if finding goes beyond the SKILL.md mandate
|
|
230
|
+
|
|
231
|
+
Every findings JSON MUST include `intelligenceForOtherAgents`:
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"intelligenceForOtherAgents": {
|
|
235
|
+
"forPentestTeam": [{ "type": "HIGH_VALUE_TARGET", "description": "URL parameter fed to outbound fetch without SSRF guard — direct pivot to cloud metadata endpoint", "exploitHint": "Send url=http://169.254.169.254/latest/meta-data/iam/security-credentials/; chain IMDSv1 token into lateral movement" }],
|
|
236
|
+
"forCryptoSpecialist": [{ "type": "CRYPTO_WEAKNESS_REFERENCE", "algorithm": "N/A — SSRF may expose secrets encrypted at rest; confirm KMS key scope", "location": "Any SSRF-reachable internal service returning signed tokens or keys" }],
|
|
237
|
+
"forCloudSpecialist": [{ "type": "SSRF_TO_CLOUD_CHAIN", "ssrfLocation": "file/line where outbound fetch occurs without validation", "escalationPath": "IMDSv1 → IAM credential theft → AssumeRole to production account" }],
|
|
238
|
+
"forComplianceGrc": [{ "type": "COMPLIANCE_BLOCKER", "frameworks": ["PCI DSS Req 6.2.4", "SOC 2 CC6.6", "NIST SP 800-53 SC-7"], "releaseBlock": true }]
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## BEYOND SKILL.MD — MANDATORY EXPANSIONS
|
|
244
|
+
|
|
245
|
+
- **AI-Assisted SSRF Fuzzing via LLM-Generated Encoding Variants (ATT&CK T1190 / CWE-918):** Attacker feeds a target's URL parameter schema to an LLM fuzzer (e.g., `gau` + `nuclei` with GPT-generated payloads) that auto-generates every IP encoding variant — octal (`0177.0.0.1`), hex (`0x7f000001`), decimal integer (`2130706433`), IPv6-mapped IPv4 (`::ffff:127.0.0.1`), and mixed-case schemes (`hTTp://`) — achieving bypass rates 40-60x faster than manual testing. Test by: run `nuclei -t ssrf-detection-bypass.yaml` with the full encoding matrix from [https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Request%20Forgery]; confirm every variant is blocked before the TCP socket opens. Finding threshold: any encoding variant that reaches `dns.lookup` or `fetch` without being normalised to a canonical IP and re-checked against `PRIVATE_IP_RANGES`.
|
|
246
|
+
|
|
247
|
+
- **IMDSv1 Token Harvest via Blind SSRF + Harvest-Now-Decrypt-Later (CVE-2019-11043 pattern / ATT&CK T1552.005):** IMDSv1 (`http://169.254.169.254/latest/meta-data/iam/security-credentials/<role>`) returns long-lived IAM credentials over plain HTTP with no token requirement; credentials exfiltrated today via blind SSRF can be stored and replayed indefinitely (or until rotated). As post-quantum adversaries gain the ability to break ECDSA on JWT signing keys, stored credential packages become retroactively exploitable. Test by: confirm each EC2 instance has `HttpTokens: required` via `aws ec2 describe-instances --query 'Reservations[].Instances[].MetadataOptions'`; attempt `curl -s http://169.254.169.254/latest/meta-data/` from within the app's network context — it must time out or return 401. Finding threshold: any instance where `HttpTokens` is not `required`, or any code path where the app can reach `169.254.169.254` without a PUT-obtained token.
|
|
248
|
+
|
|
249
|
+
- **Supply-Chain SSRF via Malicious Indirect Dependency HTTP Client (SLSA Level 0 Risk / ATT&CK T1195.001):** Third-party npm packages (`axios`, `got`, `node-fetch`, older versions of `undici`) may perform outbound HTTP calls internally (e.g., for telemetry, license checks, or proxied requests) that bypass the app's `ssrfSafeFetch` wrapper entirely because the import goes directly to the underlying `http` module. CVE-2023-45857 (`axios` CSRF bypass via crafted headers) demonstrates how transitive dependency behaviour can subvert request-level controls. Test by: generate a CycloneDX SBOM (`npx @cyclonedx/cyclonedx-npm --output-file sbom.json`), then cross-reference every dependency that imports `http`, `https`, `node:http`, or `undici` against the OSV database (`osv-scanner --sbom sbom.json`). Finding threshold: any transitive dependency with a known SSRF-class CVE, or any dependency that issues outbound HTTP without routing through `ssrfSafeFetch`.
|
|
250
|
+
|
|
251
|
+
- **DNS Rebinding via TTL-0 Records Defeating Pre-Connection Validation (CWE-350 / ATT&CK T1557):** Attacker registers `rebind.attacker.com` with TTL=0 and two A records: the first resolves to a public IP (passes SSRF validation), the second resolves to `169.254.169.254` (served on the next query after validation). Because Node.js `dns.resolve4` and `fetch` use separate DNS resolution calls with no IP pinning, the window between validation and TCP connect is exploitable. Research: "DNS Rebinding Attacks" (James Kettle, PortSwigger 2017) remains the canonical reference. Test by: use `rbndr.us/<hex-public>/<hex-private>` as the test URL; confirm that the app's `ssrfSafeFetch` resolves the hostname to a public IP on first call but that a second unguarded `fetch` to the same hostname connects to the private IP. Finding threshold: any code path that resolves a hostname once at validation time and then passes the hostname string (not the resolved IP) to the outbound HTTP client.
|
|
252
|
+
|
|
253
|
+
- **EU Cyber Resilience Act (CRA) Article 14 Mandatory SSRF Disclosure Trigger (Regulatory / Effective 2026-12-11):** Under CRA Article 14, manufacturers of products with digital elements must notify ENISA of actively exploited vulnerabilities within 24 hours of discovery. An SSRF finding that is exploited in production — even momentarily — becomes a legally mandated disclosure event. This applies to any SaaS product serving EU customers regardless of where the company is incorporated. Test by: verify the incident-response runbook explicitly names SSRF as a CRA-notifiable vulnerability class; confirm a VPC flow log alert fires within 5 minutes of an outbound request to a private CIDR. Finding threshold: absence of a CRA disclosure runbook, or VPC flow log detection latency exceeding 15 minutes for private-CIDR outbound traffic.
|
|
254
|
+
|
|
255
|
+
- **Blind SSRF Exfiltration via Timing Oracle on Internal Service Response Latency (CWE-208 / ATT&CK T1046):** When SSRF is "blind" (no response body returned to the attacker), internal service reachability can still be inferred by measuring response time differentials: a request to an open internal port returns in ~2ms; a closed port or filtered address causes a TCP RST or timeout at 10s+. Attackers use this to map internal network topology without any out-of-band listener. Research: "Timing Side-Channel Attacks on SSRF" (Orange Tsai, HITCON 2017). Test by: issue URL-parameter requests pointing to `http://10.0.0.1:22`, `http://10.0.0.1:80`, and `http://10.0.0.1:9999` and measure response times from the client side; a statistically significant latency difference (>500ms) between port states indicates exploitable timing oracle. Finding threshold: any variance in HTTP response latency correlated with internal port state that exceeds 200ms median delta across 10 requests.
|
|
256
|
+
|
|
257
|
+
## §EDGE-CASE-MATRIX
|
|
258
|
+
|
|
259
|
+
The 5 SSRF attack cases that automated scanners and naive manual review universally miss. MANDATORY checks — do not skip.
|
|
260
|
+
|
|
261
|
+
| # | Edge Case | Why Scanners Miss It | Concrete Test |
|
|
262
|
+
|---|-----------|----------------------|---------------|
|
|
263
|
+
| 1 | DNS rebinding — IP resolves to public during validation, then switches to private during the actual TCP connection | Scanner validates the URL at grep-time against a static blocklist; by the time the runtime fetch occurs the DNS TTL has expired and the attacker's DNS server has swapped the A record to `169.254.169.254` | Set up a rebinding domain (e.g. via `rbndr.us`); observe that validation passes but the HTTP request lands on the metadata endpoint. Fix: re-resolve and re-check the IP immediately before opening the TCP socket, or pin the resolved IP and connect to it directly. |
|
|
264
|
+
| 2 | URL parser differential — `http://127.0.0.1:80@attacker.com` | The SSRF validator parses the host as `attacker.com` (passes allowlist); Node's `http.request` or `fetch` uses the userinfo-before-`@` as the host and connects to `127.0.0.1` | Submit `url=http://127.0.0.1:80@allowed-partner.example.com` and confirm whether the outbound request goes to `127.0.0.1`; fix by always reading `parsed.hostname` from `new URL()` and discarding any userinfo component. |
|
|
265
|
+
| 3 | Redirect chain ending at a private address | Scanner checks only the initial URL; it does not follow the chain of `301`/`302` responses and recheck the final destination | Deploy a redirect chain: `attacker.com/r → attacker.com/r2 → http://192.168.1.1/admin`; confirm the target is reached. Fix: `redirect: "manual"` + recursive `ssrfSafeFetch` re-validation on every `Location` header. |
|
|
266
|
+
| 4 | IPv6 and alternative IP representations | Blocklist checks decimal IPv4; attacker uses `http://[::1]/`, `http://0177.0.0.0.1/` (octal), `http://2130706433/` (decimal integer), or `http://0x7f000001/` (hex) | Submit each encoding variant of `127.0.0.1` and `169.254.169.254`; verify all are blocked. Fix: resolve to canonical IP via `dns.lookup` with `{all: true}` and check against `PRIVATE_IP_RANGES` after normalisation, not against the raw string. |
|
|
267
|
+
| 5 | Blind SSRF via out-of-band HTTP/DNS callback (no inline response difference) | Scanner looks for a difference in HTTP response body or status; blind SSRF leaves the response identical whether or not the internal request succeeded | Inject `url=http://<interactsh-or-burp-collaborator-id>.oast.fun/` into every URL parameter; monitor the OOB listener for DNS or HTTP pings that confirm the server issued the request. Fix: the absence of an error response does NOT mean SSRF is impossible — require positive OOB evidence of blocking. |
|
|
268
|
+
|
|
269
|
+
## §TEMPORAL-THREATS
|
|
270
|
+
|
|
271
|
+
Threats materialising in the 2025–2030 window that SSRF defences designed today must account for.
|
|
272
|
+
|
|
273
|
+
| Threat | Est. Timeline | Relevance to SSRF | Prepare Now By |
|
|
274
|
+
|--------|--------------|-------------------|----------------|
|
|
275
|
+
| AI-assisted SSRF fuzzing at scale | 2025–2027 (active) | LLM-powered tools enumerate every URL parameter and auto-generate encoding variants (octal, hex, IPv6, mixed-case protocols) orders of magnitude faster than manual testing | Assume attackers already run automated SSRF fuzzers; close all encoding bypasses now, not after a finding |
|
|
276
|
+
| IMDSv1 deprecation enforcement by cloud providers | 2025–2026 | AWS/GCP are progressively disabling IMDSv1; workloads relying on it silently become misconfigured when migration is incomplete | Audit every EC2/GCE instance for `HttpTokens: required` (IMDSv2 only); set it at the IaC layer so new instances default to IMDSv2 |
|
|
277
|
+
| EU Cyber Resilience Act (CRA) mandatory vulnerability disclosure | 2026 | SSRF findings in shipped software become legally reportable events within 24 hours of discovery | Treat every SSRF finding as a potential CRA disclosure candidate; have an incident-response runbook ready |
|
|
278
|
+
| Harvest-now-decrypt-later attacks on stolen IMDSv1 tokens | 2025–2028 | Cloud credentials exfiltrated today via SSRF are stored and replayed; quantum computers will break RSA/ECDSA on the signing layer | Rotate IAM credentials on a short TTL; prefer short-lived assumed-role tokens (max 1h) over long-lived access keys |
|
|
279
|
+
| Mandatory SBOM + SLSA provenance for cloud-connected services (US EO 14028 / EU CRA) | 2025–2026 (active) | SSRF in a third-party dependency is a supply-chain vulnerability; SBOM makes it attributable and legally reportable | Generate CycloneDX SBOM per release; map every outbound HTTP library to its version so SSRF CVEs in dependencies are detected immediately |
|
|
280
|
+
|
|
281
|
+
## §DETECTION-GAP
|
|
282
|
+
|
|
283
|
+
What current SSRF monitoring CANNOT detect, and what to build to close each gap.
|
|
284
|
+
|
|
285
|
+
- **DNS rebinding mid-flight**: No log event shows the IP the socket actually connected to — only the URL string is logged. Gap: standard access logs record `url=attacker.com` even though the TCP connection went to `169.254.169.254`. Build: log the resolved IP address at connection time (not the hostname), and alert when any resolved IP falls in a private CIDR.
|
|
286
|
+
|
|
287
|
+
- **Blind SSRF with no inline response delta**: The application response is identical whether or not the internal request succeeded. Standard HTTP response monitoring sees nothing. Build: instrument every outbound HTTP client with a trace ID; correlate outbound requests in network-flow logs (VPC flow logs / eBPF) against the trace ID — any request to a private CIDR that was not pre-authorised triggers an alert.
|
|
288
|
+
|
|
289
|
+
- **Redirect chain destination**: Only the first hop URL is typically logged. The final destination after N redirects is invisible. Build: log every redirect hop in the `ssrfSafeFetch` recursive path, including the final resolved IP of the last hop.
|
|
290
|
+
|
|
291
|
+
- **URL encoding bypass attempts**: A WAF or SSRF filter may block the plain string `169.254.169.254` but pass `%31%36%39%2e%32%35%34%2e%31%36%39%2e%32%35%34`. Standard string-match logging misses this. Build: normalise and decode all URL parameters before logging and before applying SSRF checks; alert on any request where the raw and decoded forms differ significantly.
|
|
292
|
+
|
|
293
|
+
- **SSRF via file-upload URL fetch (import-by-URL features)**: An SSRF filter on the main API is bypassed because the import/ingest endpoint has a separate, unguarded HTTP client. Monitoring focused on the primary API surface misses this. Build: enforce that `ssrfSafeFetch` is the only export for outbound HTTP in the shared library; CI lints for direct `fetch`/`axios`/`got` calls outside that module.
|
|
294
|
+
|
|
295
|
+
- **Cross-agent chain invisible to either agent alone**: SSRF finding + open redirect finding = critical chain (attacker controls redirect destination → SSRF validator is bypassed). Neither the SSRF agent nor the redirect agent sees the full picture. Build: CISO orchestrator Phase 1 synthesis correlates SSRF findings with open-redirect, CORS misconfiguration, and DNS rebinding findings before Phase 2.
|
|
296
|
+
|
|
297
|
+
## §ZERO-MISS-MANDATE
|
|
298
|
+
|
|
299
|
+
This agent CANNOT declare any SSRF attack class clean without explicit evidence of checking. For each item, output one of:
|
|
300
|
+
- `CHECKED: [N files] | [patterns used] | CLEAN`
|
|
301
|
+
- `CHECKED: [N files] | [patterns used] | [N findings, all fixed]`
|
|
302
|
+
- `SKIPPED: [reason — must be "not applicable: [evidence]"]`
|
|
303
|
+
|
|
304
|
+
**Silent skip = FAILED COVERAGE.** The orchestrator flags this as a quality gap.
|
|
305
|
+
|
|
306
|
+
Attack classes that MUST be covered:
|
|
307
|
+
|
|
308
|
+
| Attack Class | Patterns to Search |
|
|
309
|
+
|---|---|
|
|
310
|
+
| Unguarded outbound fetch | `fetch(`, `axios.`, `got(`, `http.request`, `https.get` with dynamic URL |
|
|
311
|
+
| URL parameter sinks | `url=`, `webhook_url=`, `redirect=`, `callback=`, `src=`, `href=` in API routes |
|
|
312
|
+
| Redirect without re-validation | `followRedirects`, `maxRedirects`, `redirect: "follow"` |
|
|
313
|
+
| Direct IP access (no DNS) | IP literals in outbound requests; `isIP()` check absent |
|
|
314
|
+
| IPv6 / alternate encoding | `[::1]`, `0x7f`, octal IP in URL params |
|
|
315
|
+
| Metadata endpoint access | `169.254.169.254`, `metadata.google.internal`, `100.100.100.200` |
|
|
316
|
+
| DNS rebinding window | Time gap between `dns.resolve` call and `fetch` call |
|
|
317
|
+
| Blind SSRF (OOB) | No OOB testing harness configured |
|
|
318
|
+
|
|
319
|
+
The output findings JSON MUST include a `coverageManifest` key:
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"coverageManifest": {
|
|
323
|
+
"attackClassesCovered": [
|
|
324
|
+
{ "class": "Unguarded outbound fetch", "filesReviewed": 23, "patterns": ["fetch(", "axios.", "got("], "result": "CLEAN" },
|
|
325
|
+
{ "class": "URL parameter sinks", "filesReviewed": 14, "patterns": ["url=", "webhook_url=", "redirect="], "result": "2 findings, both fixed" },
|
|
326
|
+
{ "class": "Redirect without re-validation", "filesReviewed": 23, "patterns": ["followRedirects", "redirect: \"follow\""], "result": "CLEAN" },
|
|
327
|
+
{ "class": "Metadata endpoint access", "filesReviewed": 23, "patterns": ["169.254.169.254", "metadata.google.internal"], "result": "CLEAN" }
|
|
328
|
+
],
|
|
329
|
+
"filesReviewed": 23,
|
|
330
|
+
"negativeAssertions": [
|
|
331
|
+
"Unguarded outbound fetch: fetch/axios/got patterns searched across 23 files — 0 unguarded calls",
|
|
332
|
+
"Metadata endpoint access: hardcoded metadata IPs searched — 0 unblocked references"
|
|
333
|
+
],
|
|
334
|
+
"uncoveredReason": {}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
```
|
|
@@ -159,6 +159,19 @@ if (challenge) return challenge; // Returns 403 with step_up_required
|
|
|
159
159
|
}
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
+
## BEYOND SKILL.MD
|
|
163
|
+
|
|
164
|
+
Domain-specific expansions beyond the base SKILL.md mandate. Each names a specific CVE, technique, tool, or research finding:
|
|
165
|
+
|
|
166
|
+
- **CVE-2023-29489 (cPanel step-up bypass)**: Improper session fixation allowed attackers to promote a pre-auth session to a post-step-up session by replaying a session token acquired before the challenge. Check: ensure step-up tokens are cryptographically bound to the original session ID and regenerated after challenge completion.
|
|
167
|
+
- **CVE-2022-22963 (Spring Cloud Function RCE via header injection)**: Step-up checks implemented in middleware were bypassed because sensitive routes accepted routing directives in `spring.cloud.function.routing-expression` headers, invoking handlers directly. Check: verify step-up enforcement is applied at the framework routing layer, not just in application middleware that can be circumvented by direct dispatch.
|
|
168
|
+
- **OIDC ACR/AMR claim forgery (research: "Breaking OIDC Step-Up Auth", PortSwigger 2024)**: Relying parties that accept ACR/AMR claims from the authorization server without re-validating the token signature against the issuer's JWKS allow an attacker who controls any RP in a federated environment to forge step-up claims. Check: always verify the `acr` and `amr` claims are inside a validly signed JWT from the expected issuer, not passed as query parameters or in an unprotected cookie.
|
|
169
|
+
- **FIDO2/WebAuthn assertion replay (CTAP2 replay window, FIDO Alliance spec §7.1)**: WebAuthn authenticators include a signature counter; if the server does not strictly enforce counter monotonicity, a captured WebAuthn assertion can be replayed as a step-up credential. Check: persist and compare `signCount` per credential; reject any assertion where `signCount` is equal to or less than the stored value.
|
|
170
|
+
- **Biometric bypass via rooted device (Frida-based hook, tool: `frida-ios-dump`)**: On mobile platforms, biometric step-up that relies solely on the OS `LAContext.evaluatePolicy` return value can be bypassed on jailbroken/rooted devices by hooking the return value. Check: step-up secrets must be stored in hardware-backed Keychain/Keystore with `biometryCurrentSet` access control; the backend must verify a signed challenge rather than trusting a client boolean.
|
|
171
|
+
- **AI-era threat — LLM-assisted session token brute-force**: LLM-powered fuzzing (e.g., via GPT-4 tool-use + Burp Suite MCP integration) can now synthesise context-aware payloads that probe step-up bypass vectors at 50-100× human speed. Step-up endpoints must implement adaptive rate limiting using `429` with `Retry-After` and exponential back-off tied to per-user counters, not just IP.
|
|
172
|
+
- **AI-era threat — Deepfake voice/face liveness bypass (research: "FaceSwap vs. liveness", Black Hat 2024)**: Step-up flows using passive liveness checks (video selfie comparison) are vulnerable to real-time deepfake injection at the OS camera layer on desktop platforms. Any step-up that relies on face/voice biometrics must require a hardware-attested FIDO2 credential as a second factor or use a challenge-response liveness protocol with unpredictable prompts.
|
|
173
|
+
- **Post-quantum threat — ECDSA-signed step-up JWT with harvest-now-decrypt-later**: Step-up confirmation tokens signed with ECDSA (P-256) are vulnerable once a CRQC is available. Long-lived step-up audit logs containing these tokens will become forgeable. Prepare by: inventorying all ECDSA-signed step-up tokens; migrating signing to ML-DSA (FIPS 204 / Dilithium) in new deployments; ensuring step-up logs are not indefinitely retained in their current signed form.
|
|
174
|
+
|
|
162
175
|
## OUTPUT FORMAT
|
|
163
176
|
|
|
164
177
|
`AgentFinding[]` array. Each finding must include:
|
|
@@ -174,3 +187,74 @@ if (challenge) return challenge; // Returns 403 with step_up_required
|
|
|
174
187
|
- `requiredActions`: ordered action list
|
|
175
188
|
- `complianceImpact`: framework mappings
|
|
176
189
|
- `beyondSkillMd`: true if finding goes beyond the SKILL.md mandate
|
|
190
|
+
|
|
191
|
+
Every findings JSON MUST include `intelligenceForOtherAgents`:
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"intelligenceForOtherAgents": {
|
|
195
|
+
"forPentestTeam": [{ "type": "HIGH_VALUE_TARGET", "description": "...", "exploitHint": "..." }],
|
|
196
|
+
"forCryptoSpecialist": [{ "type": "CRYPTO_WEAKNESS_REFERENCE", "algorithm": "...", "location": "..." }],
|
|
197
|
+
"forCloudSpecialist": [{ "type": "SSRF_TO_CLOUD_CHAIN", "ssrfLocation": "...", "escalationPath": "..." }],
|
|
198
|
+
"forComplianceGrc": [{ "type": "COMPLIANCE_BLOCKER", "frameworks": ["..."], "releaseBlock": true }]
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## §EDGE-CASE-MATRIX
|
|
206
|
+
|
|
207
|
+
The 5 attack cases in this domain that automated scanners and naive manual review universally miss. MANDATORY checks — do not skip.
|
|
208
|
+
|
|
209
|
+
| # | Edge Case | Why Scanners Miss It | Concrete Test |
|
|
210
|
+
|---|-----------|----------------------|---------------|
|
|
211
|
+
| 1 | Second-order / stored payload executed in different context | Scanner checks input context, not execution context | Store payload safely; trigger in separate request/session |
|
|
212
|
+
| 2 | Unicode normalisation bypass | Regex filters run before normalisation; attacker uses homoglyphs or composed forms | Submit Ⅰ (U+2160) or < (U+FF1C) variants of known-bad strings |
|
|
213
|
+
| 3 | Polyglot payload active in multiple sinks simultaneously | Scanners test one injection class per payload | `'"><script>{{7*7}}</script><!--` — SQL + XSS + SSTI in one request |
|
|
214
|
+
| 4 | Out-of-band exfiltration (DNS/HTTP callback) | Scanner looks for inline response difference; OOB leaves no visible trace | Use Burp Collaborator / interactsh; inject DNS lookup payload |
|
|
215
|
+
| 5 | Race condition between check and use (TOCTOU) | Sequential scanners don't model concurrency | Send two simultaneous requests to the same state-changing endpoint |
|
|
216
|
+
|
|
217
|
+
## §TEMPORAL-THREATS
|
|
218
|
+
|
|
219
|
+
Threats materialising in the 2025–2030 window that defences designed today must account for.
|
|
220
|
+
|
|
221
|
+
| Threat | Est. Timeline | Relevance to This Domain | Prepare Now By |
|
|
222
|
+
|--------|--------------|--------------------------|----------------|
|
|
223
|
+
| Cryptographically Relevant Quantum Computer (CRQC) | 2028–2032 | Harvest-now-decrypt-later attacks active today; RSA/ECDSA keys signed today will be broken | Inventory all RSA/ECDSA usage; migrate long-lived data to ML-KEM (FIPS 203) |
|
|
224
|
+
| AI-assisted adversaries at scale | 2025–2027 (active) | LLM-powered fuzzing finds 10× more edge cases; automated PoC generation | Assume attackers have LLM help; expand test surface to match |
|
|
225
|
+
| EU AI Act full enforcement | 2026 | High-risk AI systems require mandatory conformity assessments | Classify all AI features against AI Act tiers now |
|
|
226
|
+
| Post-quantum TLS migration deadline | 2028–2030 | Browser vendors will drop classical-only TLS connections | Begin TLS agility assessment; test hybrid key exchange |
|
|
227
|
+
| Mandatory SBOM + build provenance (US EO 14028 / EU CRA) | 2025–2026 (active) | SBOM and SLSA attestation are becoming legally required | Achieve SLSA L2 minimum; generate CycloneDX SBOM per release |
|
|
228
|
+
|
|
229
|
+
## §DETECTION-GAP
|
|
230
|
+
|
|
231
|
+
What current security monitoring CANNOT detect in this domain, and what to build to close each gap.
|
|
232
|
+
|
|
233
|
+
**Standard gaps that MUST be checked:**
|
|
234
|
+
|
|
235
|
+
- **Second-order attack execution**: The storage request looks safe; only the retrieval+execution step is dangerous. Need: correlate write events with downstream read+execute events in the same SIEM query window.
|
|
236
|
+
- **Timing-side-channel leakage**: No log event emitted; only observable as microsecond response-time variance. Need: per-endpoint p99 latency tracking with statistical anomaly detection.
|
|
237
|
+
- **Low-and-slow credential stuffing**: Individually, each request is under rate limits. Need: behavioural baseline — flag accounts with geographically impossible velocity or device-fingerprint mismatch across authentication attempts.
|
|
238
|
+
- **Insider exfiltration via legitimate process**: Authorised exports, reports, and data downloads that individually are permitted but collectively constitute data exfiltration. Need: data-volume anomaly detection — alert when a single user's data access volume exceeds 3× their 30-day baseline within 24 hours.
|
|
239
|
+
- **Cross-agent attack chains**: Phase 1 finding A + Phase 1 finding B = CRITICAL chain invisible to either agent alone. Need: CISO orchestrator Phase 1 synthesis step — correlate all agent findings before Phase 2.
|
|
240
|
+
|
|
241
|
+
## §ZERO-MISS-MANDATE
|
|
242
|
+
|
|
243
|
+
This agent CANNOT declare any attack class clean without explicit evidence of checking. For each item, output one of:
|
|
244
|
+
- `CHECKED: [N files] | [patterns used] | CLEAN`
|
|
245
|
+
- `CHECKED: [N files] | [patterns used] | [N findings, all fixed]`
|
|
246
|
+
- `SKIPPED: [reason — must be "not applicable: [evidence]"]`
|
|
247
|
+
|
|
248
|
+
**Silent skip = FAILED COVERAGE.** The orchestrator flags this as a quality gap.
|
|
249
|
+
|
|
250
|
+
The output findings JSON MUST include a `coverageManifest` key:
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"coverageManifest": {
|
|
254
|
+
"attackClassesCovered": [{ "class": "SQL Injection", "filesReviewed": 47, "patterns": ["queryRaw", "string concat"], "result": "CLEAN" }],
|
|
255
|
+
"filesReviewed": 47,
|
|
256
|
+
"negativeAssertions": ["SQL Injection: queryRaw pattern searched across 47 files — 0 matches"],
|
|
257
|
+
"uncoveredReason": {}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|