Agent skill
security-ownership-map
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.
Install this agent skill to your Project
npx add-skill https://github.com/tercumantanumut/selene/tree/main/lib/skills/catalog/bundled/security-ownership-map
SKILL.md
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
networkx(required; community detection is enabled by default)
Install with:
pip install networkx
Workflow
- Scope the repo and time window (optional
--since/--until). - Decide sensitivity rules (use defaults or provide a CSV config).
- Build the ownership map with
${SELENE_SKILL_ROOT}/scripts/run_ownership_map.py(co-change graph is on by default; use--cochange-max-filesto ignore supernode commits). - Communities are computed by default; graphml output is optional (
--graphml). - Query the outputs with
${SELENE_SKILL_ROOT}/scripts/query_ownership.pyfor bounded JSON slices. - Persist and visualize (see
${SELENE_SKILL_ROOT}/references/neo4j-import.md).
By default, the co-change graph ignores common “glue” files (lockfiles, .github/*, editor config) so clusters reflect actual code movement instead of shared infra edits. Override with --cochange-exclude or --no-default-cochange-excludes. Dependabot commits are excluded by default; override with --no-default-author-excludes or add patterns via --author-exclude-regex.
If you want to exclude Linux build glue like Kbuild from co-change clustering, pass:
python "${SELENE_SKILL_ROOT}/scripts/run_ownership_map.py \
--repo /path/to/linux \
--out ownership-map-out \
--cochange-exclude "**/Kbuild"
Quick start
Run from the repo root:
python "${SELENE_SKILL_ROOT}/scripts/run_ownership_map.py \
--repo . \
--out ownership-map-out \
--since "12 months ago" \
--emit-commits
Defaults: author identity, author date, and merge commits excluded. Use --identity committer, --date-field committer, or --include-merges if needed.
Example (override co-change excludes):
python "${SELENE_SKILL_ROOT}/scripts/run_ownership_map.py \
--repo . \
--out ownership-map-out \
--cochange-exclude "**/Cargo.lock" \
--cochange-exclude "**/.github/**" \
--no-default-cochange-excludes
Communities are computed by default. To disable:
python "${SELENE_SKILL_ROOT}/scripts/run_ownership_map.py \
--repo . \
--out ownership-map-out \
--no-communities
Sensitivity 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.0
Use it with --sensitive-config path/to/sensitive.csv.
Output artifacts
ownership-map-out/ contains:
people.csv(nodes: people)files.csv(nodes: files)edges.csv(edges: touches)cochange_edges.csv(file-to-file co-change edges with Jaccard weight; omitted with--no-cochange)summary.json(security ownership findings)commits.jsonl(optional, if--emit-commits)communities.json(computed by default from co-change edges when available; includesmaintainersper community; disable with--no-communities)cochange.graph.json(NetworkX node-link JSON withcommunity_id+community_maintainers; falls back toownership.graph.jsonif no co-change edges)ownership.graphml/cochange.graphml(optional, if--graphml)
people.csv includes timezone detection based on author commit offsets: primary_tz_offset, primary_tz_minutes, and timezone_offsets.
LLM query helper
Use ${SELENE_SKILL_ROOT}/scripts/query_ownership.py to return small, JSON-bounded slices without loading the full graph into context.
Examples:
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out people --limit 10
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out files --tag auth --bus-factor-max 1
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out person --person alice@corp --limit 10
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out file --file crypto/tls
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out cochange --file crypto/tls --limit 10
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out summary --section orphaned_sensitive_code
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out community --id 3
Use --community-top-owners 5 (default) to control how many maintainers are stored per community.
Basic security queries
Run these to answer common security ownership questions with bounded output:
# Orphaned sensitive code (stale + low bus factor)
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out summary --section orphaned_sensitive_code
# Hidden owners for sensitive tags
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out summary --section hidden_owners
# Sensitive hotspots with low bus factor
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out summary --section bus_factor_hotspots
# Auth/crypto files with bus factor <= 1
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out files --tag auth --bus-factor-max 1
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out files --tag crypto --bus-factor-max 1
# Who is touching sensitive code the most
python "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out people --sort sensitive_touches --limit 10
# Co-change neighbors (cluster hints for ownership drift)
python "${SELENE_SKILL_ROOT}/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 "${SELENE_SKILL_ROOT}/scripts/query_ownership.py --data-dir ownership-map-out community --id 3
# Monthly maintainers for the community containing a file
python "${SELENE_SKILL_ROOT}/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 "${SELENE_SKILL_ROOT}/scripts/community_maintainers.py \
--data-dir ownership-map-out \
--file network/card.c \
--since 2025-01-01 \
--bucket quarter \
--top 5
Notes:
- Touches default to one authored commit (not per-file). Use
--touch-mode fileto count per-file touches. - Use
--window-days 90or--weight recency --half-life-days 180to smooth churn. - Filter bots with
--ignore-author-regex '(bot|dependabot)'. - Use
--min-share 0.1to show stable maintainers only. - Use
--bucket quarterfor calendar quarter groupings. - Use
--identity committeror--date-field committerto switch from author attribution. - Use
--include-mergesto include merge commits (excluded by default).
Summary format (default)
Use this structure, add fields if needed:
{
"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 ${SELENE_SKILL_ROOT}/references/neo4j-import.md when you need to load the CSVs into Neo4j. It includes constraints, import Cypher, and visualization tips.
Notes
bus_factor_hotspotsinsummary.jsonlists sensitive files with low bus factor;orphaned_sensitive_codeis the stale subset.- If
git logis too large, narrow with--sinceor--until. - Compare
summary.jsonagainst CODEOWNERS to highlight ownership drift.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
app-mockup
Create deterministic browser, tablet, laptop, and phone mockups from real screenshots via the local TypeScript renderer in this plugin. Use for marketing visuals, store screenshots, and framed UI previews without ML image generation.
chatgpt-apps
Build, scaffold, refactor, and troubleshoot ChatGPT Apps SDK applications that combine an MCP server and widget UI. Use when Codex needs to design tools, register UI resources, wire the MCP Apps bridge or ChatGPT compatibility APIs, apply Apps SDK metadata or CSP or domain settings, or produce a docs-aligned project scaffold. Prefer a docs-first workflow by invoking the openai-docs skill or OpenAI developer docs MCP tools before generating code.
screenshot
Use when the user explicitly asks for a desktop or system screenshot (full screen, specific app or window, or a pixel region), or when tool-specific capture capabilities are unavailable and an OS-level capture is needed.
gh-fix-ci
Use when a user asks to debug or fix failing GitHub PR checks that run in GitHub Actions; use `gh` to inspect checks and logs, summarize failure context, draft a fix plan, and implement only after explicit approval. Treat external providers (for example Buildkite) as out of scope and report only the details URL.
speech
Use when the user asks for text-to-speech narration or voiceover, accessibility reads, audio prompts, or batch speech generation via the OpenAI Audio API; run the bundled CLI (`${SELENE_SKILL_ROOT}/scripts/text_to_speech.py`) with built-in voices and require `OPENAI_API_KEY` for live calls. Custom voice creation is out of scope.
doc
Use when the task involves reading, creating, or editing `.docx` documents, especially when formatting or layout fidelity matters; prefer `python-docx` plus the bundled `${SELENE_SKILL_ROOT}/scripts/render_docx.py` for visual checks.
Didn't find tool you were looking for?