Files
dev-workflows/skills/dependency-scanning/docs/normalized-json-output.md
Evandro Fonseca Junior bb3fe21766 fix(skills): vendor the support files the vendored skills point at
The vendored SKILL.md files were byte-identical to upstream, but the
playbooks, templates and schemas they reference were left behind in the
original repos, so a loading skill pointed at a file that does not exist
anywhere in this repository.

Vendored 1:1 into each skill directory (so they survive
`cp -r skills/* ~/.hermes/skills/`):

- plays/ for api-security-review, code-review-security, sca-audit,
  secrets-scan, web-security-review (OWASP/secure-agent-playbook)
- templates/finding.md for the four OWASP skills that cite it
- schemas/finding.schema.json for cve-triage, patch-prioritization,
  dependency-scanning (UnitOneAI/SecuritySkills)
- docs/sarif-output.md, docs/fixer-policy.md and
  docs/normalized-json-output.md for dependency-scanning

Path fixes, the only edit to vendored text:
- the three UnitOneAI skills referenced ../../../schemas/ and ../../../docs/
  which assumed upstream's skills/<category>/<skill>/ depth; this repo is
  flat, so the links now resolve inside the skill directory
- safedeps told the agent to run <skill_dir>/../../check_deps.py while the
  script is vendored next to SKILL.md; corrected to <skill_dir>/check_deps.py
  and documented the script's exit codes (1 = finding, not a crash)
- NOTICE.md records the new files, the path edits, and the remaining known
  gaps (hermaguard's kensei-pipeline references, the ASVS dataset citations,
  and hermaguard being behind upstream)

Verified: every backticked path in every SKILL.md now resolves;
check_deps.py runs from the installed location and returns live OSV.dev
data ([email protected] -> 2 advisories); hermaguard's 46 tests still pass.
2026-09-14 07:40:21 -03:00

5.3 KiB

Normalized JSON Output Contract

SecuritySkills can emit machine-readable findings as a normalized JSON envelope that validates against schemas/finding.schema.json. This contract is independent of SARIF. Downstream systems may map it to SARIF, ticketing systems, GRC platforms, dashboards, or vulnerability stores without changing individual skill output rules. See docs/sarif-output.md for the repo-level SARIF 2.1.0 export mapping.

Envelope

Every JSON response uses this top-level shape:

{
  "schema_version": "1.0.0",
  "run": {
    "id": "run-2026-06-16T12:00:00Z",
    "timestamp": "2026-06-16T12:00:00Z",
    "tool": "codex",
    "target": "github.com/example/service",
    "source_ref": "commit-or-build-id"
  },
  "skill": {
    "name": "secure-code-review",
    "version": "1.0.0",
    "path": "skills/appsec/secure-code-review/SKILL.md",
    "frameworks": ["OWASP-ASVS-4.0.3", "CWE"]
  },
  "findings": []
}
  • schema_version is fixed at 1.0.0 until a breaking contract change is required.
  • run.id is the deduplication boundary for one execution. Use an orchestrator ID, CI job ID, or generated run ID.
  • run.timestamp should be an ISO 8601 timestamp.
  • run.target identifies the reviewed repository, project, service, artifact, environment, or evidence package.
  • skill.name, skill.version, and skill.frameworks come from the skill's SKILL.md frontmatter.

Findings

Each finding must include:

  • id: stable finding ID within the run.
  • title: concise finding title.
  • severity: one of info, low, medium, high, or critical.
  • status: one of open, mitigated, accepted_risk, or false_positive.
  • evidence: one or more concrete evidence entries.
  • remediations: one or more remediation recommendations, each with a test strategy.

Each finding must include at least one framework/CWE mapping:

  • cwe: array of CWE IDs such as CWE-89, when applicable.
  • framework_refs: array of framework/control references from the skill's declared frameworks.

Optional fields such as fingerprint, description, and references support enterprise deduplication, analyst context, and external advisory linking.

Evidence

Evidence entries must identify where the issue was observed and summarize the observation. Locations may be source paths, line ranges, cloud resource IDs, log sources, policy paths, scanner result identifiers, or evidence package records.

Use snippet only for the minimal redacted text needed to prove the finding. Set redacted: true when secrets, tokens, personal data, internal hostnames, or other sensitive values were removed.

Remediation And Tests

Each remediation item must include:

  • guidance: concrete remediation steps or patch guidance.
  • confidence: low, medium, or high.
  • blast_radius: expected affected files, systems, users, integrations, data, or workflows.
  • behavior_change_risk: low, medium, or high.
  • test_strategy: validation that proves the issue is fixed.

test_strategy must include a summary and at least one of recommended_tests or generated_tests. See docs/remediation-output.md for remediation-specific field guidance.

Minimal Example

{
  "schema_version": "1.0.0",
  "run": {
    "id": "run-001",
    "timestamp": "2026-06-16T12:00:00Z",
    "tool": "codex",
    "target": "payments-api",
    "source_ref": "abc1234"
  },
  "skill": {
    "name": "api-security",
    "version": "1.0.0",
    "path": "skills/appsec/api-security/SKILL.md",
    "frameworks": ["OWASP-API-Top-10-2023", "CWE"]
  },
  "findings": [
    {
      "id": "API-SEC-001",
      "fingerprint": "api-security:users-delete:missing-admin-auth",
      "title": "Administrative endpoint lacks authorization check",
      "description": "The delete-user route accepts authenticated requests without verifying administrative privileges.",
      "severity": "high",
      "status": "open",
      "cwe": ["CWE-862"],
      "framework_refs": [
        {
          "framework": "OWASP-API-Top-10-2023",
          "control": "API5:2023",
          "name": "Broken Function Level Authorization"
        }
      ],
      "evidence": [
        {
          "location": "routes/users.js:42",
          "artifact_type": "source",
          "summary": "DELETE /users/:id checks authentication but not administrator role.",
          "snippet": "router.delete('/users/:id', requireAuth, deleteUser)",
          "redacted": false
        }
      ],
      "remediations": [
        {
          "guidance": "Require an administrator role check before invoking deleteUser.",
          "confidence": "high",
          "blast_radius": "User administration routes only.",
          "behavior_change_risk": "medium",
          "test_strategy": {
            "summary": "Proves non-admin users cannot delete accounts while admins still can.",
            "recommended_tests": [
              {
                "name": "Reject non-admin delete",
                "type": "integration",
                "purpose": "Confirms the vulnerable authorization bypass no longer succeeds.",
                "command": "npm test -- users-authz.test.js",
                "expected_result": "Non-admin DELETE /users/:id returns 403 and admin DELETE succeeds."
              }
            ]
          }
        }
      ]
    }
  ]
}