{"schema_version":"2","scorer_version":"0.1.0","scoring_version":"prov-4+qual-2+sec-2","generated_at":"2026-11-13T23:57:06.531997Z","repo":{"ref":{"host":"github.com","owner":"mrphrazer","name":"ghidra-headless-mcp"},"default_branch":"main","clone_url":"https://github.com/mrphrazer/ghidra-headless-mcp.git","html_url":"https://github.com/mrphrazer/ghidra-headless-mcp","description":"Headless Ghidra MCP server — giving AI agents deep reverse-engineering capabilities.","stars":198,"forks":23,"is_fork":false,"is_archived":false,"is_private":false,"size_kb":469,"primary_language":"Python","license_spdx":"GPL-2.0","created_at":"2026-03-08T05:51:54Z","pushed_at":"2026-09-16T14:34:06Z","security_policy_url":null,"distribution":{"owner_created_at":"2015-02-03T17:12:57Z","owner_type":"User","release_assets":[]}},"commit_sha":"307b461cdbbedfbafb3771333f21875b745f940e","profile":{"lines_by_language":{"Python":20840,"C":10},"files_by_category":{"test":22,"source":12,"docs":6,"config":3,"binary":2},"source_lines":16599,"test_lines":4251},"pillars":{"ai_provenance":{"score":0.0,"confidence":0.43,"coverage":1.0,"label":"possible","breakdown":{"attributed_share":0.0,"suspected_share":0.0986,"estimated_share":0.0,"ai_artifacts":0.0,"flagged_work_units":1.0,"flagged_releases":null},"summary":"Weak signals consistent with AI-assisted development."},"quality":{"score":64.7,"confidence":0.9,"coverage":1.0,"label":"weak","breakdown":{"complexity":41.7,"duplication":56.5,"lint":72.9,"testing":87.8,"docs":85.0,"type_safety":0.0,"process":null},"summary":"Code quality is weak: test-to-source ratio 0.26, CI runs tests; 29% of function code in complex or long functions; 10% duplicated lines. Weakest area: type safety."},"security":{"score":77.5,"confidence":0.85,"coverage":1.0,"label":"adequate","breakdown":{"secrets":100.0,"sast":96.4,"dependencies":null,"ci_actions":66.7,"posture":0.0,"scorecard":null},"summary":"Security posture is adequate: 1 high-severity SAST findings; 2 high-severity workflow issues. Weakest area: posture."}},"analyzer_results":[{"analyzer":"sec-scorecard","pillar":"security","status":"skipped","tool_version":null,"metrics":{},"findings":[],"evidence":[],"data":{},"error":"no published OpenSSF Scorecard result for this repo"},{"analyzer":"sec-registry","pillar":"security","status":"ok","tool_version":null,"metrics":{"declared":3.0,"checked":3.0,"missing":0.0,"recent":0.0,"lookup_errors":0.0},"findings":[],"evidence":[],"data":{"phantom":false},"error":null},{"analyzer":"ai-attribution","pillar":"ai_provenance","status":"ok","tool_version":"signatures-v1","metrics":{"commits":11.0,"attributed_commits":0.0,"attributed_commit_share":0.0,"effective_loc":21109.0,"attributed_loc":0.0,"attributed_loc_share":0.0,"tools_detected":0.0},"findings":[],"evidence":[],"data":{"attributed_loc_by_commit":{},"tools":{},"monthly":{"2026-03":{"loc":18062,"attributed_loc":0},"2026-05":{"loc":68,"attributed_loc":0},"2026-06":{"loc":2082,"attributed_loc":0},"2026-07":{"loc":91,"attributed_loc":0},"2026-08":{"loc":806,"attributed_loc":0}}},"error":null},{"analyzer":"ai-artifacts","pillar":"ai_provenance","status":"ok","tool_version":"signatures-v1","metrics":{"artifacts":0.0,"workflow_agents":0.0,"tools_configured":0.0},"findings":[],"evidence":[],"data":{"artifacts":[],"workflows":[],"tools":[]},"error":null},{"analyzer":"ai-velocity","pillar":"ai_provenance","status":"ok","tool_version":"prov-4","metrics":{"work_units":7.0,"flagged_units":1.0,"pre_ai_era_bursts":0.0,"pre_ai_era_measured_loc":0.0,"pre_ai_era_burst_loc":0.0,"measured_loc":3257.0,"flagged_loc":2082.0,"flagged_loc_share":0.6392385630948726,"max_loc_per_hour":520.5,"median_loc_per_hour":100.5,"pr_timed_units":0.0,"excluded_import_loc":17852.0},"findings":[],"evidence":[{"kind":"velocity-burst","description":"2,082 lines of first-party code in ~4.0h (520 lines/hour, 1 commit session)","ref":"10a40eb99bdffe438560527029ca64ca7cdaa038","url":"https://github.com/mrphrazer/ghidra-headless-mcp/commit/10a40eb99bdffe438560527029ca64ca7cdaa038","weight":0.15614999999999998}],"data":{"flagged_loc_by_commit":{"10a40eb99bdffe438560527029ca64ca7cdaa038":2082},"excluded_import_commits":["5ff9c811186d5a9d5b65e77a1c762806209731a8"]},"error":null},{"analyzer":"ai-release-velocity","pillar":"ai_provenance","status":"skipped","tool_version":null,"metrics":{},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"ai-stylometry","pillar":"ai_provenance","status":"ok","tool_version":null,"metrics":{"lines_scanned":16599.0,"index":0.0,"count:narrated_steps":0.0,"per_kloc:narrated_steps":0.0,"files:narrated_steps":0.0,"count:placeholder":0.0,"per_kloc:placeholder":0.0,"files:placeholder":0.0,"count:emoji_in_code":0.0,"per_kloc:emoji_in_code":0.0,"files:emoji_in_code":0.0,"count:assistant_voice":0.0,"per_kloc:assistant_voice":0.0,"files:assistant_voice":0.0},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"sec-secrets","pillar":"security","status":"ok","tool_version":"gitleaks 8.30.1","metrics":{"secrets_current":0.0,"secrets_history":0.0,"secrets_nonprod":0.0,"secrets_low_confidence":0.0},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"sec-sast","pillar":"security","status":"ok","tool_version":"opengrep 1.33.1; rules f1d2b562b414","metrics":{"findings":5.0,"findings_high":1.0,"findings_medium":4.0,"findings_low":0.0,"weighted_findings":1.75,"scanned_lines":16599.0,"parse_errors":0.0},"findings":[{"rule_id":"opengrep/python.lang.security.audit.dangerous-subprocess-use-audit","severity":"high","message":"Detected subprocess function 'Popen' without a static string. If this data can be controlled by a malicious actor, it may be an instance of command injection. Audit the use of this call to ensure it is not controllable by an external resource. You may consider using 'shlex.escape()'. [confidence low]","path":"ghidra_headless_mcp/ghidra_cli.py","line":839,"fingerprint":"python.lang.security.audit.dangerous-subprocess-use-audit:ghidra_headless_mcp/ghidra_cli.py:839"},{"rule_id":"opengrep/python.lang.security.audit.exec-detected","severity":"medium","message":"Detected the use of exec(). exec() can be dangerous if used to evaluate dynamic content. If this content can be input from outside the program, this may be a code injection vulnerability. Ensure evaluated content is not definable by external sources. [confidence low]","path":"ghidra_headless_mcp/backend.py","line":3145,"fingerprint":"python.lang.security.audit.exec-detected:ghidra_headless_mcp/backend.py:3145"},{"rule_id":"opengrep/python.lang.security.audit.eval-detected","severity":"medium","message":"Detected the use of eval(). eval() can be dangerous if used to evaluate dynamic content. If this content can be input from outside the program, this may be a code injection vulnerability. Ensure evaluated content is not definable by external sources. [confidence low]","path":"ghidra_headless_mcp/backend.py","line":3148,"fingerprint":"python.lang.security.audit.eval-detected:ghidra_headless_mcp/backend.py:3148"},{"rule_id":"opengrep/python.lang.security.audit.exec-detected","severity":"medium","message":"Detected the use of exec(). exec() can be dangerous if used to evaluate dynamic content. If this content can be input from outside the program, this may be a code injection vulnerability. Ensure evaluated content is not definable by external sources. [confidence low]","path":"ghidra_headless_mcp/fake_ghidra.py","line":1193,"fingerprint":"python.lang.security.audit.exec-detected:ghidra_headless_mcp/fake_ghidra.py:1193"},{"rule_id":"opengrep/python.lang.security.audit.eval-detected","severity":"medium","message":"Detected the use of eval(). eval() can be dangerous if used to evaluate dynamic content. If this content can be input from outside the program, this may be a code injection vulnerability. Ensure evaluated content is not definable by external sources. [confidence low]","path":"ghidra_headless_mcp/fake_ghidra.py","line":1196,"fingerprint":"python.lang.security.audit.eval-detected:ghidra_headless_mcp/fake_ghidra.py:1196"}],"evidence":[],"data":{"rulesets":["python"]},"error":null},{"analyzer":"sec-deps","pillar":"security","status":"skipped","tool_version":null,"metrics":{},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"sec-actions","pillar":"security","status":"ok","tool_version":"zizmor 1.32.0","metrics":{"workflows":1.0,"findings":3.0,"findings_high":2.0,"findings_medium":0.0,"findings_low":1.0,"penalty":24.3},"findings":[{"rule_id":"zizmor/unpinned-uses","severity":"high","message":"unpinned action reference [confidence high]","path":".github/workflows/ci.yml","line":17,"fingerprint":"unpinned-uses:.github/workflows/ci.yml:17"},{"rule_id":"zizmor/unpinned-uses","severity":"high","message":"unpinned action reference [confidence high]","path":".github/workflows/ci.yml","line":20,"fingerprint":"unpinned-uses:.github/workflows/ci.yml:20"},{"rule_id":"zizmor/artipacked","severity":"low","message":"credential persistence through GitHub Actions artifacts [confidence low]","path":".github/workflows/ci.yml","line":16,"fingerprint":"artipacked:.github/workflows/ci.yml:16"}],"evidence":[],"data":{},"error":null},{"analyzer":"sec-posture","pillar":"security","status":"ok","tool_version":null,"metrics":{"check:security_policy":0.0,"check:dependency_updates":0.0,"check:signed_commits":0.0,"check:lockfiles":1.0,"signed_commit_share":0.1818,"unpinned_manifests":0.0},"findings":[],"evidence":[],"data":{},"error":null},{"analyzer":"sec-distribution","pillar":"security","status":"ok","tool_version":null,"metrics":{"signals":0.0,"binary_assets":0.0,"suspicious":0.0},"findings":[],"evidence":[],"data":{"signals":[],"suspicious":false,"unusual_traction":false,"metadata":true},"error":null},{"analyzer":"qual-complexity","pillar":"quality","status":"ok","tool_version":"lizard 1.25.1","metrics":{"functions":702.0,"function_nloc":14265.0,"mean_ccn":4.04,"p90_ccn":7.0,"complex_functions":20.0,"long_functions":16.0,"many_param_functions":31.0,"risky_nloc_share":0.2914},"findings":[],"evidence":[{"kind":"complex-function","description":"_dispatch_reference_category in ghidra_headless_mcp/fake_ghidra.py: complexity 69, 200 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L2389","weight":1.0},{"kind":"complex-function","description":"_dispatch_layout_category in ghidra_headless_mcp/fake_ghidra.py: complexity 68, 219 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L3169","weight":1.0},{"kind":"complex-function","description":"_mutate_arguments in ghidra_headless_mcp/fuzzer.py: complexity 68, 166 lines","ref":"ghidra_headless_mcp/fuzzer.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fuzzer.py#L139","weight":1.0},{"kind":"complex-function","description":"_dispatch_listing_category in ghidra_headless_mcp/fake_ghidra.py: complexity 54, 191 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L2011","weight":0.9},{"kind":"complex-function","description":"_dispatch_graph_category in ghidra_headless_mcp/fake_ghidra.py: complexity 53, 138 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L3526","weight":0.8833333333333333},{"kind":"complex-function","description":"_dispatch_function_category in ghidra_headless_mcp/fake_ghidra.py: complexity 53, 231 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L2879","weight":0.8833333333333333},{"kind":"complex-function","description":"_dispatch_annotation_category in ghidra_headless_mcp/fake_ghidra.py: complexity 48, 195 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L2602","weight":0.8},{"kind":"complex-function","description":"_dispatch_symbol_category in ghidra_headless_mcp/fake_ghidra.py: complexity 43, 154 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L2218","weight":0.7166666666666667},{"kind":"complex-function","description":"_dispatch_program_category in ghidra_headless_mcp/fake_ghidra.py: complexity 40, 152 lines","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L1831","weight":0.6666666666666666},{"kind":"complex-function","description":"_to_jsonable in ghidra_headless_mcp/backend.py: complexity 24, 30 lines","ref":"ghidra_headless_mcp/backend.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/backend.py#L7270","weight":0.4}],"data":{},"error":null},{"analyzer":"qual-duplication","pillar":"quality","status":"ok","tool_version":"repotrust-dup-w6","metrics":{"compared_lines":13622.0,"duplicated_lines":1295.0,"duplicated_share":0.0951,"files":12.0},"findings":[],"evidence":[{"kind":"duplicate-code","description":"6+ duplicated lines: ghidra_headless_mcp/backend.py:116 and ghidra_headless_mcp/fake_ghidra.py:156","ref":"ghidra_headless_mcp/backend.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/backend.py#L116","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: ghidra_headless_mcp/backend.py:120 and ghidra_headless_mcp/backend.py:176","ref":"ghidra_headless_mcp/backend.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/backend.py#L120","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: ghidra_headless_mcp/fake_ghidra.py:307 and ghidra_headless_mcp/fake_ghidra.py:366","ref":"ghidra_headless_mcp/fake_ghidra.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fake_ghidra.py#L307","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: ghidra_headless_mcp/fuzz_support.py:1916 and ghidra_headless_mcp/fuzz_support.py:2400","ref":"ghidra_headless_mcp/fuzz_support.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fuzz_support.py#L1916","weight":0.3},{"kind":"duplicate-code","description":"6+ duplicated lines: ghidra_headless_mcp/fuzzer.py:229 and ghidra_headless_mcp/fuzzer.py:284","ref":"ghidra_headless_mcp/fuzzer.py","url":"https://github.com/mrphrazer/ghidra-headless-mcp/blob/307b461cdbbedfbafb3771333f21875b745f940e/ghidra_headless_mcp/fuzzer.py#L229","weight":0.3}],"data":{},"error":null},{"analyzer":"qual-lint","pillar":"quality","status":"ok","tool_version":"opengrep 1.33.1; rules f1d2b562b414","metrics":{"findings":11.0,"weighted_findings":21.0,"scanned_lines":16599.0,"category:best-practice":4.0,"category:correctness":2.0,"category:maintainability":5.0},"findings":[{"rule_id":"opengrep/python.lang.correctness.tempfile.tempfile-without-flush","severity":"high","message":"Using 'tmp.name' without '.flush()' or '.close()' may cause an error because the file may not exist when 'tmp.name' is used. Use '.flush()' or close the file before using 'tmp.name'.","path":"ghidra_headless_mcp/backend.py","line":223,"fingerprint":"python.lang.correctness.tempfile.tempfile-without-flush:ghidra_headless_mcp/backend.py:223"},{"rule_id":"opengrep/python.lang.best-practice.arbitrary-sleep","severity":"high","message":"time.sleep() call; did you mean to leave this in?","path":"ghidra_headless_mcp/fuzzer.py","line":398,"fingerprint":"python.lang.best-practice.arbitrary-sleep:ghidra_headless_mcp/fuzzer.py:398"},{"rule_id":"opengrep/python.lang.best-practice.open-never-closed","severity":"high","message":"file object opened without corresponding close","path":"ghidra_headless_mcp/ghidra_cli.py","line":838,"fingerprint":"python.lang.best-practice.open-never-closed:ghidra_headless_mcp/ghidra_cli.py:838"},{"rule_id":"opengrep/python.lang.best-practice.arbitrary-sleep","severity":"high","message":"time.sleep() call; did you mean to leave this in?","path":"ghidra_headless_mcp/ghidra_cli.py","line":854,"fingerprint":"python.lang.best-practice.arbitrary-sleep:ghidra_headless_mcp/ghidra_cli.py:854"},{"rule_id":"opengrep/python.lang.best-practice.arbitrary-sleep","severity":"high","message":"time.sleep() call; did you mean to leave this in?","path":"ghidra_headless_mcp/ghidra_cli.py","line":890,"fingerprint":"python.lang.best-practice.arbitrary-sleep:ghidra_headless_mcp/ghidra_cli.py:890"},{"rule_id":"opengrep/python.lang.correctness.common-mistakes.string-concat-in-list","severity":"medium","message":"Detected strings that are implicitly concatenated inside a list. Python will implicitly concatenate strings when not explicitly delimited. Was this supposed to be individual elements of the list?","path":"ghidra_headless_mcp/backend.py","line":7003,"fingerprint":"python.lang.correctness.common-mistakes.string-concat-in-list:ghidra_headless_mcp/backend.py:7003"},{"rule_id":"opengrep/python.lang.maintainability.return-not-in-function","severity":"medium","message":"`return` only makes sense inside a function","path":"ghidra_headless_mcp/fake_ghidra.py","line":26,"fingerprint":"python.lang.maintainability.return-not-in-function:ghidra_headless_mcp/fake_ghidra.py:26"},{"rule_id":"opengrep/python.lang.maintainability.return-not-in-function","severity":"medium","message":"`return` only makes sense inside a function","path":"ghidra_headless_mcp/fake_ghidra.py","line":34,"fingerprint":"python.lang.maintainability.return-not-in-function:ghidra_headless_mcp/fake_ghidra.py:34"},{"rule_id":"opengrep/python.lang.maintainability.return-not-in-function","severity":"medium","message":"`return` only makes sense inside a function","path":"ghidra_headless_mcp/fake_ghidra.py","line":76,"fingerprint":"python.lang.maintainability.return-not-in-function:ghidra_headless_mcp/fake_ghidra.py:76"},{"rule_id":"opengrep/python.lang.maintainability.return-not-in-function","severity":"medium","message":"`return` only makes sense inside a function","path":"ghidra_headless_mcp/fake_ghidra.py","line":82,"fingerprint":"python.lang.maintainability.return-not-in-function:ghidra_headless_mcp/fake_ghidra.py:82"},{"rule_id":"opengrep/python.lang.maintainability.return-not-in-function","severity":"medium","message":"`return` only makes sense inside a function","path":"ghidra_headless_mcp/fake_ghidra.py","line":89,"fingerprint":"python.lang.maintainability.return-not-in-function:ghidra_headless_mcp/fake_ghidra.py:89"}],"evidence":[],"data":{},"error":null},{"analyzer":"qual-testing","pillar":"quality","status":"ok","tool_version":null,"metrics":{"check:has_tests":1.0,"check:test_ratio":0.5122,"check:ci_present":1.0,"check:ci_runs_tests":1.0,"check:ci_runs_lint":1.0,"check:tests_cochange":1.0,"test_lines":4251.0,"source_lines":16599.0,"test_ratio":0.2561,"ci_files":1.0,"cochange_share":0.75,"cochange_commits":8.0},"findings":[],"evidence":[{"kind":"ci","description":"CI config: .github/workflows/ci.yml","ref":".github/workflows/ci.yml","url":null,"weight":0.0},{"kind":"ci","description":"CI runs tests (pytest)","ref":null,"url":null,"weight":0.0},{"kind":"ci","description":"CI runs linters (ruff)","ref":null,"url":null,"weight":0.0}],"data":{"test_commands":["pytest"],"lint_commands":["ruff"]},"error":null},{"analyzer":"qual-docs","pillar":"quality","status":"ok","tool_version":null,"metrics":{"check:readme":1.0,"check:license":1.0,"check:contributing":0.0,"check:changelog":1.0,"readme_lines":503.0},"findings":[],"evidence":[{"kind":"docs","description":"README.md","ref":"README.md","url":null,"weight":0.0},{"kind":"docs","description":"LICENSE","ref":"LICENSE","url":null,"weight":0.0},{"kind":"docs","description":"CHANGELOG.md","ref":"CHANGELOG.md","url":null,"weight":0.0}],"data":{},"error":null},{"analyzer":"qual-type-safety","pillar":"quality","status":"ok","tool_version":null,"metrics":{"check:strict_config":0.0,"check:low_escape_density":0.0,"escapes":733.0,"escapes_per_kloc":44.159},"findings":[],"evidence":[],"data":{},"error":null}],"flags":[]}