Skip to content

feat(project): add runtime agent framework fingerprinting - #37

Merged
trionnemesis merged 1 commit into
mainfrom
agent/framework-fingerprint
Aug 10, 2026
Merged

feat(project): add runtime agent framework fingerprinting#37
trionnemesis merged 1 commit into
mainfrom
agent/framework-fingerprint

Conversation

@trionnemesis

Copy link
Copy Markdown
Owner

What changed

Adds the first bounded PR from #32's remaining repository-inspection work: a deterministic, read-only runtime framework fingerprint.

  • Classifies repositories as confirmed, likely, configuration_only, not_detected, or unsupported.
  • Keeps application runtime agents separate from Claude Code, Codex, Gemini CLI, Cursor, and MCP development configuration.
  • Recognises LangGraph/LangChain, OpenAI Agents SDK, AutoGen, Semantic Kernel, CrewAI (including current JSON-first crew.jsonc projects), and framework-neutral Python/Node tool calling.
  • Uses Python AST and bounded JS/TS structural matching; repository code is never imported or executed.
  • Returns only relative paths and bounded structural evidence. Source text never enters the report.
  • Refuses to follow symlinks, skips generated/vendor/test/config trees for runtime attribution, and reports incomplete parsing as unsupported rather than absence.
  • Records the intentional next boundary in the roadmap: compose this result into the inspection DTO/CLI in a later PR without widening PurpleVerdict.

Why

The current discovery layer can inventory coding-agent surfaces, but it cannot answer whether the repository itself implements a runtime AI agent. Treating .mcp.json or CLAUDE.md as runtime proof would overstate what AgentSec knows. This change creates the evidence-backed classifier without coupling it to the dashboard or Purple evaluator yet.

The package/import/builder vocabulary was checked against current primary documentation:

Safety and semantics

  • Read-only; no shell, hook, package script, import, or agent entrypoint is executed.
  • No MCP capability is added.
  • No scenario, axis, verdict, gate, or publication policy changes.
  • A normal non-AI repository is not_detected, never secure.
  • AgentSec's own checkout is classified configuration_only, not as a runtime agent.

Checks

  • make check
  • pytest -q --cov=agentsec --cov-report=term-missing — 80.82% total coverage (72% floor)
  • 18 focused fingerprint cases covering framework builders, dependency-only candidates, config-only repositories, ordinary repositories, custom tool calling, JSONC, malformed manifests, source non-execution, path stability, symlink escape, and evidence non-disclosure

Refs #32

@trionnemesis
trionnemesis marked this pull request as ready for review August 10, 2026 07:58
@trionnemesis
trionnemesis merged commit f3e51c0 into main Aug 10, 2026
5 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31d6e11242

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

resolved = _resolve_call_name(node.func, aliases)
short = resolved.rsplit(".", 1)[-1]
for framework, _module in imported_frameworks:
if short in _FRAMEWORKS[framework].builders:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve builder calls against the framework import

When a file imports a recognized framework but also calls an unrelated local function whose short name matches one of that framework's builders, this check records a high-confidence builder call. For example, import langgraph followed by a locally defined StateGraph() is reported as a confirmed LangGraph runtime because the resolved callee is reduced to its final component and never checked against the imported module. Associate the resolved call with the corresponding framework import before raising confidence.

Useful? React with 👍 / 👎.

Comment on lines +449 to +450
if re.search(rf"\b(?:new\s+)?{re.escape(builder)}\s*\(", text):
self.observations[framework].builders.add((relative, builder, language))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude comments and strings from JavaScript builder matching

When a JavaScript or TypeScript file imports a recognized package but mentions a builder only in a comment or string, this raw-source regex still records a builder call and upgrades the repository to confirmed. A commented-out new Agent(...) beside an active @openai/agents import is therefore reported as a live runtime entrypoint. Tokenize or otherwise remove comments and literals before using builder matches as high-confidence evidence.

Useful? React with 👍 / 👎.

Comment on lines +627 to +630
elif isinstance(node, ast.ImportFrom) and node.module:
framework = _framework_for_module(node.module)
if framework:
frameworks.add((framework, node.module))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore relative imports when attributing frameworks

When application code uses a relative module whose name matches a framework, ast.ImportFrom exposes the module name without the leading dots and records the relative import as third-party framework evidence. For example, from .langgraph import StateGraph is classified as a LangGraph import and its call produces a confirmed runtime even though the module is entirely local. Check node.level before attributing an ImportFrom node to an external framework.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant