security-ownership-map
Original:🇺🇸 English
Not Translated
4 scripts
Analyze git repositories to build a security ownership topology (people-to-file), compute bus factor and sensitive-code ownership, and export CSV/JSON for graph databases and visualization. Trigger only when the user explicitly wants a security-oriented ownership or bus-factor analysis grounded in git history (for example: orphaned sensitive code, security maintainers, CODEOWNERS reality checks for risk, sensitive hotspots, or ownership clusters). Do not trigger for general maintainer lists or non-security ownership questions.
2installs
Added on
NPX Install
npx skill4agent add davila7/claude-code-templates security-ownership-mapSKILL.md Content
Security Ownership Map
Overview
Build a bipartite graph of people and files from git history, then compute ownership risk and export graph artifacts for Neo4j/Gephi. Also build a file co-change graph (Jaccard similarity on shared commits) to cluster files by how they move together while ignoring large, noisy commits.
Requirements
- Python 3
- (required; community detection is enabled by default)
networkx
Install with:
bash
pip install networkxWorkflow
- Scope the repo and time window (optional ).
--since/--until - Decide sensitivity rules (use defaults or provide a CSV config).
- Build the ownership map with (co-change graph is on by default; use
scripts/run_ownership_map.pyto ignore supernode commits).--cochange-max-files - Communities are computed by default; graphml output is optional ().
--graphml - Query the outputs with for bounded JSON slices.
scripts/query_ownership.py - Persist and visualize (see ).
references/neo4j-import.md
By default, the co-change graph ignores common “glue” files (lockfiles, , editor config) so clusters reflect actual code movement instead of shared infra edits. Override with or . Dependabot commits are excluded by default; override with or add patterns via .
.github/*--cochange-exclude--no-default-cochange-excludes--no-default-author-excludes--author-exclude-regexIf you want to exclude Linux build glue like from co-change clustering, pass:
Kbuildbash
python skills/skills/security-ownership-map/scripts/run_ownership_map.py \
--repo /path/to/linux \
--out ownership-map-out \
--cochange-exclude "**/Kbuild"Quick start
Run from the repo root:
bash
python skills/skills/security-ownership-map/scripts/run_ownership_map.py \
--repo . \
--out ownership-map-out \
--since "12 months ago" \
--emit-commitsDefaults: author identity, author date, and merge commits excluded. Use , , or if needed.
--identity committer--date-field committer--include-mergesExample (override co-change excludes):
bash
python skills/skills/security-ownership-map/scripts/run_ownership_map.py \
--repo . \
--out ownership-map-out \
--cochange-exclude "**/Cargo.lock" \
--cochange-exclude "**/.github/**" \
--no-default-cochange-excludesCommunities are computed by default. To disable:
bash
python skills/skills/security-ownership-map/scripts/run_ownership_map.py \
--repo . \
--out ownership-map-out \
--no-communitiesSensitivity rules
By default, the script flags common auth/crypto/secret paths. Override by providing a CSV file:
# pattern,tag,weight
**/auth/**,auth,1.0
**/crypto/**,crypto,1.0
**/*.pem,secrets,1.0Use it with .
--sensitive-config path/to/sensitive.csvOutput artifacts
ownership-map-out/- (nodes: people)
people.csv - (nodes: files)
files.csv - (edges: touches)
edges.csv - (file-to-file co-change edges with Jaccard weight; omitted with
cochange_edges.csv)--no-cochange - (security ownership findings)
summary.json - (optional, if
commits.jsonl)--emit-commits - (computed by default from co-change edges when available; includes
communities.jsonper community; disable withmaintainers)--no-communities - (NetworkX node-link JSON with
cochange.graph.json+community_id; falls back tocommunity_maintainersif no co-change edges)ownership.graph.json - /
ownership.graphml(optional, ifcochange.graphml)--graphml
people.csvprimary_tz_offsetprimary_tz_minutestimezone_offsetsLLM query helper
Use to return small, JSON-bounded slices without loading the full graph into context.
scripts/query_ownership.pyExamples:
bash
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out people --limit 10
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out files --tag auth --bus-factor-max 1
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out person --person alice@corp --limit 10
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out file --file crypto/tls
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out cochange --file crypto/tls --limit 10
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section orphaned_sensitive_code
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out community --id 3Use (default) to control how many maintainers are stored per community.
--community-top-owners 5Basic security queries
Run these to answer common security ownership questions with bounded output:
bash
# Orphaned sensitive code (stale + low bus factor)
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section orphaned_sensitive_code
# Hidden owners for sensitive tags
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section hidden_owners
# Sensitive hotspots with low bus factor
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out summary --section bus_factor_hotspots
# Auth/crypto files with bus factor <= 1
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out files --tag auth --bus-factor-max 1
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out files --tag crypto --bus-factor-max 1
# Who is touching sensitive code the most
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out people --sort sensitive_touches --limit 10
# Co-change neighbors (cluster hints for ownership drift)
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out cochange --file path/to/file --min-jaccard 0.05 --limit 20
# Community maintainers (for a cluster)
python skills/skills/security-ownership-map/scripts/query_ownership.py --data-dir ownership-map-out community --id 3
# Monthly maintainers for the community containing a file
python skills/skills/security-ownership-map/scripts/community_maintainers.py \
--data-dir ownership-map-out \
--file network/card.c \
--since 2025-01-01 \
--top 5
# Quarterly buckets instead of monthly
python skills/skills/security-ownership-map/scripts/community_maintainers.py \
--data-dir ownership-map-out \
--file network/card.c \
--since 2025-01-01 \
--bucket quarter \
--top 5Notes:
- Touches default to one authored commit (not per-file). Use to count per-file touches.
--touch-mode file - Use or
--window-days 90to smooth churn.--weight recency --half-life-days 180 - Filter bots with .
--ignore-author-regex '(bot|dependabot)' - Use to show stable maintainers only.
--min-share 0.1 - Use for calendar quarter groupings.
--bucket quarter - Use or
--identity committerto switch from author attribution.--date-field committer - Use to include merge commits (excluded by default).
--include-merges
Summary format (default)
Use this structure, add fields if needed:
json
{
"orphaned_sensitive_code": [
{
"path": "crypto/tls/handshake.rs",
"last_security_touch": "2023-03-12T18:10:04+00:00",
"bus_factor": 1
}
],
"hidden_owners": [
{
"person": "alice@corp",
"controls": "63% of auth code"
}
]
}Graph persistence
Use when you need to load the CSVs into Neo4j. It includes constraints, import Cypher, and visualization tips.
references/neo4j-import.mdNotes
- in
bus_factor_hotspotslists sensitive files with low bus factor;summary.jsonis the stale subset.orphaned_sensitive_code - If is too large, narrow with
git logor--since.--until - Compare against CODEOWNERS to highlight ownership drift.
summary.json