Imported from TGPSKI/security-context-spec (
AGENTS.md). Install upstream withnpx skills add TGPSKI/security-context-spec. Copyright stays with the author.
Security Context Spec
Structured, portable, machine-readable security context for code repositories. SARIF (2020, OASIS) standardized scanner output; this spec standardizes scanner input — the security-relevant context that every scanner rediscovers on every run.
This is a specification repository, not a deployed service. Changes here affect the spec document, JSON Schema, reference validator, scanner, and examples.
Design Principle (The Gate)
Every field must either change a security finding or eliminate a security-relevant discovery step.
This is the acceptance filter for every proposed addition. A field that models mechanism without security effect (e.g., "which secret manager" or "which CI system") doesn't pass. Five sections were cut from v1.0 for failing this filter — they're preserved in decisions/proposals/broad-spectrum-expansion.md for potential promotion if scanner feedback justifies them.
Repository Layout
| Path | Purpose |
|---|---|
spec/versions/ |
Normative spec documents. v1.0 is current. |
spec/security-context-latest.md |
Symlink to current version — must not break. |
schema/security-context-v1.schema.json |
JSON Schema 2020-12 for IDE autocompletion and external CI tooling. |
tools/validate/ |
Go reference validator. Struct-based validation, not JSON Schema at runtime. |
skills/generate-security-context/ |
Agent skill for generating .security-context.yaml in consumer repos (scan + multi-phase workflow). |
examples/ |
8 validated example files: minimal through full 7-section kubernetes-service. |
decisions/ |
Proposal lifecycle: TEMPLATE.md → proposals/ → accepted/ or rejected/. |
Three Artifacts That Must Stay Aligned
Any schema change requires updating all three — they are independent implementations of the same spec:
| Artifact | Role | Key detail |
|---|---|---|
spec/versions/security-context-v1.0.md |
Normative human-readable spec | Source of truth for section definitions, enums, and semantics |
schema/security-context-v1.schema.json |
Machine-readable schema | additionalProperties: true at root (extensibility), false within sections |
tools/validate/main.go |
Reference validator binary | Struct-based; enum sets as map[string]bool via set() helper |
The spec is authoritative. The JSON Schema and Go validator are derived from it. When they disagree, the spec wins.
Working Principles
- Read the spec before changing validation. The spec defines what's valid. Don't add an enum value to
main.gowithout first adding it to the spec. - Never invent enum values. All enums are closed sets defined in spec section 3. Adding a value is a schema change and requires the proposal process.
- One Go dependency. Both
tools/validateandskills/generate-security-context/scriptsdepend only ongopkg.in/yaml.v3. Don't add dependencies. - Examples are contracts. All 8 examples must pass
make validate. Useacme/org names in examples, never real organizations. - Unknown top-level keys are intentional. The validator silently ignores unknown top-level keys for forward compatibility (spec §4). Unknown fields within defined sections are validation errors — enforced via raw map key checking against
knownSectionKeys.
Development Workflow
make check # Full CI locally: lint + fmt-check + test + validate. Always run before finishing.
make validate # Build validator, then validate all examples/*.yaml
make test # Go tests for both validator and scanner
make lint # go vet (both packages) + JSON Schema syntax + symlink integrity
make fmt / fmt-check # gofmt for both packages
Other useful targets:
make build # Build validator binary only
make build-scan # Build scanner binary
make scan REPO=/path # Scan a repo for detectable security properties
make validate-file FILE=path/to/file.yaml # Validate a single file
Schema Change Process
Schema changes go through the proposal lifecycle — don't edit the spec, schema, or validator directly for structural changes:
make proposal NAME=my-change # Create from decisions/TEMPLATE.md
# Fill in: Context, Schema Changes, Scanner Value, Alternatives, Impact
# Open PR for discussion
make accept PROPOSAL=my-change # Move to decisions/accepted/
make reject PROPOSAL=my-change # Move to decisions/rejected/
After acceptance, implement across all three artifacts + examples + CHANGELOG.
Spec Sections (v1.0)
| Section | Purpose | Key constraint |
|---|---|---|
metadata |
Identity, lifecycle, ownership, scope | schema_version: "1.0" is the only required field in the entire spec |
security_properties |
Flat deployment facts that change severity | All booleans or closed enums — no free-form text |
prerequisites |
Attacker cost model for severity adjustment | requires accepts string or string array (oneOf in schema) |
relationships |
Cross-repo blast radius and trust boundaries | access_grants enables novel cross-repo analysis |
dependencies |
Unreachable CVEs and base image context | VEX justification vocabulary from CSAF standard |
accepted_risks |
Intentional tradeoffs with expiry and audit trail | All 6 fields required — id, finding_pattern, rationale, reviewed_by, reviewed_date, expires |
scanning_directives |
Finding modifiers: suppress, prioritize, false positive patterns | suppress requires cwe OR paths (or both) — enforced by anyOf |
Validator Internals
Relevant when editing tools/validate/main.go:
- Enum sets:
map[string]boolbuilt byset()helper. One set per enum (e.g.,statusEnum,exposureEnum). checkEnum(path, value, allowed): Validates against set; skips empty values (optional fields).checkDate(path, value): ParsesYYYY-MM-DDviatime.Parse("2006-01-02", ...).- CVE pattern:
^CVE-\d{4}-\d{4,}$— regex validated onknown_unreachableentries. - CWE pattern:
^CWE-\d+$— regex validated onsuppressandaccepted_risksentries. - Per-section validators:
validateMetadata,validateSecurityProperties,validatePrerequisite,validateRelationship,validateDependencies,validateAcceptedRisk,validateScanningDirectives. - Output format:
OK: {file} is validorFAIL: {file} — N error(s):with{section}.{field}: {message}lines.
Scanner Capabilities
The scanner at skills/generate-security-context/scripts/scan.go detects security properties from files on disk:
| Detection | Sources |
|---|---|
| Repo name | git remote get-url origin |
| Owner | CODEOWNERS (root, .github/, docs/) |
| Platform | serverless.yml → lambda; SAM template → lambda; ECS task-def → ecs; K8s/Helm/Kustomize → kubernetes; Route/DeploymentConfig → openshift; Dockerfile alone → kubernetes (assumed); none → not-deployed |
| Exposure | Ingress/Route/VirtualService → public |
| Security booleans | K8s securityContext fields, Dockerfile USER directive |
| WAF | Istio mTLS STRICT, CI files with WAF vendor references |
| Network isolation | NetworkPolicy resources |
| Base images | Dockerfile FROM lines, @sha256: pinning detection, EOL map |
| Existing tooling | .security-context.yaml, .trivyignore, .snyk, .semgrepignore, .checkov.yaml, SECURITY-INSIGHTS.yml, VEX files |
Trust Model
The .security-context.yaml file is a declaration of context, not a grant of authority. The team whose code is scanned maintains it. Scanners decide how much weight to give each field. Scanners SHOULD NOT silently suppress findings — always report what was suppressed. Security teams MAY audit the file. Expired accepted_risks SHOULD be flagged.
CI Requirements
Two-tier structure:
cijob (tier 1) — runs on every push and PR. Single job with all lint/test/build/validate steps. This is the sole required status check for merge.full-scopejob (tier 2) — cross-platform matrix (linux/arm64, macos/arm64). Only runs onfull-testlabel,workflow_dispatch, or push tomain. Informational — doesn't block merge.
Actions are SHA-pinned with version comments. Runner is pinned to ubuntu-24.04. Go version read from go.mod via go-version-file. Concurrency group cancels superseded PR runs.
Common Tasks
| Task | Files to touch |
|---|---|
| Add enum value | spec, schema, main.go enum set, affected examples, CHANGELOG |
| Add optional field to existing section | spec section 3, schema properties, Go struct + validator, example, CHANGELOG |
| Add new section | Proposal first (make proposal), then all three artifacts + examples + CHANGELOG |
| Fix validator bug | main.go + main_test.go, run make check |
| Improve scanner detection | scan.go + scan_test.go, run make check |
| Add example | examples/new-pattern.yaml — must pass make validate, use acme/ names |
| Release new spec version | make release VERSION=X.Y — copies current spec, updates symlink |
Example Files Reference
Use these as starting points when generating .security-context.yaml files in consumer repos:
| Example | Best for |
|---|---|
minimal.yaml |
Getting started — metadata only |
kubernetes-service.yaml |
Full 7-section reference for internal K8s services |
public-api.yaml |
Public-facing APIs with OAuth, CSRF waivers, external trust |
gitops-reconciler.yaml |
Controllers with cross-repo access grants and out-of-scope paths |
data-pipeline.yaml |
ETL/analytics with cross-team data sources |
serverless-function.yaml |
AWS Lambda — ephemeral, air-gapped |
open-source-library.yaml |
Libraries (not-deployed), supply chain focus |
terraform-infra.yaml |
IaC repos, Atlantis prerequisites |
Contributing
One logical change per PR. Update CHANGELOG.md under [Unreleased]. make check must pass. No new Go dependencies. Schema changes require the proposal process — don't skip it.
License
Apache-2.0