name: audit-frameworks
description: Audit Cartography's rules and compliance frameworks under cartography/rules/data/rules/. Surfaces TODOs that the schema can now satisfy, per-provider rules that should collapse into one ontology rule, and duplicate detections across frameworks (CIS, ISO 27001, SOC 2, NIST). Use when the user asks to "audit frameworks", "audit rules", "review rule TODOs", "find duplicate rules", "find ontology candidates", "consolidate compliance frameworks", or "map ISO/SOC2 onto CIS".
audit-frameworks
A read-only audit of cartography/rules/data/rules/. Produces a structured report with concrete consolidation proposals; does NOT modify rules unless the user explicitly approves a follow-up.
The audit covers four dimensions:
- TODO triage: every
# TODO: ...in a rule file annotates a control that could not be implemented at the time. Re-check whether the schema now exposes the missing data. - Cross-provider to ontology: find rule clusters that have one fact per provider but could collapse into a single fact using ontology semantic labels (
UserAccount,Database,ObjectStorage, etc.). - Duplicate rules: find rules across different framework files that detect the same condition.
- Framework consolidation: when duplicates exist, keep the rule from the most technical framework (CIS, NIST 800-53) and map the compliance frameworks (ISO 27001, SOC 2) onto it via
frameworks=.
Critical rules
- Audit only. Emit a report. Do not delete rules, change
__init__.py, or rewrite Cypher without explicit user confirmation per finding. - Cite file and line for every finding. The user must be able to jump straight to the code.
- Prefer the technical framework. When consolidating, CIS and NIST 800-53 keep the rule; ISO 27001, SOC 2, PCI DSS get added to that rule's
frameworkstuple. - Same detection is not the same as same control. Two rules can share a Cypher pattern but enforce different controls (e.g. encryption-at-rest vs encryption-in-transit). Verify the description and the
WHEREclause before flagging a duplicate. - Framework control titles are not rule names.
Rule.namedescribes the Cartography security detection;Framework.control_titledescribes the external framework control or requirement. Many rules may map to the same framework control. - Never reference closed-source platforms or tickets in the audit report or in any rule change. Cartography is open source.
Inputs
cartography/rules/data/rules/: every*.pyfile is in scope.cartography/rules/data/rules/__init__.py: registration map.cartography/rules/spec/model.py:Rule,Fact,Framework,Module,Maturitydefinitions.cartography/models/<provider>/: declarative schema. Drives the TODO triage.cartography/models/ontology/mapping/data/anddocs/root/modules/ontology/schema.md: semantic labels and_ont_*properties. Drives the ontology-collapse pass.cartography-rules list,cartography-rules frameworks: quick inventory.
Instructions
Step 1: inventory
Build a working set before analysing:
cartography-rules list
cartography-rules frameworks
grep -rn "TODO\|FIXME" cartography/rules/data/rules/
For each rule file, record:
- file path
- rule ids declared (
grep -E '^[a-z0-9_]+ = Rule\(') - frameworks attached (
grep -A6 'frameworks=' <file>) - modules touched (parse
module=Module.<X>per fact)
Keep this inventory in the working report; later steps refer back to it.
Step 2: TODO triage
For every TODO comment in scope (typical form: # TODO: CIS K8s 1.1.1: ... followed by # Missing datamodel: <thing>):
- Parse the TODO: extract the framework, the requirement id, and the "Missing datamodel" / "Missing datamodel or evidence" clause.
- Probe the schema for the named gap. Examples:
- "Missing datamodel: control plane host filesystem metadata for kube-apiserver manifest files" suggests searching
cartography/models/kubernetes/for a node with file-permission properties. - "Missing datamodel: initContainers and ephemeralContainers securityContext fields" suggests searching
cartography/models/kubernetes/pod.pyand friends for those fields.
- "Missing datamodel: control plane host filesystem metadata for kube-apiserver manifest files" suggests searching
- Verdict per TODO:
IMPLEMENTABLE: schema now exposes the data; propose authoring the rule and link to thecreate-ruleskill.BLOCKED: schema still lacks the data; record the missing node/property names so the user can prioritise schema work.OUTDATED: control is no longer relevant (e.g. CIS revision dropped it); propose deleting the TODO comment.
Do not author the implementable rules in this skill. Hand off to create-rule.
Step 3: cross-provider to ontology candidates
Goal: find rules whose facts repeat the same logic across providers but could be a single ontology fact.
Identify candidate rules: rules with 2+ facts where each fact targets a different provider node label and the Cypher patterns are structurally similar. Useful starting filter:
grep -lE "module=Module\.(AWS|AZURE|GCP)" cartography/rules/data/rules/*.py \ | xargs grep -lE "module=Module\.[A-Z]+" \ | sort -uCheck ontology coverage: for each provider node in the rule's facts, confirm whether it carries a semantic label (
UserAccount,Database,ObjectStorage,FileStorage,Tenant,DeviceInstance, etc.) and the_ont_*properties referenced in the existing query (typically_ont_email,_ont_source,_ont_public, etc.). If every relevant node is mapped, the rule is a strong collapse candidate.Output: for each candidate emit:
- rule id and file
- facts that would be replaced
- proposed ontology Cypher (one fact,
module=Module.CROSS_CLOUD) - any provider-specific facts that should remain (e.g. provider-only attributes the ontology does not normalise)
Defer authoring: propose, do not write. Hand off to
enrich-ontologyif a node still needs to be mapped.
Step 4: duplicate detection
Two rules are duplicates when they detect the same condition on the same asset class, even if their ids, descriptions, or Cypher differ cosmetically. Cluster candidates by:
- Shared Cypher signature: same
MATCHlabels, same load-bearingWHEREpredicate (ignoring identifier renames). - Shared output identity field: both Findings expose the same
asset_id_field(or equivalent). - Shared remediation intent: descriptions paraphrase each other.
Useful one-liners:
# pull every WHERE clause to spot near-duplicates
grep -nE "WHERE " cartography/rules/data/rules/*.py | sort -k2
# group rules by MATCH label
grep -nE "MATCH \([a-z]+:[A-Z]" cartography/rules/data/rules/*.py
Report each suspected duplicate as a tuple: {keeper_rule_id, duplicate_rule_id, file_paths, evidence} plus a confidence note (HIGH / MEDIUM / LOW).
Step 5: consolidation plan
For every HIGH-confidence duplicate cluster:
- Pick the keeper in this priority order: CIS, then NIST 800-53, then NIST CSF / NIST AI RMF, then ISO 27001, then SOC 2, then PCI DSS, then custom. Tie-breaker: the rule with the more specific Cypher and richer Finding.
- Plan the merge:
- Add the duplicate's
Framework(...)entries to the keeper'sframeworks=tuple. Preservename,short_name,scope,revision,requirement, andcontrol_titleexactly. - Delete the duplicate file or rule symbol.
- Remove the duplicate's import + registration line from
cartography/rules/data/rules/__init__.py. - If the duplicate had references the keeper lacks, merge them into the keeper's
references=list.
- Add the duplicate's
- Sanity check: keeper's
moduleset still covers every provider the duplicate covered. If not, downgrade the merge to "needs an extra fact first" and stop.
Hand the plan to the user as a numbered list. Apply only after explicit confirmation, one cluster at a time.
Output report format
# Cartography rules audit, <YYYY-MM-DD>
## 1. TODO triage
- IMPLEMENTABLE (n)
- <file>:<line>: CIS K8s 1.1.x. Schema now exposes <Node.property>. Author via the create-rule skill.
- BLOCKED (n)
- <file>:<line>: needs <Node>/<property>. Suggest opening a schema task.
- OUTDATED (n)
- <file>:<line>: control dropped in revision X.
## 2. Ontology collapse candidates
- <rule_id> (<file>): facts aws_*, azure_*, gcp_* could become a single Module.CROSS_CLOUD fact using :<SemanticLabel>.
## 3. Duplicate clusters
- HIGH: aws_public_rds_instances (<file>) vs iso27001_public_databases (<file>): same MATCH(:RDSInstance) WHERE publicly_accessible=true.
## 4. Consolidation plan
1. Keep aws_public_rds_instances. Add Framework(short_name="ISO27001", requirement="A.13.1.1", control_title=..., ...). Remove iso27001_public_databases. Update __init__.py.
2. ...
Always end the report with Recommended next: ... listing the highest-impact item.
Examples
Example: TODO triage hit
User says: audit the cartography frameworks.
- Inventory finds
# TODO: CIS K8s 5.2.6: Partial control coverage. Missing datamodel or evidence: initContainers and ephemeralContainers securityContext fields. - Probe
cartography/models/kubernetes/pod.py(or related). Ifinit_containersis now a property withsecurity_contextfields, markIMPLEMENTABLE. - Report references the new schema fields and points at the
create-ruleskill for authoring.
Example: ontology collapse
User says: find rules we can move to ontology only.
- Find
database_instance_exposed.pywith three facts (RDS, Azure SQL, Cloud SQL). - Confirm all three node types carry the
:Databasesemantic label and_ont_publicproperty. - Propose a single
Module.CROSS_CLOUDfact:MATCH (db:Database) WHERE db._ont_public = true RETURN db.id AS id, db._ont_source AS source.
Example: duplicate consolidation
User says: find duplicate rules across frameworks.
aws_s3_public_bucketsandstorage_public_exposurebothMATCH (b:S3Bucket) WHERE b.public = true.- Keeper:
aws_s3_public_buckets. Action: appendFramework(name="ISO/IEC 27001:2022 Annex A", short_name="ISO27001", requirement="A.13.1.1", control_title="...")to itsframeworks=tuple, deletestorage_public_exposure, drop the import from__init__.py. - Wait for the user's go-ahead before editing.
Hand-offs
- Authoring a missing rule:
create-ruleskill. - Mapping a provider node to a semantic label so an ontology collapse becomes possible:
enrich-ontologyskill. - Adding the missing schema property surfaced by a BLOCKED TODO:
add-node-typeskill.
Common pitfalls
- Treating tags as frameworks.
tags=("compliance", "cis:1.14")is legacy. Useframeworks=(Framework(...),)and keeptagsfor category labels only. - Collapsing rules whose facts diverge in
WHEREsemantics. Public-network exposure and missing-encryption look similar in Cypher but are different controls; do not merge. - Dropping the duplicate before updating
__init__.py. The CLI inventory will break. Always edit__init__.pyin the same change. - Mapping ISO 27001 to a CIS rule that does not actually satisfy the ISO control. Read the ISO control text before claiming coverage; partial coverage means keep both rules and add a TODO instead.