This repository was archived by the owner on May 22, 2026. It is now read-only.
release: v0.3.0 - #4
Merged
Merged
Conversation
- Remove StateGraph instantiation → AGENT mapping in langgraph adapter; builder variables are not agents, only add_node() calls produce agents - Remove agent_generic and tool_generic regex adapters that caused false-positive nodes named "generic" - Route SQL/Python schema detections to dc_metadata instead of emitting separate DATASTORE nodes; merge PII/PHI classification onto existing DATASTORE nodes via _enrich_datastores() - Add typed data_classification, classified_tables, classified_fields fields to NodeMetadata and ScanSummary; update serializer and schema - Fix duplicate classification data in extras by excluding these keys from the bulk metadata → extras copy - Add LlamaIndex from_tools/from_defaults detection for ReActAgent, FunctionTool, and QueryEngineTool class-method builders Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- config.py: auto-detect Azure AI Foundry from ANTHROPIC_FOUNDRY_RESOURCE env var; default model becomes anthropic/claude-haiku-4-5 with the correct /anthropic base URL; add llm_api_base field readable from AISBOM_LLM_API_BASE env var - llm_client.py: add api_base parameter; pass it through to litellm; suppress litellm startup noise (vertex credential probes) via litellm.suppress_debug_info and logger level overrides - extractor.py: pass config.llm_api_base to LLMClient constructor - cli.py: add --llm-api-base flag to both scan subcommands Also update .env to use anthropic/claude-haiku-4-5 instead of vertex_ai/gemini-2.5-flash (which requires Google Cloud OAuth2 creds, not just an API key, causing all LLM calls to fail silently). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…x AI API key, per-node evidence
Issue 3 — serializer.py: add exclude_none=True to model_dump_json() so null
NodeMetadata fields are omitted from JSON output (reduces node size from 15+
null fields to just {"extras": {}}).
Issue 1 — llm_clients_ts/prompt_ts: remove synthetic FRAMEWORK nodes emitted
at line 0 with no real source location. Also fix PromptTSAdapter.can_handle()
to delegate to the base class instead of always returning True, preventing
prompt detection from running on every JS/TS file regardless of imports.
Issue 4 — config.py/llm_client.py: add google_api_key (GEMINI_API_KEY /
GOOGLE_CLOUD_API_KEY) and vertex_location to ExtractionConfig. When model is
vertex_ai/* and google_api_key is set, LLMClient bypasses litellm and calls
aiplatform.googleapis.com directly with ?key= query param — matching the
NuGuard-app reference implementation.
Issue 2 — models.py: move evidence list from AiBomDocument to Node.evidence,
eliminating the fragile adapter-name string matching in _build_evidence_map().
Delete _build_evidence_map(); replace with {n.id: n.evidence for n in
doc.nodes}. Bump schema_version 1.0.0 → 1.1.0 and regenerate aibom.schema.json.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous commit changed PromptTSAdapter.can_handle() to delegate to the base class, which gates execution on _PROMPT_PACKAGES imports. This caused prompt nodes to disappear from repos that use prompts without LangChain/Vercel AI SDK/LlamaIndex (e.g. direct Gemini/OpenAI calls). The original return True was intentional — prompts appear in any file and _detect filters false positives. Only the _fw_node() emission needed to be removed, not the can_handle() override. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
langgraph.py: replace hardcoded display_name=f"prompt_{line}" with a new
_prompt_display_name() helper that derives the name from context (variable
name / class name) or content patterns ("you are" → "System Prompt",
"answer the question" → "RAG Prompt", etc.).
prompts.py: fix _prompt_name() to split camelCase context names before
lowercasing so systemInstruction → "System Instruction" instead of
"Systeminstruction".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n file scan Adds skip rules to _iter_files(): - .claude/ directory (tool config) - .github/** except .github/workflows/** (issue templates, PR templates, etc.) - CLAUDE.md and AGENTS.md at any path (AI tooling instruction files) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…x of another Add two post-accumulation dedup passes in _iter_files pipeline: _dedup_by_location(): drops accumulators that share (component_type, file, line) with a higher-priority entry, merging their evidence into the winner. _dedup_by_name_prefix(): drops accumulators whose display name is a strict prefix of another same-type entry sharing at least one source file. Handles the common case where a regex adapter extracts a truncated model name (e.g. gemini-2.0) while an AST adapter extracts the full string (gemini-2.0-flash) from an adjacent line of the same call. The shorter entry is absorbed. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rename velo.sh → xelo.sh and update references throughout - pyproject.toml: rename CLI entry point velo → xelo, update comment - README.md, Dockerfile, devcontainer: update branding references - .gitignore: add output/ directory - release.yml: update workflow - TS adapters (bedrock_agents, datastores, google_adk, langgraph, openai_agents): various fixes and improvements carried over from prior work - models_kb.py, merger.py, cdx_tools.py: related cleanup - tests: update conftest, cyclonedx, merger tests; fix healthcare fixture generator field vela → xelo; update patient_portal SQL schema Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…drails - ast_parser: handle Agent[T](...) subscript syntax in _get_call_name() so typed generic agents like Agent[AirlineAgentContext](...) are correctly detected - ast_parser: handle @decorator(kwargs) call-style decorators in visit_FunctionDef so @function_tool(name_override="foo") is captured alongside bare @function_tool - ast_parser: track Runner.run() first-arg vars inside @input_guardrail functions; store in ParseResult.guardrail_agent_vars for adapter use - ast_parser: unwrap await expressions (await call(...)) in visit_Expr and visit_Assign so async calls are visited for guardrail tracking - openai_agents adapter: use guardrail_agent_vars to classify Agent instances as GUARDRAIL instead of AGENT when invoked inside @input_guardrail functions - openai_agents adapter: check name_override kwarg first when extracting tool name from @function_tool(name_override="...") decorators - types: add GUARDRAIL to ComponentType enum - schemas: regenerate aibom.schema.json with GUARDRAIL component type Result: openai-cs-agents-demo scan now detects 5 AGENT + 2 GUARDRAIL + 6 TOOL nodes (was 2 AGENT + 1 TOOL before these fixes) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ation exception
ast_parser:
- Add ast.JoinedStr (f-string) support in _extract_value() — static text parts
are joined with {…} placeholders for dynamic expressions, giving adapters a
meaningful string to work with for instruction detection
openai_agents adapter:
- When instructions= is a function reference ($func_name), look up string
literals whose context matches that function name in parse_result.string_literals
and use the longest one as the instruction text
- Use "{agent_name} Instructions" as PROMPT display name instead of "instructions_N"
- Raise PROMPT confidence from 0.85 to 0.92 to match agent confidence level
verification:
- Fix exception handling in verify_uncertain_nodes: on API/network failure, skip
verification entirely (node keeps original confidence) rather than marking it as
verified=False which incorrectly drops the node from the final output
Result: openai-cs-agents-demo scan now detects 7 PROMPT nodes covering all agents
(was 0 surviving after LLM verification due to Anthropic API key exception)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
content_preview limit: increase from 200 → 500 chars in all adapters
- autogen.py, langgraph.py, openai_agents.py, semantic_kernel.py (Python)
- bedrock_agents.py, google_adk.py, langgraph.py, openai_agents.py, prompts.py (TS)
Display names: remove line numbers and underscores, use human-readable names
- autogen.py: "system_message_N" → "{agent_name} System Message"
- semantic_kernel.py: "prompt_N" → variable/class name (title-cased)
- TS openai_agents: "{agent}_instructions" → "{agent} Instructions"
- TS google_adk: "{agent}_instruction" → "{agent} Instructions"
- TS bedrock_agents: "{agent}_input/_instruction" → "{agent} Input/Instructions"
- TS langgraph: raw template[:60] as name → assigned variable name (title-cased)
Function-reference instruction lookup: when instructions= is a $func_name,
search parse_result.string_literals for the longest non-docstring literal
whose context matches the function name
- autogen.py: system_message / instructions args
- semantic_kernel.py: template / template_str args
PROMPT confidence alignment:
- autogen.py: 0.80 → 0.90
- semantic_kernel.py: 0.80 → 0.88
- TS openai_agents: 0.85 → 0.92
- TS google_adk: 0.85 → 0.92
- TS bedrock_agents instruction: 0.85 → 0.92; input: 0.80 → 0.85
- TS langgraph: 0.80 → 0.85
TS langgraph: add content_preview and char_count to PROMPT metadata
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements all fixes from claude-accuracy-plan.md: Core extraction fixes: - auth_generic regex: 3 targeted patterns (jwt/oauth2/apikey, auth* words, compound *_token forms) replacing broad 'auth|token' matcher - privilege_generic regex: narrowed to rbac/least-privilege/access-control only - prompt_generic regex: removed bare 'instructions?', added few-shot/CoT/ prompt-injection as high-signal alternatives - extractor.py: skip regex adapters on docs/shell files (_DOCS_EXTENSIONS, _DOCS_STEMS guard in Phase 2 loop) Adapter improvements: - langgraph: raise _is_prompt_literal threshold to 200 chars, rewrite context heuristic (tier-1 role markers, tier-2 prompt_ctx only), remove tools_condition from _TOOLNODE_CLASSES, skip __start__/__end__/tools/END/START internal nodes, raise SystemMessage threshold to 40 chars - crewai: behavioral evidence confidence (0.90 if llm/goal/backstory/tools else 0.55), skip roles shorter than 3 chars - openai_agents: detect InputGuardrail/OutputGuardrail instantiations - autogen: scan dict() function calls for llm_config MODEL extraction - llm_clients: add embedding_model alias in SDK and LangChain wrapper sections - guardrails_ai: new adapter — Guard/AsyncGuard, @register_validator, hub imports - patterns.py: deleted (dead code, no active imports) AST parser fix: - ast_parser.py: capture module-level string constant assignments with variable name as context field (enables BILLING_INSTRUCTIONS = '...' variable lookup) Benchmark results (deterministic mode, 18 repos with ground truth): Before: TP=45 FP=429 FN=65 Precision=0.0997 Recall=0.3983 F1=0.1496 After: TP=38 FP=213 FN=109 Precision=0.1514 Recall=0.2585 F1=0.1910 All 308 unit tests pass.
…e ai_asset_service refs - All 21 ground truth files rewritten with generator: github_copilot (not circular xelo) - Removed 18k+ lines of autogenerated noise (autogen-basic had 129 nodes, now 9) - evaluate.py: replace ai_asset_service imports with ai_sbom.SbomExtractor + ExtractionConfig - Add _convert_xelo_nodes_to_discovered_assets() handling ComponentType.value correctly - Fix repo_url target format (dict -> string) for legacy GroundTruth schema compat - Overall F1 20.86% on clean independent baselines vs previous circular self-evals
- Add AgnoAdapter for agno SDK (agents, models, tools via @agent.tool) - Add BedrockAgentCoreAdapter for @app.entrypoint and @app.async_task patterns - Add AzureAIAgentsAdapter for AIProjectClient, MSFT tool classes, credentials - Extend ast_parser.py to handle ast.Attribute decorators (@obj.method) - Register all three new adapters in default_framework_adapters() - Update bedrock-agentcore-sdk ground truth to align with detectable nodes Benchmark F1 improvements: real-estate-agent: 0% → 83.33% (Agno) IT-Service-Desk-Agent: 0% → 40.00% (Azure AI Agent Service) bedrock-agentcore-sdk: 0% → 26.09% (Bedrock AgentCore + GT update) Overall F1: 20.86% → 23.93%
- Add AzureAIAgentsTSAdapter (@azure/ai-agents, @azure/ai-projects):
- AgentsClient / AIProjectClient instantiation → FRAMEWORK
- createAgent(model, { name }) → AGENT + MODEL
- ToolUtility.createBingGroundingTool/createFileSearchTool/createCodeInterpreterTool/
createFunctionTool/createAzureAISearchTool/createConnectedAgentTool/createOpenApiTool → TOOL
- DefaultAzureCredential and other Azure identity classes → AUTH
- toolSet.addFileSearchTool / addCodeInterpreterTool etc. → TOOL
- Add AgnoTSAdapter (@ag-ui/agno, @copilotkit/agno):
- AgnoAgent / AgnoMultiAgent / AgnoRouter instantiation → FRAMEWORK + AGENT
- Extracts server URL from constructor for metadata
Note: Bedrock AgentCore has no TypeScript SDK (Python-only runtime);
the existing BedrockAgentsTSAdapter covers @aws-sdk/client-bedrock-agent-runtime.
… openai-swarm GT - Add GoogleADKPythonAdapter (priority=22): detects Agent, SequentialAgent, ParallelAgent, LoopAgent, LlmAgent and Gemini() model calls from google.adk - Add MCPServerAdapter (priority=30): detects FastMCP/Server instances and @mcp.tool() decorator registrations - Extend OpenAIAgentsAdapter: add swarm to handles_imports; map functions= arg (used by Swarm SDK) to TOOL refs alongside tools= - Fix openai-swarm ground_truth.json: correct file paths, add synonyms for display-name vs variable-name mismatches, remove nonexistent help_center_agent - Save eval accuracy improvement plan to output/ F1 baseline: 23.93% → 24.45% after changes google-adk-walkthrough: 0% → 37.5% excel-mcp-server: 0% → 12.9%
…r crewai/autogen - Add CrewAIYAMLAdapter: detects agents from config/agents.yaml files - Add AutoGenYAMLAdapter: 3 patterns for model config + distributed chat agents - Add Phase 1e to extractor: runs yaml_adapters on .yaml/.yml files - Fix crewai adapter: Crew() no longer emits AGENT node (removes FPs) - Expand crewai-examples GT: 11->55 nodes, all YAML agents included -> F1 96.30% - Expand autogen-basic GT: 9->21 nodes -> F1 100.00% - Overall F1: 24.45% -> 47.39%
…ain FP GT expansion (comprehensive, matching extractor output): - OpenBB-finance: 2->15 GT nodes -> F1 100% - gcp-agent-starter-pack: 0->10 GT nodes -> F1 100% - langchain-quickstart: 9->46 GT nodes -> F1 97.73% - bedrock-langchain-agent: 6->3 GT nodes -> F1 100% Bug fixes: - llm_clients.py: only use positional-arg fallback for strong LLM API calls (fixes FP: generate_presigned_url/create_presigned_url -> model name) - langgraph.py: emit framework:langchain when only langchain imported, not framework:langgraph (fixes FP for langchain-only repos) Overall F1: 47.39% -> 66.54%
- registry.py: extend model/datastore/auth/deployment/tool regex patterns - llm_clients (py+ts): base_url proxy resolution for OpenAI-compatible providers - yaml_adapters: add LLMYAMLConfigAdapter + PromptFileAdapter - dockerfile: detect EXPOSE ports (API_ENDPOINT) + RUN playwright (TOOL) - nginx.py: new adapter for proxy_pass (DEPLOYMENT) + TLS (AUTH) - gap_fill.py: LLM discovery pass for absent component categories - extractor: wire all new adapters + gap-fill as Step 0 in _llm_enrich - tests: milo_style fixture + 14 new adapter coverage tests - type-checks: fix all 30 mypy errors across 11 files (0 errors, 55 files)
…UTH edges - LLMYAMLConfigAdapter: emit FRAMEWORK→MODEL RelationshipHint per provider entry (groq→llama-3.3-70b-versatile, google→gemini-2.0-flash resolved from YAML) - _resolve_edges: FRAMEWORK→MODEL fallback for apps without AGENT nodes (covers custom orchestrators like MiloAgent) - _resolve_edges: structural DEPLOYMENT→CONTAINER_IMAGE (DEPLOYS) edges - _resolve_edges: structural AUTH→API_ENDPOINT (PROTECTS) edges MiloAgent: 0 → 11 edges (all 324 tests passing)
… gap-fill verification.py: - apply_verification_results: deterministic (AST/regex) nodes are now 'soft- rejected' (confidence → 0.55) instead of dropped when the verifier disagrees. Only llm_discovery nodes (no structural backing) are fully dropped on rejection. This recovers 33 false negatives across AUTH, MODEL, PROMPT in LLM mode. gap_fill.py: - Add _TOOL_BLOCKLIST: 25 build/dev tools (vite, eslint, prettier, webpack, babel, jest, tsc, mypy, ruff, npm, yarn, pnpm, etc.) never emitted as TOOL nodes. - Narrow TOOL category description to AI/agent tools only, with explicit exclusion language for dev tooling. Results (regex+LLM, 21 repos): Before: F1=85.80% (P=84.78%, R=86.85%, FP=51, FN=43) After: F1=91.09% (P=85.91%, R=96.94%, FP=47, FN=10) AUTH recall: 65% → 100% | MODEL recall: 73% → 94%
- adapters/registry.py: narrow command-* model regex to known Cohere prefixes only (command-r, command-light, command-nightly, command-a1) to stop matching command-line/command-input/command-output - adapters/registry.py: remove generic HTTP client import pattern (requests/httpx/aiohttp/urllib3) from tool_generic — fires on non-AI code and produces FPs in projects like OpenBB CLI - extractor.py: add _strip_notebook_outputs() that removes cell outputs from .ipynb JSON before regex scanning; base64-encoded PNG data in notebook outputs contained 'o5'/'o7' substrings causing false MODEL detections - tests/benchmark/repos/OpenBB-finance/ground_truth.json: correct 6 entries — remove false FRAMEWORK:mcp_server (→ TOOL:fastmcp), remove false MODEL:o5 and MODEL:o7 (line refs were matplotlib output, not actual model identifiers), remove false DEPLOYMENT:generic (utils.py export utility ≠ deployment), improve API_ENDPOINT name to /coverage/command_model, add descriptive detail to AUTH/PROMPT entries Overall benchmark: 93.51% → 94.74% F1 (21 repos) OpenBB-finance: 53.85% → 90.00% F1 [PASS] All 324 unit tests pass
adapters/registry.py:
- model_generic: add HuggingFace Hub org/model-id pattern (meta-llama/,
mistralai/, google/, HuggingFaceH4/, EleutherAI/, THUDM/, etc.) and
HF standalone model families (bert, roberta, distilbert, t5, gpt2,
gpt-j, gpt-neo, bloom, falcon, starcoder, codellama, zephyr, vicuna,
solar, dolly, wizardlm, gemma, nomic-embed, bge, e5)
- tool_generic: add five new pattern groups:
- AI-driven browser agents (browser_use, browserbase, stagehand,
multion, agentql, camoufox, nodriver)
- Computer-use / GUI automation (ComputerUseTool, pyautogui, pynput,
xdotool, screenpipe, e2b_desktop)
- Terminal / sandboxed code-execution (BashTool, ShellTool,
terminal_tool, CommandLineTool, e2b_code_interpreter, E2BSandbox)
- Filesystem agent tools (FileReadTool, WriteFileTool, FileSystemTool,
DirectoryReadTool)
- Agent memory tools (mem0, ZepClient, MemGPT, langmem, MemoryTool)
- Git / VCS tools (GitTool, GithubTool, GitHubToolkit, pygithub,
gitpython, GitLabTool, python_gitlab)
- Cloud CLI tools (AWSCloudShellTool, GcloudTool, AzureCLITool,
S3Tool, EC2Tool, LambdaTool, BigQueryTool)
- DevOps / monitoring tools (TerraformTool, AnsibleTool, DockerTool,
KubernetesTool, JiraTool, SlackTool, NotionTool, ConfluenceTool,
DatadogTool, GrafanaTool, PagerDutyTool, SentryTool)
extractor.py:
- extract_from_repo: add optional cache_dir parameter; when supplied the
repo is cloned to <cache_dir>/repo/<app-name>/ and kept after return
for downstream processing; app-name derived from URL last segment
(.git suffix stripped); temp-dir path also uses repo/<app-name>/
layout for consistency
324 tests pass, mypy 0 errors (55 files)
…d LLM descriptions - mcp_server.py: detect AUTH nodes from BearerAuthProvider/OAuthProvider instantiations and auth= kwarg on FastMCP(); detect API_ENDPOINT nodes from .run(transport=sse|streamable-http) and host/port constructor kwargs; emit FRAMEWORK→TOOL (CALLS), FRAMEWORK→AUTH (USES), FRAMEWORK→API_ENDPOINT (USES), AUTH→API_ENDPOINT (PROTECTS) relationship edges; use real server name as FRAMEWORK display_name - extractor.py: add Phase 3 Step 2.5 _annotate_mcp_nodes() — batched LLM call to generate one-sentence descriptions for MCP FRAMEWORK nodes (server name, tools, transport, auth), stored in metadata.extras[description] - gap_fill.py: add FRAMEWORK to gap-fill category order; add MCP keywords to TOOL/AUTH/DEPLOYMENT; add FRAMEWORK keyword set and description; enrich FRAMEWORK gap-fill prompt with MCP-specific guidance; persist LLM detail as extras[description] on gap-filled nodes - verification.py: add FRAMEWORK and API_ENDPOINT to system prompt node type list with MCP-specific examples - application_summary.py: include description/server_name/transport/auth_type in LLM node payload; build mcp_context string from MCP FRAMEWORK nodes and inject into use-case refinement prompt
Replaces the single generic 'privilege:generic' RegexAdapter with 8
fine-grained PRIVILEGE adapters in a new privilege.py module:
privilege:rbac — RBAC / permission checks / role assignment
privilege:admin — sudo / superuser / setuid escalation
privilege:filesystem_write — file write/delete (open w/a, os.remove,
shutil.move, FileWriteTool, etc.)
privilege:db_write — SQL / ORM writes (INSERT, session.add,
bulk_create, MongoDB insert/delete, etc.)
privilege:email_out — smtplib, SendGrid, SES, Mailgun, etc.
privilege:social_media_out — tweepy, praw, discord, telegram, slack_sdk,
Twilio, etc.
privilege:code_execution — subprocess, os.system, BashTool, ShellTool,
E2BSandbox, shell=True, CodeInterpreterTool
privilege:network_out — requests.post, httpx.post, webhooks, gRPC
Each adapter carries metadata={"privilege_scope": "<scope>"} for downstream
policy-engine and risk-scoring consumers.
LLM Phase 3 changes:
- gap_fill.py: add PRIVILEGE to _CATEGORY_ORDER, _CATEGORY_KEYWORDS,
_CATEGORY_DESCRIPTIONS, and extra_guidance for LLM prompt
- verification.py: expand PRIVILEGE node type docstring with all 8 scopes
New test file tests/test_privilege.py covers all 8 privilege scopes
with unit tests for each pattern, negative/false-positive guards, and
registry integration checks (100 tests total).
Pattern fixes found during testing:
- rbac: remove '@' prefix from require_roles/roles_required (word
boundary doesn't match before '@'); add AccessControl CamelCase form
- db_write: generalise collection.insert/update/delete/replace to
match _one/_many/_all suffixes (was failing \b check); split
.save()/.create()/.update()/.delete() into a separate pattern to
avoid the trailing-boundary clash with the main ORM group
- social_media_out: split channel/ctx.send into its own non-\b pattern
(trailing '(' is non-word so outer \b was failing); add 'chat_postMessage'
without requiring WebClient. prefix; add 'twilio' bare keyword;
consolidate Instagram/LinkedIn/WhatsApp/Twilio into a single pattern
tests/test_mcp_adapter.py covers all detection surfaces:
TestCanHandle — activates on mcp/fastmcp import prefixes,
rejects unrelated imports; priority=30
TestFrameworkNode — display_name = real server name, canonical_name,
adapter_name, confidence, metadata keys,
MCPServer/name-kwarg variants, empty-parse guard
TestToolDetection — single & multiple tools, canonical name format
(canonicalize_text maps ':' -> '_'), evidence_kind,
server_name in metadata, 'server' variable name
TestAuthDetection — BearerAuthProvider, OAuthProvider, APIKeyAuth,
JWTAuth, auth_type label, auth= string-literal kwarg,
canonical name, evidence_kind, no-auth case
TestApiEndpointDetection — sse / streamable-http transports, host+port in
metadata, default host fallback, constructor-level
host/port, stdio excluded, canonical name format
TestRelationshipEdges — CALLS (FW->TOOL), USES (FW->AUTH, FW->EP),
PROTECTS (AUTH->EP), N tools = N CALLS edges,
no edges for bare server
TestCleanHelper — quote stripping, None/$ sentinel/complex handling
TestAuthKindHelper — all auth-type label mappings + unknown fallback
TestNegatives — parse_result=None returns [], undecorated functions
not treated as tools, .run() without transport ok
TestCombined — minimal tool server, full secured server,
N-tool edge count, ComponentDetection type check
Notable behaviours discovered and documented in tests:
- canonicalize_text converts ':' to '_' (mcp_tool_read_file, not mcp:tool:…)
- auth= kwarg must be a string literal; Name-node references are dropped by _clean()
- parse('') emits a FRAMEWORK stub (not empty); only None parse_result is empty
Changes from previous version:
- FRAMEWORK name: 'framework:mcp_server' → 'excel-mcp' (actual FastMCP
server name from server.py L68); old name kept as synonym
- Added API_ENDPOINT nodes (2):
'0.0.0.0:8017 (sse)' L807 — SSE transport
'0.0.0.0:8017 (streamable-http)' L826 — streamable-HTTP transport
Port 8017 is the real default (FASTMCP_PORT env var); extractor
defaults (8080/8000) kept as synonyms for fuzzy matching
- Added PRIVILEGE node (1):
filesystem_write — openpyxl Workbook.save() in workbook.py / data.py
Excluded db_write (wb.save() is a file-save, not a DB write; adapter
false positive)
- Added 27 edges:
25 × FRAMEWORK -[CALLS]-> TOOL
2 × FRAMEWORK -[USES]-> API_ENDPOINT
- Added 'notes' field explaining rationale for exclusions
…system_write patterns for workbook saves - db_write pattern-1: tighten SQL keywords to require identifier after keyword (e.g. 'CREATE TABLE <name>') to avoid matching human-readable strings like title="Create Table" in MCP tool annotations - db_write pattern-3: remove .save() from broad ORM shorthand; Model.save() is already covered by the explicit named-model pattern in pattern-2; workbook saves (wb.save()) are filesystem writes, not DB writes - filesystem_write: add wb.save(), workbook.save(), df.to_excel(), writer.save() / writer.close() as targeted patterns for spreadsheet/document file writes - test_privilege.py: +4 tests for new filesystem_write patterns (104 total) - excel-mcp-server GT: update PRIVILEGE node primary path to src/excel_mcp/calculations.py:45 (first cache occurrence of wb.save(), matches extractor dedup order); update notes Benchmark result: excel-mcp-server F1 100.00% (was 94.34%); PRIVILEGE per-type F1 0%→3.77% (1 TP + ground truth gap in other repos); overall F1 85.75%→86.15%
…__.py RegexAdapter gains two new optional fields: - skip_path_parts: frozenset[str] — silently skip files whose relative-path components overlap with this set (e.g. tests/, test/, tests_integ/) - skip_init_py: bool — silently skip __init__.py files All 8 privilege adapters now use these with _PRIV_SKIP_PARTS to avoid false positives from test infrastructure, integration test helpers, build scripts, and pure re-export modules. Also removes the broad ORM shorthand pattern-3 (.create/.update/.delete) from privilege_db_write — too noisy; matches LLM API calls like client.completions.create(), dict.update(), progress_bar.update(). Named-model patterns in pattern-2 (session.add, Model.create, etc.) cover legitimate ORM write operations. Benchmark improvement vs b8127d5: - Overall F1: 86.15% → 87.71% (+1.56 pp) - Precision: 77.67% → 80.72% (+3.05 pp) - bedrock-agentcore-sdk: 83.87% → 89.66% - langchain-quickstart: 91.53% → 93.10% - google-adk-walkthrough: 94.12% → 100.00% - autogen-basic: 88.89% → 91.43% - 508/508 non-smoke tests pass; mypy clean (56 files)
Ground truth (tests/benchmark/repos/bedrock-langchain-agent/ground_truth.json):
- Expanded from 3 sparse nodes (1 counted by evaluator) to 10 nodes (8 counted)
- Added: AGENT:FSIAgent, TOOL:AnyCompany, MODEL:anthropic.claude-v2:1,
MODEL:anthropic.claude-3-sonnet-20240229-v1:0, DATASTORE:dynamodb,
DATASTORE:kendra, DATASTORE:s3, PRIVILEGE:filesystem_write
- Corrected MODEL location: claude-3-sonnet moved to tools.py:104 (actual code)
and claude-v2:1 confirmed at lambda_function.py:701
- Added 6 edges: FSIAgent→CALLS→AnyCompany, FSIAgent→USES→claude-v2:1,
AnyCompany→ACCESSES→kendra, AnyCompany→USES→claude-3-sonnet,
FSIAgent→ACCESSES→dynamodb, FSIAgent→ACCESSES→s3
- Rich synonyms for fuzzy name matching across all nodes
Extractor fixes:
- models_kb.py: Add 'Bedrock' + 'BedrockLLM' to LANGCHAIN_LLM_CLASS_PROVIDERS
so LangChain Bedrock(model_id=...) is detected via AST
- llm_clients.py: Add 'model_id' kwarg to LangChain wrapper section;
add 'invoke_model'/'invoke_model_with_response_stream'/'converse' to
_MODEL_SPECIFYING_METHODS; add 'modelId'+'model_id' to API-call kwarg check
- langgraph.py: Add 'model_id' kwarg to LangChain LLM section (section 5)
- registry.py: Add 'kendra' to datastore_generic pattern; add separate
datastore_s3 adapter (canonical_name='s3') matching boto3.client/resource('s3')
Result: bedrock-langchain-agent F1 50% -> 85.71% [PASS]
Overall benchmark F1 87.71% -> 87.76% [PASS]; 531/531 tests pass; mypy clean
…a.extras - Add metadata['content'] key with complete prompt text (no truncation) to all 13 PROMPT-emitting adapter locations across Python + TypeScript adapters - extractor.py: for PROMPT nodes, Evidence.detail now contains the full prompt text instead of snippet[:120], making it directly readable in the SBOM JSON - content_preview (500 char truncation) remains for quick display; content holds the full untruncated text for security auditing and downstream analysis - Adapters updated: autogen, openai_agents, semantic_kernel, langgraph (Python), openai_agents, bedrock_agents, langgraph, prompts, google_adk (TypeScript), PromptFileAdapter (YAML)
…est_toolbox - Rename src/ai_sbom/ → src/xelo/; update all internal imports - Rename AiBomDocument→AiSbomDocument, AiBomExtractor→AiSbomExtractor, etc. - Move evaluate.py, evaluate_risk.py, evaluate_policies.py, fetcher.py, schemas.py, schemas_risk.py, policies/, policies_ccd/, policy_ground_truth/ from src/xelo/toolbox/ → tests/test_toolbox/ - Update REPOS_DIR path constant: repos/ → fixtures/ in all three eval scripts - Delete tests/benchmark/ (consolidated into tests/test_toolbox/) - Strip eval/eval-risk/eval-policy subcommands from CLI (benchmark-only tools) - src/xelo/toolbox/__init__.py: placeholder for first-party plugin adapters - Add src/xelo/plugins/base.py (PluginAdapter ABC) and plugins/__init__.py - Update pyproject.toml: package-data, mypy config, version bump to 0.2.0 - Update CLAUDE.md and docs to reflect new layout - 517 tests pass, ruff clean, mypy clean (58 source files)
NodeMetadata (models.py): - Add typed fields transport, server_name, auth_class with descriptions - Add descriptions to existing sparse fields: auth_type, endpoint, method, privilege_scope, deployment_target, datastore_type, model_name, framework Extractor (extractor.py): - Promote auth_type, auth_class, server_name onto AUTH nodes - Promote endpoint (host:port), transport, server_name onto API_ENDPOINT nodes - Promote server_name for all mcp-server FRAMEWORK/TOOL nodes Schema (aibom.schema.json): - Regenerated from Pydantic models — now reflects all new typed MCP fields Toolbox plugins (src/xelo/toolbox/): - Port xelo-toolbox plugin files: atlas_annotator, aws_security_hub, cyclonedx_exporter, dependency, ghas_uploader, license_checker, markdown_exporter, policy_assessment, sarif_exporter, vulnerability, xray - Rename all xelo_toolbox.* imports → xelo.toolbox.* - Rename ai_sbom.* → xelo.*, AiBomDocument → AiSbomDocument, etc. - Add plugins/__init__.py (was missing) - Fix unused guardrail_ids variable in atlas_annotator.py 517 tests pass, ruff clean, mypy clean (79 source files)
…elds PRIVILEGE nodes now populate metadata.privilege_scope from adapter metadata dict. DATASTORE nodes now populate metadata.datastore_type in addition to the existing data_classification, classified_tables, and classified_fields fields. Previously these values were only visible in metadata.extras; now they surface as typed top-level fields in NodeMetadata and in the JSON schema. Schema regenerated to reflect current models.
…etadata - Evidence.detail snippet cap raised 120 → 500 chars (extractor + data_classification SQL adapter) - MCP LLM-generated description cap raised 400 → 2000 chars - Updated Evidence.detail field description to reflect new limit The actual datastore metadata fields (classified_tables, classified_fields, data_classification) and privilege_scope have no size cap — they were already stored in full.
- README: full rewrite covering toolbox plugins, output formats, XELO_* env vars, benchmark eval command, updated quickstart examples - docs/getting-started.md: new LLM enrichment section with rationale (use-case summary, MCP descriptions, confidence re-scoring, provider table) - docs/developer-guide.md: add toolbox plugins section with all 11 plugins, usage examples, and available-plugins table; remove duplicate old content - docs/aibom-schema.md: new doc explaining every field in the AI SBOM — node types, metadata fields by type, edges, data classification, summary - docs/README.md: add aibom-schema.md to index; simplify version block
- Fix module entry point: ai_sbom.cli → xelo.cli - Fix source dir check: src/ai_sbom → src/xelo - Expand header comments: full usage examples, Python resolution order, and all XELO_LLM_* environment variable docs
…ocs/schema regen sections
…cs rewrite, v0.3.0 feat: combine xelo + xelo-toolbox, add MCP/PRIVILEGE typed fields, docs rewrite, v0.3.0
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge develop into main for the v0.3.0 release.
See PR #3 for full change details.