Threat-Framework Mapping

Asqav binds caller-supplied threat-and-control taxonomy mappings into the signed receipt via seven optional wire fields: mitre_techniques, mitre_atlas, owasp_llm_top10, nist_ai_rmf, iso_42001, eu_ai_act_articles, and rfc3161_timestamp. A server-set boolean framework_mappings_self_declared auto-flips to true whenever any of the six list fields is populated, so verifiers can tell self-declared classifications apart from cloud-verified ones.

What it binds: seven optional taxonomy and timestamp fields plus one server-set guard, all inside the same tamper-evident envelope as the action and the policy decision.

What it guarantees: the receipt was issued against THAT mapping, not a later mapping that papered over a missed control.

Where the line sits: the taxonomy ids are caller-supplied and preserved verbatim. Asqav signs the bytes. The producer asserts the semantics.

What is the threat-framework mapping 8-tuple?

Seven OPTIONAL caller-supplied fields plus one server-set guard on POST /api/v1/agents/{id}/sign:

FieldTypeWhat it carries
mitre_techniqueslist of stringsMITRE ATT&CK technique ids (e.g. ["T1059", "T1078"]). Self-declared.
mitre_atlaslist of stringsMITRE ATLAS ids for AI-system threats (e.g. ["AML.T0051", "AML.T0043"]). Self-declared.
owasp_llm_top10list of stringsOWASP Top 10 for LLM ids (e.g. ["LLM01", "LLM02"]). Self-declared.
nist_ai_rmflist of stringsNIST AI RMF function ids and subcategories (e.g. ["GOVERN-1.1", "MEASURE-2.7"]). Self-declared.
iso_42001list of stringsISO/IEC 42001 control ids (e.g. ["A.6.2.6"]). Self-declared.
eu_ai_act_articleslist of stringsEU AI Act article ids (e.g. ["Article-12", "Article-15"]). Self-declared.
rfc3161_timestampstringCaller-supplied base64-encoded TimeStampResp (DER) preserved on the receipt for offline TSA chain verification.
framework_mappings_self_declaredbooleanServer-set false-attestation guard. Auto-flips to true whenever any of the six list fields is populated. Verifiers display the caller-supplied label prominently.

All seven caller-supplied fields are OPTIONAL on the wire. None is mandatory on any receipt_type. The cloud accepts each independently. Producers populate whichever taxonomies their compliance pipeline uses.

Why bind threat-framework mappings into the signed receipt?

A signed receipt without taxonomy mappings answers "did the agent perform action X under policy Y?". It does not answer "which regulatory or threat-framework control does this action map to?".

When a regulator or buyer asks for evidence of coverage against the NIST AI RMF or the EU AI Act, the answer must travel inside the audit chain, not in a separate spreadsheet. The 8-tuple keeps both questions inside the same tamper-evident envelope, so the audit chain does not branch.

Industry threat-and-control standards already cover the second question:

Asqav's 8-tuple references each of those frameworks by id. The signed receipt becomes the entry point an auditor uses to recover the caller-declared mapping at audit time. The cryptographic binding guarantees the receipt was issued against THAT mapping, not a later mapping that papered over a missed control.

Self-declared, not Asqav-verified

The cloud does NOT validate that an owasp_llm_top10 value is a real OWASP id, that a nist_ai_rmf subcategory exists in the published framework, or that an eu_ai_act_articles entry maps to a real article. The values are caller-supplied and preserved verbatim.

To prevent false-attestation, the cloud auto-flips framework_mappings_self_declared=true whenever any of the six list fields is populated, even if the caller passes false. Verifiers display the self-declared label prominently so a downstream auditor knows the taxonomy entries came from the producer, not from Asqav.

The asymmetry mirrors the wire-evidence pattern already in use for executable_hash (caller-asserts the binary), slsa_provenance_pointer (caller-asserts the URL), and cve_inventory_digest (caller-asserts the inventory). Asqav signs the bytes. The producer asserts the semantics.

How does this combine with the rest of the receipt?

The 8-tuple is orthogonal to every other wire field. A decision receipt with full taxonomy binding looks like:

