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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,26 @@
"card_exists": true,
"state": "CONTROLLED_TEST_VALIDATED_WITH_PRIVATE_INTERNAL_BOUNDARY_CONTEXT"
},
"proof_status_index_visibility": {
"index_path": "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml",
"truth_owner": "hawkinsoperations-proof",
"visibility_owner": "hawkinsoperations-platform",
"visibility_status": "STATUS_VISIBILITY_ONLY_NON_AUTHORITATIVE",
"detection_id": "HO-DET-001",
"source_status": "SOURCE_EXISTS",
"validation_status": "CONTROLLED_TEST_VALIDATED",
"proof_ceiling": "CONTROLLED_TEST_VALIDATED",
"runtime_status": "PRIVATE_RUNTIME_BOUNDARY_CONTEXT_ONLY",
"signal_status": "NOT_PROVEN",
"public_safe_status": "NOT_PUBLIC_SAFE",
"website_status": "WEBSITE_UNTOUCHED_NOT_PROOF",
"promotion_allowed": false,
"proof_promotion_allowed": false,
"runtime_signal_promotion_allowed": false,
"public_safe_promotion_allowed": false,
"website_proof_claim_allowed": false,
"claim_boundary": "Platform reports proof-index status metadata only. Proof truth remains owned by hawkinsoperations-proof, and this visibility field does not promote proof, runtime, signal, public-safe, or website status."
},
"platform_guardrail_status": "SATISFIED_NON_PROMOTIONAL_BOUNDARY",
"blocked_claims": [
"runtime-active",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,26 @@
"card_exists": false,
"state": "PRIVATE_RUNTIME_EVIDENCE_CAPTURED"
},
"proof_status_index_visibility": {
"index_path": "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml",
"truth_owner": "hawkinsoperations-proof",
"visibility_owner": "hawkinsoperations-platform",
"visibility_status": "STATUS_VISIBILITY_ONLY_NON_AUTHORITATIVE",
"detection_id": "HO-DET-011",
"source_status": "SOURCE_EXISTS",
"validation_status": "CONTROLLED_TEST_VALIDATED",
"proof_ceiling": "PRIVATE_RUNTIME_EVIDENCE_CAPTURED",
"runtime_status": "PRIVATE_RUNTIME_EVIDENCE_CAPTURED",
"signal_status": "NOT_PROVEN",
"public_safe_status": "NOT_PUBLIC_SAFE",
"website_status": "WEBSITE_UNTOUCHED_NOT_PROOF",
"promotion_allowed": false,
"proof_promotion_allowed": false,
"runtime_signal_promotion_allowed": false,
"public_safe_promotion_allowed": false,
"website_proof_claim_allowed": false,
"claim_boundary": "Platform reports proof-index status metadata only. Proof truth remains owned by hawkinsoperations-proof, and this visibility field does not promote proof, runtime, signal, public-safe, or website status."
},
"platform_guardrail_status": "STATE_DRIFT_REVIEW_REQUIRED",
"blocked_claims": [
"runtime-active",
Expand Down
92 changes: 92 additions & 0 deletions contracts/schemas/detection-factory-controller-v0.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,95 @@
}
}
},
"proof_status_index_visibility": {
"type": "object",
"required": [
"index_path",
"truth_owner",
"visibility_owner",
"visibility_status",
"detection_id",
"source_status",
"validation_status",
"proof_ceiling",
"runtime_status",
"signal_status",
"public_safe_status",
"website_status",
"promotion_allowed",
"proof_promotion_allowed",
"runtime_signal_promotion_allowed",
"public_safe_promotion_allowed",
"website_proof_claim_allowed",
"claim_boundary"
],
"additionalProperties": false,
"properties": {
"index_path": {
"const": "hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml"
},
"truth_owner": {
"const": "hawkinsoperations-proof"
},
"visibility_owner": {
"const": "hawkinsoperations-platform"
},
"visibility_status": {
"const": "STATUS_VISIBILITY_ONLY_NON_AUTHORITATIVE"
},
"detection_id": {
"type": "string"
},
"source_status": {
"type": "string"
},
"validation_status": {
"type": "string"
},
"proof_ceiling": {
"enum": [
"NO_PROOF_RECORD",
"CONTROLLED_TEST_VALIDATED",
"PRIVATE_RUNTIME_EVIDENCE_CAPTURED",
"CROSS_SOURCE_CORROBORATION_CONTRACT_DEFINED"
]
},
"runtime_status": {
"enum": [
"NOT_PROVEN",
"PRIVATE_RUNTIME_BOUNDARY_CONTEXT_ONLY",
"PRIVATE_RUNTIME_EVIDENCE_CAPTURED"
]
},
"signal_status": {
"const": "NOT_PROVEN"
},
"public_safe_status": {
"const": "NOT_PUBLIC_SAFE"
},
"website_status": {
"const": "WEBSITE_UNTOUCHED_NOT_PROOF"
},
"promotion_allowed": {
"const": false
},
"proof_promotion_allowed": {
"const": false
},
"runtime_signal_promotion_allowed": {
"const": false
},
"public_safe_promotion_allowed": {
"const": false
},
"website_proof_claim_allowed": {
"const": false
},
"claim_boundary": {
"type": "string"
}
}
},
"gate_summary_item": {
"type": "object",
"required": [
Expand Down Expand Up @@ -371,6 +460,9 @@
"proof_state": {
"$ref": "#/$defs/proof_state"
},
"proof_status_index_visibility": {
"$ref": "#/$defs/proof_status_index_visibility"
},
"platform_guardrail_status": {
"type": "string"
},
Expand Down
17 changes: 17 additions & 0 deletions docs/factory/DETECTION_FACTORY_CONTROLLER_V0.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ The v0 controller is intentionally narrow. It produces reviewer packets for
visibility inputs. It does not promote proof, publish evidence, update the
website, create pull requests, merge changes, or write generated output files.

After Phase 2D, the controller also reads
`hawkinsoperations-proof/proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml` and
emits `proof_status_index_visibility` as platform visibility metadata only. The
proof index remains owned by `hawkinsoperations-proof`; platform output does not
become proof truth and does not promote runtime, signal, public-safe, or website
status.

## Controller Boundary

The controller is source and contract truth for a read-only platform view.
Expand All @@ -23,6 +30,8 @@ Required boundary statements:
- Website and GitHub rendering are not proof.
- AI output is labor only.
- Evidence and human review authorize claims.
- The proof status index is proof-owned truth; platform reports it as
non-authoritative visibility only.
- Public-safe status remains `NOT_PUBLIC_SAFE` unless separately approved.
- Runtime-active, signal-observed, production-ready, fleet-wide, autonomous SOC,
live Splunk, Cribl-routed, Wazuh-routed, AWS-live, AI-approved, and
Expand Down Expand Up @@ -70,6 +79,7 @@ Each reviewer packet must include:
- `required_surfaces_missing`
- `validation_state`
- `proof_state`
- `proof_status_index_visibility`
- `platform_guardrail_status`
- `blocked_claims`
- `supported_claims`
Expand All @@ -88,6 +98,13 @@ signal-observed, or public-safe proof.
`HO-DET-011` must report `PRIVATE_RUNTIME_EVIDENCE_CAPTURED` where supported by
the proof record. Public-safe status remains `NOT_PUBLIC_SAFE`.

`proof_status_index_visibility` must fail closed if the proof index is missing,
malformed, missing the requested detection ID, or attempts to promote
`public_safe_status`, `signal_status`, website proof, or unsupported proof
ceilings. Private runtime boundary values may be reported only as proof-index
visibility metadata and only where the existing proof record supports that
private boundary status.

The existing platform `HO-DET-011` case-packet guardrail is pinned to an older
6-case sample. Current detection, validation, and proof surfaces record 17
controlled-test fixtures. v0 must not repair that drift. It must report
Expand Down
126 changes: 126 additions & 0 deletions scripts/ho_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,25 @@
from pathlib import Path
from typing import Any

try:
import yaml # type: ignore
except ImportError: # pragma: no cover - absence is handled as fail-closed runtime state.
yaml = None


CONTROLLER_VERSION = "0.1.0"
CASE_LEDGER_VERSION = "AUTOSOC_CASE_LEDGER_V0"
PLATFORM_ROOT = Path(__file__).resolve().parents[1]
DEFAULT_REPO_ROOT = PLATFORM_ROOT.parent
DEFAULT_CASE_LEDGER = PLATFORM_ROOT / "evidence" / "autosoc-case-ledger-v0.sqlite"
PROOF_STATUS_INDEX_REL = "proof/indexes/DETECTION_PROOF_STATUS_INDEX.yml"
PROOF_STATUS_INDEX_OWNER = "hawkinsoperations-proof"
PROOF_STATUS_INDEX_VISIBILITY_STATUS = "STATUS_VISIBILITY_ONLY_NON_AUTHORITATIVE"
PROOF_STATUS_INDEX_BOUNDARY = (
"Platform reports proof-index status metadata only. Proof truth remains owned by "
"hawkinsoperations-proof, and this visibility field does not promote proof, runtime, "
"signal, public-safe, or website status."
)
SPLUNK_HO_DET_001_APPEND_APPROVAL = "APPEND_ONE_SANITIZED_SPLUNK_HO_DET_001_RUNTIME_CASE_APPROVED"
RUNTIME_LEDGER_TRUTH_BOUNDARY = "private_runtime_review_only_not_public_proof_not_public_safe"
RUNTIME_REVIEW_SUPPORTED_CLAIM = "PRIVATE_RUNTIME_REVIEW_SUPPORT_ONLY"
Expand Down Expand Up @@ -203,6 +216,19 @@ class DetectionSpec:
"analyst-approved disposition",
)

PROOF_INDEX_ALLOWED_CEILINGS = (
"NO_PROOF_RECORD",
"CONTROLLED_TEST_VALIDATED",
"PRIVATE_RUNTIME_EVIDENCE_CAPTURED",
"CROSS_SOURCE_CORROBORATION_CONTRACT_DEFINED",
)

PROOF_INDEX_ALLOWED_RUNTIME_STATUSES = (
"NOT_PROVEN",
"PRIVATE_RUNTIME_BOUNDARY_CONTEXT_ONLY",
"PRIVATE_RUNTIME_EVIDENCE_CAPTURED",
)

PLATFORM_SAMPLE_BLOCKED = (
"runtime-active",
"signal-observed",
Expand Down Expand Up @@ -1937,6 +1963,105 @@ def assert_proof_record(repo_root: Path, spec: DetectionSpec) -> tuple[bool, boo
return True, card_exists


def load_proof_status_index(repo_root: Path) -> dict[str, Any]:
if yaml is None:
raise FactoryError("PyYAML is required to read the proof status index")
Comment on lines +1967 to +1968
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove undeclared PyYAML runtime dependency

This introduces a hard dependency on PyYAML for normal status/plan packet generation (load_proof_status_index raises as soon as yaml is unavailable), but the repository does not declare or bootstrap that dependency. On runners that previously executed this controller with only stdlib dependencies, packet builds now fail immediately before emitting output, which is a functional regression for the new visibility path.

Useful? React with 👍 / 👎.

path = repo_path(repo_root, PROOF_STATUS_INDEX_OWNER, PROOF_STATUS_INDEX_REL)
if not path.is_file():
raise FactoryError(f"missing proof status index: {PROOF_STATUS_INDEX_OWNER}/{PROOF_STATUS_INDEX_REL}")
try:
value = yaml.safe_load(path.read_text(encoding="utf-8"))
except Exception as exc: # noqa: BLE001 - fail closed on parser boundary.
raise FactoryError(f"invalid proof status index YAML: {exc}") from exc
if not isinstance(value, dict):
raise FactoryError("proof status index root must be an object")
if value.get("owner_repo") != PROOF_STATUS_INDEX_OWNER:
raise FactoryError("proof status index owner_repo must be hawkinsoperations-proof")
if value.get("truth_surface") != "proof_boundary_index":
raise FactoryError("proof status index truth_surface must be proof_boundary_index")
entries = value.get("entries")
if not isinstance(entries, list) or not entries:
raise FactoryError("proof status index entries must be a non-empty list")
return value


def proof_index_entries_by_id(repo_root: Path) -> dict[str, dict[str, Any]]:
index = load_proof_status_index(repo_root)
entries_by_id: dict[str, dict[str, Any]] = {}
for raw in index["entries"]:
if not isinstance(raw, dict):
raise FactoryError("proof status index entry must be an object")
detection_id = raw.get("detection_id")
if not isinstance(detection_id, str) or not detection_id:
raise FactoryError("proof status index entry detection_id must be a non-empty string")
if detection_id in entries_by_id:
raise FactoryError(f"duplicate detection_id in proof status index: {detection_id}")
entries_by_id[detection_id] = raw
return entries_by_id


def proof_status_index_visibility(repo_root: Path, spec: DetectionSpec) -> dict[str, Any]:
entries = proof_index_entries_by_id(repo_root)
entry = entries.get(spec.detection_id)
if entry is None:
raise FactoryError(f"{spec.detection_id} missing from proof status index")

proof_ceiling = entry.get("proof_ceiling")
runtime_status = entry.get("runtime_status")
signal_status = entry.get("signal_status")
public_safe_status = entry.get("public_safe_status")
website_status = entry.get("website_status")

if proof_ceiling not in PROOF_INDEX_ALLOWED_CEILINGS:
raise FactoryError(f"{spec.detection_id} proof index has unsupported proof_ceiling: {proof_ceiling}")
if runtime_status not in PROOF_INDEX_ALLOWED_RUNTIME_STATUSES:
raise FactoryError(f"{spec.detection_id} proof index has unsupported runtime_status: {runtime_status}")
if signal_status != "NOT_PROVEN":
raise FactoryError(f"{spec.detection_id} proof index signal_status must remain NOT_PROVEN")
if public_safe_status != "NOT_PUBLIC_SAFE":
raise FactoryError(f"{spec.detection_id} proof index public_safe_status must remain NOT_PUBLIC_SAFE")
if website_status != "WEBSITE_UNTOUCHED_NOT_PROOF":
raise FactoryError(f"{spec.detection_id} proof index website_status must remain WEBSITE_UNTOUCHED_NOT_PROOF")
if entry.get("source_truth_owner") != "hawkinsoperations-detections":
raise FactoryError(f"{spec.detection_id} proof index source truth owner drifted")
if entry.get("validation_truth_owner") != "hawkinsoperations-validation":
raise FactoryError(f"{spec.detection_id} proof index validation truth owner drifted")
if entry.get("platform_visibility_owner") != "hawkinsoperations-platform":
raise FactoryError(f"{spec.detection_id} proof index platform visibility owner drifted")

expected_record = None if spec.proof_record is None else spec.proof_record.removeprefix("hawkinsoperations-proof/")
if entry.get("proof_record_path") != expected_record:
raise FactoryError(f"{spec.detection_id} proof index proof_record_path drifted")

expected_card = None if spec.proof_card is None else spec.proof_card.removeprefix("hawkinsoperations-proof/")
if entry.get("proof_card_path") != expected_card:
raise FactoryError(f"{spec.detection_id} proof index proof_card_path drifted")

if runtime_status != "NOT_PROVEN" and spec.proof_record is None:
raise FactoryError(f"{spec.detection_id} proof index runtime status requires an existing proof record")

return {
"index_path": f"{PROOF_STATUS_INDEX_OWNER}/{PROOF_STATUS_INDEX_REL}",
"truth_owner": PROOF_STATUS_INDEX_OWNER,
"visibility_owner": "hawkinsoperations-platform",
"visibility_status": PROOF_STATUS_INDEX_VISIBILITY_STATUS,
"detection_id": spec.detection_id,
"source_status": entry["source_status"],
"validation_status": entry["validation_status"],
Comment on lines +2049 to +2050
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate proof-index fields before direct subscripting

The new visibility payload reads source_status and validation_status via direct subscripting without prior presence/type checks. If the index entry is malformed or partially missing, this throws KeyError instead of FactoryError; since main() only handles FactoryError, the controller exits with an uncaught traceback rather than the intended fail-closed error path.

Useful? React with 👍 / 👎.

"proof_ceiling": proof_ceiling,
"runtime_status": runtime_status,
"signal_status": signal_status,
"public_safe_status": public_safe_status,
"website_status": website_status,
"promotion_allowed": False,
"proof_promotion_allowed": False,
"runtime_signal_promotion_allowed": False,
"public_safe_promotion_allowed": False,
"website_proof_claim_allowed": False,
"claim_boundary": PROOF_STATUS_INDEX_BOUNDARY,
}


def gate_summary(spec: DetectionSpec) -> list[dict[str, Any]]:
platform_claim = "platform guardrail reported"
if spec.platform_sample is None:
Expand Down Expand Up @@ -2188,6 +2313,7 @@ def build_packet(repo_root: Path, spec: DetectionSpec) -> dict[str, Any]:
"card_exists": card_exists,
"state": spec.proof_state,
},
"proof_status_index_visibility": proof_status_index_visibility(repo_root, spec),
"platform_guardrail_status": spec.platform_guardrail_status,
"blocked_claims": sorted(set(platform_claims)),
"supported_claims": list(spec.supported_claims),
Expand Down
Loading