Skip to content

Pin the OCSF mapping to 1.9.0 and use OCSF's class names - #258

Merged
rocklambros merged 2 commits into
integrationfrom
spec/121-ocsf-1-9-0-pin
Oct 13, 2026
Merged

rocklambros merged 2 commits into
integrationfrom
spec/121-ocsf-1-9-0-pin

Conversation

@rocklambros

@rocklambros rocklambros commented Oct 12, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

ocsf-mapping.json now pins OCSF 1.9.0 and uses OCSF's own class captions, as Bar decided on #121. Class 5001 is "Device Inventory Info" and 6002 is "Application Lifecycle" at every OCSF release, so the agbom/* rows and the five 6002 rows were mislabeled. The class numbers already meant these classes, so only the names change. steps/skillRegister, agbom/snapshot and agbom/changed gain "activity_id": 2 (Collect), because OCSF requires activity_id on every event. decision_class.required_fields names finding_info, the object Detection Finding defines, in place of finding.title and finding.uid. The class tables and examples in events.md and extend_ocsf.md follow.

Reviewers are @bar-capsule, @afogel and @artmaro, and one approval lands this. Bar and Ariel lead the Spec workstream, so the proposals below need an answer from one of them first. After that, any one of the three can approve.

Which issue does this implement

Closes #121

  • This is an editorial correction (typo, grammar, link, formatting) with no change in meaning, so it needs no issue

Base branch

  • integration, because this touches the specification, schemas, a reference implementation, an adapter, tests, or CI
  • main, because every changed path is on the documentation lane allowlist in CONTRIBUTING.md

Type of change

  • Specification change (schema, hooks, events, AgBOM)
  • Reference implementation or adapter
  • Documentation
  • Tooling or CI
  • Governance (licensing, security policy, contributor docs)

Specification changes

  • I opened a Discussion before this PR
  • Schema changes validate against the JSON Schema spec
  • I described the impact on downstream implementers below