json
{
  "action_type": "api:call",
  "receipt_type": "protectmcp:decision",
  "policy_decision": "permit",
  "mitre_techniques": ["T1059", "T1078"],
  "mitre_atlas": ["AML.T0051", "AML.T0043"],
  "owasp_llm_top10": ["LLM01", "LLM02"],
  "nist_ai_rmf": ["GOVERN-1.1", "MEASURE-2.7"],
  "iso_42001": ["A.6.2.6"],
  "eu_ai_act_articles": ["Article-12", "Article-15"],
  "rfc3161_timestamp": "MIIGgzADAgEAMIIGegYJKoZIhvcNAQcCoIIGazCCBmcCAQMxDTALBglghkgBZQMEAgE=",
  "framework_mappings_self_declared": true
}

The signed envelope binds every wire field. A verifier who later recovers the receipt cannot mutate any of the eight values without breaking the ML-DSA signature.

Field constraints

A malformed value is rejected with HTTP 422 and a machine-readable error code:

The SDK enforces the same shapes client-side and raises before the HTTP roundtrip. The framework_mappings_self_declared boolean is server-set.

How do I produce a taxonomy-bound receipt?

Python SDK

python
import asqav

asqav.init()
agent = asqav.Agent.create("threat-fw-emitter")

sig = agent.sign(
    "api:call",
    {"user": "..."},
    compliance_mode=True,
    receipt_type="protectmcp:decision",
    mitre_techniques=["T1059", "T1078"],
    owasp_llm_top10=["LLM01"],
    nist_ai_rmf=["GOVERN-1.1", "MEASURE-2.7"],
    eu_ai_act_articles=["Article-12"],
)

TypeScript SDK

typescript
import { Agent } from "@asqav/sdk";

const agent = Agent.attach({ /* ... */ });

await agent.sign({
  actionType: "api:call",
  context: { user: "..." },
  complianceMode: true,
  receiptType: "protectmcp:decision",
  mitreTechniques: ["T1059", "T1078"],
  owaspLlmTop10: ["LLM01"],
  nistAiRmf: ["GOVERN-1.1", "MEASURE-2.7"],
  euAiActArticles: ["Article-12"],
});

curl

bash
curl -X POST https://api.asqav.com/api/v1/agents/agt_threat_fw/sign \
  -H "X-API-Key: $ASQAV_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "action_type": "api:call",
    "compliance_mode": true,
    "receipt_type": "protectmcp:decision",
    "mitre_techniques": ["T1059", "T1078"],
    "owasp_llm_top10": ["LLM01"],
    "nist_ai_rmf": ["GOVERN-1.1", "MEASURE-2.7"],
    "eu_ai_act_articles": ["Article-12"]
  }'

How does an auditor use the 8-tuple?

The auditor holds the receipt (with the 8-tuple) and the producer's compliance documentation (kept by the producer's GRC team). To reconstruct the control coverage, the auditor:

  1. Reads the six taxonomy lists plus framework_mappings_self_declared from the signed envelope.
  2. Confirms framework_mappings_self_declared=true matches the populated lists (cloud-set, so always consistent).
  3. Maps each id against the corresponding canonical framework document (the auditor holds the authoritative MITRE / OWASP / NIST / ISO / EU references).
  4. Cross-checks the producer's policy artefact (resolved via policy_digest) against the declared controls.
  5. Verifies the rfc3161_timestamp against the issuing TSA chain when present, for an additional independent trusted-timestamp anchor.

Each step is independently verifiable. A mismatch on any step is an audit signal. Matching values across all steps document the producer's declared control coverage for that specific action.

How does this relate to standalone GRC mapping tools?

Standalone GRC tooling maintains a control-coverage spreadsheet separate from the action log. Asqav publishes the mapping as part of the action log itself. The auditor needs one canonical chain, not two.

This subsumption is documented in the IETF Compliance Receipts draft under the optional-extensions section, alongside config_manifest_digest, cve_inventory_digest, executable_hash, sbom_digest, slsa_provenance_pointer, and supply_chain_pointer. Producers MAY maintain a separate GRC system in parallel. The receipt's taxonomy fields then resolve to the same control ids the GRC system tracks.

Related documentation