Skip to content

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 .py.j2 and .yaml.j2 templates for provider-specific remediations. If None, built-in string templates are used.

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 diff

RemediationProposal

field will be non-empty for code fixes; config_snippet for

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:propose_fix.

required
repo str

Full repository name, e.g. "owner/repo".

required
installation_id str

GitHub App installation ID for the repo.

required
base_branch str

The target base branch for the PR. Default main.

'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. CALLS, CONTAINS).

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 {path: new_content}.

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 X-Hub-Signature-256 header value (sha256=...).

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 .py.j2 and .yaml.j2 templates for provider-specific remediations. If None, built-in string templates are used.

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 diff

RemediationProposal

field will be non-empty for code fixes; config_snippet for

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:propose_fix.

required
repo str

Full repository name, e.g. "owner/repo".

required
installation_id str

GitHub App installation ID for the repo.

required
base_branch str

The target base branch for the PR. Default main.

'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. CALLS, CONTAINS).

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.