Breaking for implementers? Only for a deployment that copied the old captions into class_name. Every class_uid is unchanged. A deployment emitting OCSF for steps/skillRegister or agbom/* should now set activity_id 2, which OCSF already required.

No Discussion was opened. The decisions are on #121.

Decided by a Spec lead. Bar's decision (6059681393) covers the 1.9.0 pin, the 5001 and 6002 captions, the version strings, and finding_info in required_fields.

Proposed by me, for a Spec lead to confirm. My notes on #121 (6117227902) add two things Bar's list does not include. steps/skillRegister, agbom/snapshot and agbom/changed get activity_id 2 (Collect), because OCSF requires an activity_id and 5001 offers only 1 (Log) and 2 (Collect). The skillRegister note no longer says "modeled as Inventory Info". Please say if you'd rather have 1 (Log).

I ran the three examples in extend_ocsf.md once, by hand, through https://schema.ocsf.io/api/v2/validate, which validated against its default schema version, 1.9.0. That check does not look inside unmapped, and nothing in CI repeats it. Before this change, each failed on missing required attributes and warned about the 1.5.0 version. The 5001 example also warned about its caption. After it, the deny example (2004) and the AgBOM example (5001) validate with no errors or warnings. They gained type_uid, plus metadata.product and device.type_id on the 5001 example. The tool call example (1007) still fails on one error, a missing device. That is the class-fit problem in #235, so it stays open there.

Not changed

Merging

  • Merge with an explicit squash message: the title, the closing line from the issue section above, a blank line, and the Signed-off-by lines. The repository squashes with the PR body by default, which drops the sign-off.
  • If Add a Go Guardian reference implementation #169 (the Go Guardian) has merged first, run cd reference-implementations/agt-go && go generate ./internal/schema and commit the result to this branch before merging.

I tested this

  • I synced my branch with the base branch before opening this
  • uv run pytest -v passes on my machine
  • uv run mkdocs build --strict passes on my machine

pytest reports 907 passed and 1 skipped. The hook taxonomy guard passes unchanged, because no steps/* key changes.

Checklist

  • Commits are signed off with git commit -s (required by the DCO)
  • Prose follows STYLE.md
  • No secrets, tokens, or internal URLs in the diff

Security

  • This change has no security impact

Bar decided on #121 to pin OCSF 1.9.0 and use OCSF's own captions. Class
5001 is "Device Inventory Info" and 6002 is "Application Lifecycle" at every
OCSF release, so the agbom/* rows and the five 6002 rows were mislabeled.
The class numbers already meant these classes, so only the names change.

OCSF requires activity_id on every event, and steps/skillRegister,
agbom/snapshot and agbom/changed had none. They take 2 (Collect), the
closer fit of 5001's two activities for a registration and a snapshot.
The skillRegister note no longer names the old caption.

Detection Finding calls its finding object finding_info, which the deny
example already uses, so decision_class.required_fields now names it.
evidences and verdict_id stay for #228, and the allow sentence stays for
#198.

The docs pin 1.9.0 everywhere they said 1.5+, including the unmapped.acs
note and metadata.version in the three examples. The 2004 and 5001
examples also gain the type_uid, metadata.product and device.type_id that
the 1.9.0 validator requires. The 1007 example still lacks device, which
is the class-fit question in #235.

Signed-off-by: Rock Lambros <rock@rockcyber.com>
@github-actions

Copy link
Copy Markdown

This pull request's description says it closes #121, which is on the project roadmap. When it merges, GitHub closes that issue and the roadmap counts it as delivered. Please confirm the change delivers the whole issue before merging.

"type": "array",
"items": { "type": "string" },
"default": ["finding.title", "finding.uid", "evidences", "verdict_id"]
"default": ["finding_info", "evidences", "verdict_id"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rocklambros just noting that this drops the title: finding.title was required before, and finding_info alone doesn't require one (OCSF only recommends title). Not dramatic, but not sure it's intended.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, that wasn't intended. finding_info.title is back in required_fields in 79407d9, so the title stays required the way finding.title was.

bar-capsule
bar-capsule previously approved these changes Oct 13, 2026
finding.title was required before the move to finding_info, and OCSF only
recommends finding_info.title, so the mapping lists it explicitly.

Signed-off-by: rocklambros <rock@rockcyber.com>
@rocklambros
rocklambros merged commit 1445556 into integration Oct 13, 2026
8 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in ACS Project Tracker Oct 13, 2026
@rocklambros

Copy link
Copy Markdown
Collaborator Author

Merged with the admin bypass. @bar-capsule approved 5588f20 on October 13. The only change since is 79407d9, the one-line fix from her review that puts finding_info.title back in required_fields, and that push dismissed the approval. CI was green on the merged head.

President added a commit to disciplinedware/agent-control-standard that referenced this pull request Oct 15, 2026
The embedded schemas follow integration after GenAI-Security-Project#63, GenAI-Security-Project#258 and GenAI-Security-Project#261.

The parity recording stores the git object IDs of the deciding TypeScript
paths instead of a commit, so a squash merge no longer makes
TestRecordingIsCurrent fail. The paths include agt.lock and the
agt-bridge package.json, which pin the AGT commit and SDK. The recorder
and the test read them from one list; the test fails when the recording
names other paths or was made from another corpus, and the recorder
refuses uncommitted changes to them. A test checks that the Go workflow
runs on every deciding path. agt/README.md tells a TypeScript contributor
when to re-record.

A test requires agt-native.lock and agt.lock to pin the same AGT repo
and commit. The native build fails when AGT at that commit changes
ffi.rs, which agteval/acs.h is written from, or fetches another opa than
agt-native.lock pins.

Under the agt evaluator, policy.opa_timeout must be shorter than
protocol.decision_timeout; guardian.yaml ships 4s. Otherwise a slow opa
lets the agent stop waiting before AGT's deny arrives (GenAI-Security-Project#269).

make load measures either evaluator under concurrent clients, selected
by EVALUATOR like the other targets. Under agt each decision runs one opa
process; on a 20-core host decisions stop rising
near 16 concurrent ones, and at 256 they reach the 5s timeout.
guardian.yaml now ships limits.max_engine_calls 32, and docs/load.md
records the results. The load run found that an engine call gave its slot
back only after its answer was sent, so a client's next step could be
refused engine_saturated with no call running; callEngine now releases
the slot first, also after a panic.

When the host adapter's audit log cannot be written, the record goes to
stderr as one JSON line with the final disposition, and the refusal's
reasoning says whether the record went to stderr or nowhere. The audit file's close error now
counts. The Codex and OpenCode hooks pass their own stderr. The §6.3 row
says that ALLOW at a hook that permits no DENY is an interpretation
(GenAI-Security-Project#270), and the docs say only the adapter library reaches those hooks.

Dependabot proposes Go updates, one pull request per dependency across
both modules. The Go workflow runs weekly, so govulncheck sees advisories
for the toolchain that Dependabot does not move, triggers on the
agt-bridge package.json, and no longer fetches full history. A test
checks that the method table permits DENY exactly where hooks.md does,
and fails on a Decision line it cannot classify.
The coverage floor counts every package except commands, examples and
test helpers.

The Guardian meets the expectations GenAI-Security-Project#256 sets for reference
implementations, mapped to tests in docs/conformance.md. audit.max_file_bytes
and audit.max_files rotate each audit file; guardian.yaml keeps at most
1 GiB per log; max_file_bytes needs max_files of at least 1, so a
rotation never deletes the live log. make load can set the rotation and
fails if the log drops, splits or reorders a record or exceeds its bound;
docs/load.md records a run and the cost of long sessions. storage.provider names the session store. The HMAC secret
file holds base64 of at least 32 bytes. key_id is a configured label with
no default, an unknown key_id is refused (GenAI-Security-Project#118, GenAI-Security-Project#272), and the hooks and
the conformance harness take it as a flag. A toolCallResult whose
request_id_ref matches no request is recorded (GenAI-Security-Project#272).

Decisions from other pull requests that the Go code contradicted:
- A shell command's URL ends only at ASCII whitespace; U+FEFF or U+00A0
  in the userinfo no longer hides the host curl reaches (GenAI-Security-Project#268).
- Versions are compatible when they share X.Y under 0.x; the handshake
  negotiates only such a version, and a step must match it (GenAI-Security-Project#196, GenAI-Security-Project#255).
- A policy target that is not a string is denied evaluation_failed; the
  TypeScript fix is in GenAI-Security-Project#229, so two recorded cases are listed as ahead of
  the recording until it is re-recorded.
- The host adapter records every decision failure under either posture,
  with its class, handshake failures included (GenAI-Security-Project#37).
- The host adapter denies under any posture a signed error other than
  -32603, an unverified answer and a malformed answer; the docs mark this
  as an interpretation of §6.4 ahead of GenAI-Security-Project#32, which is open. The version
  rule is marked the same way against §3.
- The audit directory is 0700; a symlinked audit path or an audit file
  open to group or others is refused (GenAI-Security-Project#71).

Claude-Session: https://claude.ai/code/session_01DFdYnoBitHhPcUSq7wMWs1
Signed-off-by: Konstantin Trunin <konstantin@trunin.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Docs] OCSF 1.5 renames class 5001 to "Device Inventory Info"; Trace docs still say "Inventory Info"

2 participants