crp.scan¶
Auto-generated reference for the crp.scan subpackage.
scan¶
crp.scan ¶
CRP Scan - static AI-governance scanner + Remediation Engine.
Public API
SemanticCodeIngestion - SPEC-039 code knowledge graph builder RemediationEngine - SPEC-036 diff + PR proposal generator ScanFinding - finding data model RemediationProposal - proposal data model AICallSite - detected call site data model CodeGraph - code knowledge graph CodeFact - code entity fact
RemediationEngine ¶
Generate concrete remediation proposals for CRP Scan findings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
templates_dir | str | None | Optional path to a directory containing Jinja2 | None |
propose_fix(finding) ¶
Produce a remediation proposal for finding.
Determines the remediation class, selects the appropriate template, and renders the diff / config snippet + PR body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding | ScanFinding | A ScanFinding from the scan engine. | required |
Returns:
| Type | Description |
|---|---|
RemediationProposal | RemediationProposal with all fields populated. The |
RemediationProposal | field will be non-empty for code fixes; |
RemediationProposal | config fixes. |
open_pr(proposal, repo, installation_id, base_branch='main') ¶
Open a pull request with the remediation diff.
ALWAYS opens a PR to a dedicated branch - NEVER commits directly to a protected branch (AGENTS.md invariant #11).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
proposal | RemediationProposal | RemediationProposal produced by :meth: | required |
repo | str | Full repository name, e.g. | required |
installation_id | str | GitHub App installation ID for the repo. | required |
base_branch | str | The target base branch for the PR. Default | 'main' |
Returns:
| Type | Description |
|---|---|
str | The URL of the created pull request. |
Note
This method requires crp.scan.github_app (Agent B Round 3) to provide the GitHub App token-minting facility. If not yet installed, returns a descriptive error string.
proposals_for_repo(findings) ¶
Produce proposals for all findings in a repo scan result.
RemediationProposal dataclass ¶
A concrete, actionable remediation for a ScanFinding.
Always produced by the engine before any PR is opened. The diff field contains the unified diff ready for git apply / PR creation.
ScanFinding dataclass ¶
A single finding from CRP Scan.
Maps to the SARIF result structure emitted by crp-scan GitHub Action.
AICallSite dataclass ¶
A detected ungoverned (or governed) AI call site.
CodeFact dataclass ¶
A single code entity - a fact in the code knowledge graph (SPEC-039 §2.1).
display property ¶
Return the display.
CodeGraph dataclass ¶
Knowledge graph of a codebase (SPEC-039 §2).
file_count property ¶
Return the current file count.
function_count property ¶
Return the current function count.
add_fact(fact) ¶
Add a code fact to the graph and update the name index.
add_edge(from_id, to_id, edge_type) ¶
Add a directed edge between two facts, plus an inverse edge.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
from_id | str | Source fact ID. | required |
to_id | str | Target fact ID. | required |
edge_type | str | Relationship type (e.g. | required |
get_neighbours(fact_id) ¶
Return all neighbours of fact_id as (neighbour_id, edge_type) pairs.
lookup_name(name) ¶
Return all facts that define the given symbol name.
SemanticCodeIngestion ¶
Ingest a repository into a code-aware knowledge graph.
Zero external dependency - uses Python's stdlib ast module for Python files. TypeScript/Go parsing uses regex fallback when tree-sitter is not installed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_file_size_kb | int | Skip files larger than this. Default 512 KB. | 512 |
include_extensions | tuple[str, ...] | File extensions to parse. Default Python + TS + Go. | ('.py', '.ts', '.tsx', '.js', '.go') |
exclude_dirs | tuple[str, ...] | Directory names to skip. | ('.git', '__pycache__', 'node_modules', '.venv', 'venv', 'dist', 'build', '.tox', '.mypy_cache') |
ingest_repo(repo_path) ¶
Ingest an entire repository into a CodeGraph.
Traverses all eligible source files, parses each to extract code facts, and builds graph edges for cross-file relationships.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
repo_path | str | Absolute or relative path to the repository root. | required |
Returns:
| Type | Description |
|---|---|
CodeGraph | CodeGraph populated with facts and edges. |
find_ai_calls(graph) ¶
Identify AI call sites in the code graph.
Finds both: - Direct calls: OpenAI(), client.chat.completions.create(), etc. - Indirect calls: functions that call AI helpers which are themselves wrappers (requires CDGR tracing, step 2 of SPEC-039 §3.2).
Returns:
| Type | Description |
|---|---|
list[AICallSite] | List of AICallSite, each describing one ungoverned or governed AI call. |
trace_through_wrappers(call_site, graph, max_hops=4) ¶
CDGR-style multi-hop trace: find all call sites that eventually call this AI call.
Starting from call_site (the direct AI call), walks the CALLS edges in reverse (INV_CALLS) to find every function that transitively invokes this AI call - even through N layers of wrapping.
Returns a list of additional AICallSite objects for each discovered transitive call site.
Example (from SPEC-039 §1.2)::
make_client() → returns OpenAI() # direct call (already found)
handle_ticket() → calls make_client() # this function is discovered here
is_governed(call_site) ¶
Return True if call_site already has CRP governance applied.
scan.github_app¶
crp.scan.github_app ¶
GitHub App client - JWT, installation tokens, transient clone, PRs (SPEC-048).
Security invariants
- Installation tokens minted per-use, ~1h TTL, NEVER persisted.
- Private key loaded from env path only, never in code.
- Remediation PRs always to dedicated branches - never direct commits.
GithubAppClient ¶
CRP Comply GitHub App client.
Usage::
client = GithubAppClient.from_env()
token = client.installation_token(installation_id="123")
repos = client.list_repos(installation_id="123")
from_env() classmethod ¶
Create client from environment variables.
installation_token(installation_id) ¶
Mint a short-lived (~1h) installation access token.
The token is returned to the caller and NEVER persisted.
list_repos(installation_id) ¶
List repositories accessible to this installation.
GitHub's endpoint for this is GET /installation/repositories (singular installation, no ID in the path) -- the installation is implied entirely by the installation access token used for auth. There is no /installations/{id}/repositories route; using it returns a 404 regardless of a valid installation_id/token.
fetch_repo(installation_id, owner, repo, branch='main') ¶
Transiently clone a repo and return the temp directory path.
Caller MUST delete the directory after scanning.
create_branch(installation_id, owner, repo, branch_name, from_sha) ¶
Create a new branch from a SHA.
commit_file(installation_id, owner, repo, path, content, branch, message) ¶
Commit a file via the Contents API (creates or updates).
open_pr(installation_id, owner, repo, title, head, base, body) ¶
Open a pull request from head to base.
open_remediation_pr(installation_id, owner, repo, base_branch, file_changes, pr_title, pr_body) ¶
Full remediation flow: create branch → commit files → open PR.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
file_changes | dict[str, str] | dict of | required |
verify_webhook(body, sig_header, webhook_secret) staticmethod ¶
Verify GitHub webhook HMAC-SHA256 signature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
body | bytes | Raw request body bytes. | required |
sig_header | str | The | required |
webhook_secret | str | The GitHub App webhook secret. | required |
Returns:
| Type | Description |
|---|---|
bool | True if signature is valid. |
scan.remediation¶
crp.scan.remediation ¶
CRP Scan - Remediation Engine (SPEC-036).
For each ungoverned AI call site detected by Scan, the Remediation Engine produces the exact code change that governs it. Remediations are delivered as pull-request-ready diffs - never auto-committed.
Three remediation classes (SPEC-036 §2): CODE FIX - governed-client diff (PR) for ungoverned AI calls. CONFIG FIX - safety/policy settings applied in CRP Comply control plane. GUIDED FIX - checkpoint-style task when human judgment is required.
CRITICAL INVARIANT (AGENTS.md §NON-NEGOTIABLE): Remediations are ALWAYS proposals (PRs). NEVER auto-commit to protected branches. The open_pr() method only opens a PR; the caller must hold a valid GitHub installation token and a dedicated (non-protected) branch.
Usage::
engine = RemediationEngine()
proposal = engine.propose_fix(scan_finding)
print(proposal.diff)
# For paid users with GitHub App connected:
pr_url = engine.open_pr(proposal, repo="owner/repo", installation_id="12345")
ScanFinding dataclass ¶
A single finding from CRP Scan.
Maps to the SARIF result structure emitted by crp-scan GitHub Action.
RemediationProposal dataclass ¶
A concrete, actionable remediation for a ScanFinding.
Always produced by the engine before any PR is opened. The diff field contains the unified diff ready for git apply / PR creation.
RemediationEngine ¶
Generate concrete remediation proposals for CRP Scan findings.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
templates_dir | str | None | Optional path to a directory containing Jinja2 | None |
propose_fix(finding) ¶
Produce a remediation proposal for finding.
Determines the remediation class, selects the appropriate template, and renders the diff / config snippet + PR body.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
finding | ScanFinding | A ScanFinding from the scan engine. | required |
Returns:
| Type | Description |
|---|---|
RemediationProposal | RemediationProposal with all fields populated. The |
RemediationProposal | field will be non-empty for code fixes; |
RemediationProposal | config fixes. |
open_pr(proposal, repo, installation_id, base_branch='main') ¶
Open a pull request with the remediation diff.
ALWAYS opens a PR to a dedicated branch - NEVER commits directly to a protected branch (AGENTS.md invariant #11).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
proposal | RemediationProposal | RemediationProposal produced by :meth: | required |
repo | str | Full repository name, e.g. | required |
installation_id | str | GitHub App installation ID for the repo. | required |
base_branch | str | The target base branch for the PR. Default | 'main' |
Returns:
| Type | Description |
|---|---|
str | The URL of the created pull request. |
Note
This method requires crp.scan.github_app (Agent B Round 3) to provide the GitHub App token-minting facility. If not yet installed, returns a descriptive error string.
proposals_for_repo(findings) ¶
Produce proposals for all findings in a repo scan result.
scan.semantic_ingestion¶
crp.scan.semantic_ingestion ¶
CRP Scan - Semantic Code Ingestion (SPEC-039).
Uses CRP's own Contextual Knowledge Fabric to understand large repositories with high accuracy and specificity - detecting AI calls hidden behind wrappers, factory functions, and dependency injection that simple pattern matching misses.
The codebase is ingested as a knowledge graph where files, functions, calls, and data flows are facts and graph edges. CDGR multi-hop retrieval then traces an AI call from its wrapper definition to every call site.
See SPEC-039 §1–§4 for full specification.
Usage (zero-dependency mode - no tree-sitter required)::
ingestion = SemanticCodeIngestion()
graph = ingestion.ingest_repo("/path/to/repo")
findings = ingestion.find_ai_calls(graph)
for finding in findings:
chain = ingestion.trace_through_wrappers(finding, graph)
governed = ingestion.is_governed(finding)
CodeFact dataclass ¶
A single code entity - a fact in the code knowledge graph (SPEC-039 §2.1).
display property ¶
Return the display.
AICallSite dataclass ¶
A detected ungoverned (or governed) AI call site.
CodeGraph dataclass ¶
Knowledge graph of a codebase (SPEC-039 §2).
file_count property ¶
Return the current file count.
function_count property ¶
Return the current function count.
add_fact(fact) ¶
Add a code fact to the graph and update the name index.
add_edge(from_id, to_id, edge_type) ¶
Add a directed edge between two facts, plus an inverse edge.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
from_id | str | Source fact ID. | required |
to_id | str | Target fact ID. | required |
edge_type | str | Relationship type (e.g. | required |
get_neighbours(fact_id) ¶
Return all neighbours of fact_id as (neighbour_id, edge_type) pairs.
lookup_name(name) ¶
Return all facts that define the given symbol name.
SemanticCodeIngestion ¶
Ingest a repository into a code-aware knowledge graph.
Zero external dependency - uses Python's stdlib ast module for Python files. TypeScript/Go parsing uses regex fallback when tree-sitter is not installed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_file_size_kb | int | Skip files larger than this. Default 512 KB. | 512 |
include_extensions | tuple[str, ...] | File extensions to parse. Default Python + TS + Go. | ('.py', '.ts', '.tsx', '.js', '.go') |
exclude_dirs | tuple[str, ...] | Directory names to skip. | ('.git', '__pycache__', 'node_modules', '.venv', 'venv', 'dist', 'build', '.tox', '.mypy_cache') |
ingest_repo(repo_path) ¶
Ingest an entire repository into a CodeGraph.
Traverses all eligible source files, parses each to extract code facts, and builds graph edges for cross-file relationships.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
repo_path | str | Absolute or relative path to the repository root. | required |
Returns:
| Type | Description |
|---|---|
CodeGraph | CodeGraph populated with facts and edges. |
find_ai_calls(graph) ¶
Identify AI call sites in the code graph.
Finds both: - Direct calls: OpenAI(), client.chat.completions.create(), etc. - Indirect calls: functions that call AI helpers which are themselves wrappers (requires CDGR tracing, step 2 of SPEC-039 §3.2).
Returns:
| Type | Description |
|---|---|
list[AICallSite] | List of AICallSite, each describing one ungoverned or governed AI call. |
trace_through_wrappers(call_site, graph, max_hops=4) ¶
CDGR-style multi-hop trace: find all call sites that eventually call this AI call.
Starting from call_site (the direct AI call), walks the CALLS edges in reverse (INV_CALLS) to find every function that transitively invokes this AI call - even through N layers of wrapping.
Returns a list of additional AICallSite objects for each discovered transitive call site.
Example (from SPEC-039 §1.2)::
make_client() → returns OpenAI() # direct call (already found)
handle_ticket() → calls make_client() # this function is discovered here
is_governed(call_site) ¶
Return True if call_site already has CRP governance applied.