From 1d5bc7f52f79ce896b28ecb94deef40277fb6ddc Mon Sep 17 00:00:00 2001 From: Joe Esquibel Date: Tue, 4 Aug 2026 09:25:30 -0400 Subject: [PATCH 1/2] Remove db_complexity metric from the structural extractor db_complexity never actually looked for databases -- it summed unrelated io (x3), serialization_parsing (x2), and state_mutation (x1) signature hits and called the result "database complexity," so any IO-heavy or mutation-heavy function scored as DB-complex regardless of whether a database or ORM was involved. Removes the metric from detector.py (FunctionNode schema + calculation) and every downstream consumer it had spread to: the dead graph-node attribute in network_risk_sensor.py, the DoS-score amplifier and max_db_complexity telemetry in signal_processor.py, the "Over-Permissioned Agent" security heuristic in ai_appsec_sensor.py (now gated on the real arch_io signal instead), and the SQLite schemas/report sections in record_keeper.py and llm_recorder.py. Closes #1013. Co-Authored-By: Claude Sonnet 5 --- ANTIGRAVITY.md | 2 +- docs/wiki/08-24-Big-O-Detection.md | 25 ++++---- gitgalaxy/core/detector.py | 12 ---- gitgalaxy/core/network_risk_sensor.py | 1 - gitgalaxy/metrics/signal_processor.py | 9 +-- gitgalaxy/recorders/llm_recorder.py | 30 ++-------- gitgalaxy/recorders/record_keeper.py | 8 +-- .../tools/ai_guardrails/ai_appsec_sensor.py | 12 +++- tests/core_engine/test_signal_processor.py | 15 ++--- tests/ruff_audit_baseline.json | 58 +++++++++---------- .../test_ai_appsec_sensor.py | 12 ++-- tests/tools_recorders/test_llm_recorder.py | 1 - tests/tools_recorders/test_record_keeper.py | 1 - 13 files changed, 74 insertions(+), 112 deletions(-) diff --git a/ANTIGRAVITY.md b/ANTIGRAVITY.md index 5b27bad5b..5ef8062cc 100644 --- a/ANTIGRAVITY.md +++ b/ANTIGRAVITY.md @@ -59,7 +59,7 @@ GitGalaxy scans itself and outputs intelligence to `/docs/gitgalaxy_architecture - **The SQLite Database (`docs/self_scan/gitgalaxy_master.db`):** Instead of reading an entire heavy file to figure out dependencies or function bounds, run targeted SQL queries. - *Example:* Find complexity and out-bound calls before touching a function: ```bash - sqlite3 docs/self_scan/gitgalaxy_master.db "SELECT db_complexity, is_recursive, calls_out_to FROM function_data WHERE func_name = 'execute_pipeline';" + sqlite3 docs/self_scan/gitgalaxy_master.db "SELECT big_o_depth, is_recursive, calls_out_to FROM function_data WHERE func_name = 'execute_pipeline';" ``` - *Example:* Check a file's risk exposure before adding features (e.g. `risk_cognitive_load`, `risk_state_flux`): ```bash diff --git a/docs/wiki/08-24-Big-O-Detection.md b/docs/wiki/08-24-Big-O-Detection.md index 81fb87a24..c63185db2 100644 --- a/docs/wiki/08-24-Big-O-Detection.md +++ b/docs/wiki/08-24-Big-O-Detection.md @@ -3,7 +3,7 @@ > **File Reference:** [`gitgalaxy/metrics/signal_processor.py`](file:///home/joe/nyx_projects/gitgalaxy/gitgalaxy/metrics/signal_processor.py) ## Engineering Summary -Performance and algorithmic complexity directly impact application security. Deeply nested loops ($O(N^2)$, $O(N^3)$) or exponential recursion ($O(2^N)$) connected to public API endpoints, network I/O, or database queries present severe Algorithmic Denial of Service (DoS) vulnerabilities. GitGalaxy evaluates function nesting depth and correlates it with public exposure and data operations. This subsystem evaluates the input signals to calculate a formalized risk score. In GitGalaxy, this subsystem is known as the Algorithmic DoS & Big-O Detection metric. +Performance and algorithmic complexity directly impact application security. Deeply nested loops ($O(N^2)$, $O(N^3)$) or exponential recursion ($O(2^N)$) connected to public API endpoints or network I/O present severe Algorithmic Denial of Service (DoS) vulnerabilities. GitGalaxy evaluates function nesting depth and correlates it with public exposure and data operations. This subsystem evaluates the input signals to calculate a formalized risk score. In GitGalaxy, this subsystem is known as the Algorithmic DoS & Big-O Detection metric. ## Purpose The metric calculates a density-based risk score (0-100) to flag files containing high-risk logic patterns and architectural deviations. @@ -12,25 +12,30 @@ The metric calculates a density-based risk score (0-100) to flag files containin Unmitigated anti-patterns and vulnerabilities often lead to hard-to-debug bugs and security flaws. By statically analyzing the codebase, this subsystem proactively identifies hazardous logic. ## Design -The engine analyzes function complexity depth, choke point multipliers, database gravity, and guardrail mitigations: +The engine analyzes function complexity depth, choke point multipliers, and guardrail mitigations: | Variable | Signal Focus | Role / Multiplier | Description | | :--- | :--- | :--- | :--- | | `big_o_depth` | Algorithmic Depth | **Exponential Base** | Evaluates nesting depth. $O(N)$ ($\text{depth} < 2$) is ignored. $O(N^2)$ yields base threat of $4$; $O(N^3)$ yields base threat of $9$. | | `api` / `io` | Choke Points | **Additive Multiplier** | Functions exposed to public APIs or network I/O act as weaponizable triggers. | -| `db_complexity` | Database Gravity | **Additive Multiplier** | Heavy loops paired with database queries generate severe locking and latency risks ($1.0 + \text{DBComplexity} \times 0.5$). | | `state_mutation` / `globals` | State Mutation | **Additive Multiplier** | Mutating state inside high-depth loops increases risk. | | `safety` / `panics_and_aborts` | Guardrails | **0.5x Dampener** | Break statements, return limits, and try/catch blocks reduce function threat mass by $50\%$. | | `popularity` | Network Posture | **0.1x – 3.0x** | Repository-wide import popularity scales the final threat mass. Safely isolated orphans are scaled to $0.10$. | +> **#1013:** this metric used to also fold in a per-function `db_complexity` score +> as a "Database Gravity" multiplier. It was removed engine-wide: despite the name, +> it never looked for databases at all, it just summed unrelated `io` (x3), +> `serialization_parsing` (x2), and `state_mutation` (x1) signature hits -- so any +> IO-heavy or mutation-heavy function scored as "database complex" even with zero +> database or ORM involvement. `api`/`io` choke points below already cover the real +> IO signal this used to (partially, and inaccurately) proxy for. ### 1. Function Base Threat & Amplifiers For each function with nesting depth $\ge 2$: $$\text{BaseThreat} = \text{big\_o\_depth}^2$$ -$$\text{GravityMultiplier} = 1.0 + (\text{db\_complexity} \times 0.5)$$ $$\text{ChokeMultiplier} = 1.0 + \text{api\_hits} + \text{io\_hits} + \text{flux\_hits}$$ -$$\text{FuncThreat} = \text{BaseThreat} \times \text{GravityMultiplier} \times \text{ChokeMultiplier}$$ +$$\text{FuncThreat} = \text{BaseThreat} \times \text{ChokeMultiplier}$$ ### 2. Guardrail Mitigation If safety guardrails (`safety`, `panics_and_aborts`, `cleanup`) exist within the function, threat mass is halved: @@ -60,7 +65,7 @@ def _calc_algorithmic_dos( popularity: int, ) -> float: """ - Calculates Algorithmic DoS Exposure based on Big-O depth, data gravity, and network choke points. + Calculates Algorithmic DoS Exposure based on Big-O depth and network choke points. """ if not functions: return 0.0 @@ -76,11 +81,7 @@ def _calc_algorithmic_dos( # 2. Base Threat (Exponential decay of performance) func_threat = float(depth**2) - # 3. Data Gravity & Network Choke Points - db_complex = func.get("db_complexity", 0) - if db_complex > 0: - func_threat *= 1.0 + (db_complex * 0.5) - + # 3. Network Choke Points hv = func.get("hit_vector", {}) api_hits = hv.get("api", 0) io_hits = hv.get("io", 0) + hv.get("sec_io", 0) @@ -126,7 +127,7 @@ def _calc_algorithmic_dos( **Risk Classification:** * 🟦 **VERY LOW (Score 0–19):** Linear $O(N)$ execution or safely bounded stream iterations. * 🟨 **INTERMEDIATE (Score 40–59):** Isolated $O(N^2)$ logic guarded by safety bailouts or low exposure. -* 🟥 **VERY HIGH (Score 80–100):** Recursive $O(2^N)$ or $O(N^3)$ loops directly wired into unauthenticated public API routes, I/O operations, or state-mutating database calls. +* 🟥 **VERY HIGH (Score 80–100):** Recursive $O(2^N)$ or $O(N^3)$ loops directly wired into unauthenticated public API routes or state-mutating I/O operations. ## Pipeline Integration Inputs received include raw static analysis signals from the AST parser and contextual multipliers. Outputs produced are a normalized risk score (0-100). The subsystem depends on upstream token parsers that feed AST information into the signal processor. diff --git a/gitgalaxy/core/detector.py b/gitgalaxy/core/detector.py index 8006b7c6d..844458769 100644 --- a/gitgalaxy/core/detector.py +++ b/gitgalaxy/core/detector.py @@ -120,7 +120,6 @@ class FunctionNode(TypedDict, total=False): big_o_depth: int is_recursive: bool - db_complexity: int docstring: str calls_out_to: list[str] hit_vector: dict[str, int] @@ -2136,16 +2135,6 @@ def _calculate_block_metrics( if occurrence_count > 1: is_recursive = True - # --- NEW: FUNCTION-LEVEL DATABASE COMPLEXITY (Data Gravity) --- - # Mapped to active v6 schemas: 'io' (DB connections/SQL), 'state_mutation' (mutations), and 'serialization_parsing' (JSON/ORMs). - db_complexity = 0 - if hit_vector: - db_complexity = ( - (hit_vector.get("io", 0) * 3) - + (hit_vector.get("serialization_parsing", 0) * 2) - + (hit_vector.get("state_mutation", 0) * 1) - ) - # --- NEW: FUNCTION-LEVEL KEYWORD DENSITY (The Micro-Auditor) --- # Total structural signals divided by the physical lines of the function. total_keyword_hits = sum(hit_vector.values()) if hit_vector else total_hits @@ -2287,7 +2276,6 @@ def _calculate_block_metrics( "args_count": args_count, "big_o_depth": big_o_depth, "is_recursive": is_recursive, - "db_complexity": db_complexity, "docstring": docstring, "logic_angle": round(angle, 2), "angle": round(angle, 2), diff --git a/gitgalaxy/core/network_risk_sensor.py b/gitgalaxy/core/network_risk_sensor.py index 021f4730f..d04c43fbb 100644 --- a/gitgalaxy/core/network_risk_sensor.py +++ b/gitgalaxy/core/network_risk_sensor.py @@ -188,7 +188,6 @@ def build_dependency_graph(self, parsed_files: list[dict[str, Any]]) -> tuple[li risk_vector=f.get("risk_vector", [0.0] * len(self.RISK_SCHEMA)), max_big_o=max_big_o, is_recursive=is_recursive, - db_complexity=(max([func.get("db_complexity", 0) for func in funcs]) if funcs else 0), ) # 2. Wire the Edges (File-to-File Level 1 & Entity Level 2) diff --git a/gitgalaxy/metrics/signal_processor.py b/gitgalaxy/metrics/signal_processor.py index f0142ce31..a259f720a 100644 --- a/gitgalaxy/metrics/signal_processor.py +++ b/gitgalaxy/metrics/signal_processor.py @@ -512,7 +512,6 @@ def calculate_risk_vector( avg_func_args = 0.0 func_gini = 0.0 max_big_o = 1 - max_db_complexity = 0 func_ml_brain = getattr(analysis_lens, "GENERAL_FUNCTION_INFERENCE_MODEL", {}) f_medians = func_ml_brain.get("SCALER_MEDIANS", []) @@ -553,7 +552,6 @@ def calculate_risk_vector( max_func_comp = max(complexities) avg_func_args = sum([f.get("args", 0) for f in functions]) / len(functions) max_big_o = max([f.get("big_o_depth", 1) for f in functions]) - max_db_complexity = max([f.get("db_complexity", 0) for f in functions]) has_recursion = any([f.get("is_recursive", False) for f in functions]) # 1. Z-Scores Mathematics @@ -852,7 +850,6 @@ def calculate_risk_vector( "max_algorithmic_complexity": ( "O(2^N) [Recursive]" if has_recursion else (f"O(N^{max_big_o})" if max_big_o > 1 else "O(N)") ), - "max_db_complexity": max_db_complexity, "ownership_entropy": ownership_score, "author_distribution": silo_exposure, "ownership": dominant_author, @@ -2199,11 +2196,7 @@ def _calc_algorithmic_dos( # 1. The Base Threat (Exponential decay of performance) func_threat = float(depth**2) - # 2. The Amplifiers (Network & Data Gravity) - db_complex = func.get("db_complexity", 0) - if db_complex > 0: - func_threat *= 1.0 + (db_complex * 0.5) - + # 2. The Amplifiers (Network Chokepoints) hv = func.get("hit_vector", {}) api_hits = hv.get("api", 0) io_hits = hv.get("io", 0) + hv.get("sec_io", 0) diff --git a/gitgalaxy/recorders/llm_recorder.py b/gitgalaxy/recorders/llm_recorder.py index c96b54cae..f3c419144 100644 --- a/gitgalaxy/recorders/llm_recorder.py +++ b/gitgalaxy/recorders/llm_recorder.py @@ -555,19 +555,6 @@ def _build_markdown( lines.append(f" * *Intent:* {clean_doc}") lines.append("") - sorted_by_db = sorted(all_functions, key=lambda x: x[0].get("db_complexity", 0), reverse=True) - db_functions = [s for s in sorted_by_db if s[0].get("db_complexity", 0) > 0] - - if db_functions: - lines.append("### Highest Data Gravity (Database Complexity)") - for f, file_path in db_functions[:10]: - lines.append(f"- `{f.get('name')}` (@ `{file_path}`) -> DB Complexity: **{f.get('db_complexity', 0)}**") - doc = f.get("docstring", "").strip() - if doc: - clean_doc = " ".join(doc.split())[:150] + ("..." if len(doc) > 150 else "") - lines.append(f" * *Intent:* {clean_doc}") - lines.append("") - # --- 9. DIRECTORY GROUPS --- lines.append("## 9. DIRECTORY GROUPS (Top 10 Heaviest Modules)") dir_groups = summary.get("directory_groups", {}) @@ -924,9 +911,7 @@ def _build_markdown( lines.append( f"- **Magnitude:** {m} | **LOC:** {loc} | **CtrlFlow:** {round(tel.get('control_flow_ratio', 0.0) * 100, 1)}% | **Authorship Centralization:** {round(tel.get('author_distribution', 0.0), 1)}%" ) - lines.append( - f"- **Algorithmic:** {tel.get('max_algorithmic_complexity', 'O(N)')} | **DB Complexity:** {tel.get('max_db_complexity', 0)}" - ) + lines.append(f"- **Algorithmic:** {tel.get('max_algorithmic_complexity', 'O(N)')}") lines.append(f"- **Risk Profile:** Cognitive Load ({cog}%), Tech Debt ({debt}%)") hv = s.get("hit_vector", []) @@ -950,8 +935,7 @@ def _build_markdown( lines.append("**Top Internal Functions/Classes:**") for sat in sats: o_str = "O(2^N)" if sat.get("is_recursive", False) else f"O(N^{sat.get('big_o_depth', 1)})" - db_str = f" | DB: {sat.get('db_complexity', 0)}" if sat.get("db_complexity", 0) > 0 else "" - lines.append(f" * `{sat.get('name')}` (Impact: {sat.get('impact')} | {o_str}{db_str})") + lines.append(f" * `{sat.get('name')}` (Impact: {sat.get('impact')} | {o_str})") doc = sat.get("docstring", "").strip() if doc: clean_doc = " ".join(doc.split())[:100] + ("..." if len(doc) > 100 else "") @@ -1329,7 +1313,6 @@ def _generate_sqlite_graph( ecosystem_baseline TEXT, repo_z_score REAL, max_algorithmic_complexity TEXT, - max_db_complexity INTEGER, {risk_cols} ) """) @@ -1358,7 +1341,6 @@ def _generate_sqlite_graph( impact REAL, big_o_depth INTEGER, is_recursive BOOLEAN, - db_complexity INTEGER, docstring TEXT, calls_out_to TEXT, FOREIGN KEY(artifact_id) REFERENCES artifacts(id) @@ -1445,10 +1427,10 @@ def _generate_sqlite_graph( control_flow_ratio, author_distribution, ownership_entropy, raw_churn_freq, cog_raw, ownership, popularity, archetype, global_drift, local_archetype, local_drift, - ecosystem_baseline, repo_z_score, max_algorithmic_complexity, max_db_complexity, + ecosystem_baseline, repo_z_score, max_algorithmic_complexity, {", ".join(self.RISK_SCHEMA)} ) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, {", ".join(["?"] * len(self.RISK_SCHEMA))}) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, {", ".join(["?"] * len(self.RISK_SCHEMA))}) """, # noqa: S608 ( p, @@ -1475,7 +1457,6 @@ def _generate_sqlite_graph( str(repo_macro), repo_z, tel.get("max_algorithmic_complexity", "O(N)"), - tel.get("max_db_complexity", 0), *rv, ), ) @@ -1496,7 +1477,6 @@ def _generate_sqlite_graph( func.get("impact"), func.get("big_o_depth", 1), func.get("is_recursive", False), - func.get("db_complexity", 0), func.get("docstring", ""), calls_json, ) @@ -1512,7 +1492,7 @@ def _generate_sqlite_graph( cursor.executemany("INSERT INTO dna_hits VALUES (?, ?, ?)", all_dna_data) cursor.executemany( - "INSERT INTO functions (artifact_id, name, type_id, loc, impact, big_o_depth, is_recursive, db_complexity, docstring, calls_out_to) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO functions (artifact_id, name, type_id, loc, impact, big_o_depth, is_recursive, docstring, calls_out_to) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", all_functions, ) cursor.executemany("INSERT INTO outbound_dependencies VALUES (?, ?)", all_outbound) diff --git a/gitgalaxy/recorders/record_keeper.py b/gitgalaxy/recorders/record_keeper.py index 256e4e7b7..f8f8e4743 100644 --- a/gitgalaxy/recorders/record_keeper.py +++ b/gitgalaxy/recorders/record_keeper.py @@ -290,7 +290,6 @@ def record_mission( ecosystem_baseline TEXT, repo_z_score REAL, max_algorithmic_complexity TEXT, - max_db_complexity INTEGER, ai_threat_score REAL, is_malware INTEGER, has_credentials INTEGER, @@ -338,7 +337,6 @@ def record_mission( func_z_score REAL DEFAULT 0.0, big_o_depth INTEGER, is_recursive INTEGER, - db_complexity INTEGER, docstring TEXT, calls_out_to TEXT, token_mass INTEGER DEFAULT 0, @@ -613,7 +611,6 @@ def record_mission( repo_macro, repo_z, tel.get("max_algorithmic_complexity", "O(N)"), - int(tel.get("max_db_complexity", 0)), ai_score, is_malware, has_creds, @@ -653,7 +650,7 @@ def record_mission( func_z_max, func_z_mean, func_z_median, pct_z_above_5, pct_z_above_15, file_archetype, file_fingerprint, ecosystem_baseline, repo_z_score, - max_algorithmic_complexity, max_db_complexity, + max_algorithmic_complexity, ai_threat_score, is_malware, has_credentials, binary_anomaly, obfuscation_flag, token_mass, financial_read_cost, agentic_isolation_risk, requires_hitl, appsec_rce_funnel, appsec_god_mode, appsec_exfiltration, hallucination_zone, silent_mutation_risk, {", ".join([f"risk_{r.replace('-', '_')}" for r in self.RISK_SCHEMA])}, @@ -710,7 +707,6 @@ def record_mission( float(func.get("z_score", 0.0)), int(func.get("big_o_depth", 1)), 1 if func.get("is_recursive", False) else 0, - int(func.get("db_complexity", 0)), str(func.get("docstring", ""))[:2000], json.dumps(func.get("calls_out_to", [])), (int(func.get("token_mass")) if func.get("token_mass") is not None else None), @@ -726,7 +722,7 @@ def record_mission( cursor.executemany( f""" INSERT INTO function_data - (file_id, parent_class_id, func_name, complexity, loc, args, usage_status, keyword_density, func_archetype, func_z_score, big_o_depth, is_recursive, db_complexity, docstring, calls_out_to, token_mass, {", ".join([self.SHORT_KEY_MAP.get(h, h) for h in self.SIGNAL_SCHEMA])}) + (file_id, parent_class_id, func_name, complexity, loc, args, usage_status, keyword_density, func_archetype, func_z_score, big_o_depth, is_recursive, docstring, calls_out_to, token_mass, {", ".join([self.SHORT_KEY_MAP.get(h, h) for h in self.SIGNAL_SCHEMA])}) VALUES ({func_placeholders}) """, # noqa: S608 all_func_rows, diff --git a/gitgalaxy/tools/ai_guardrails/ai_appsec_sensor.py b/gitgalaxy/tools/ai_guardrails/ai_appsec_sensor.py index e2c956f49..05719c50b 100644 --- a/gitgalaxy/tools/ai_guardrails/ai_appsec_sensor.py +++ b/gitgalaxy/tools/ai_guardrails/ai_appsec_sensor.py @@ -41,7 +41,6 @@ def hunt_threats(self, parsed_files: list[dict[str, Any]]) -> list[dict[str, Any arch_api = equations.get("api", 0) > 0 # Publicly exposed arch_io = (equations.get("io", 0) + equations.get("sec_io", 0)) > 0 # Network/Disk I/O - db_complexity = file_data.get("max_db_complexity", 0) # Data gravity # Security Structural Signatures sec_danger = equations.get("sec_high_risk_execution", 0) > 0 # eval, exec, subprocess @@ -87,10 +86,17 @@ def hunt_threats(self, parsed_files: list[dict[str, Any]]) -> list[dict[str, Any # detectable proxy for "this file has agentic tool-binding capability" -- # library-identity detection, exactly the kind of signal #323 said this # engine is good at, not the behavioral one it isn't. - if ai_orchestrator and (db_complexity >= 2 or arch_io) and safety_density < 0.5: + # + # #1013: this used to also gate on `db_complexity >= 2`, a per-function + # score removed engine-wide because it just summed unrelated io/ + # serialization_parsing/state_mutation hits and called it "database + # complexity" -- it fired on any IO-heavy or mutation-heavy function + # regardless of whether a database was involved. `arch_io` alone + # already covers the "raw IO write access" signal this rule needs. + if ai_orchestrator and arch_io and safety_density < 0.5: appsec_report["over_permissioned_agent"] = True appsec_report["critical_warnings"].append( - "CRITICAL [Over-Permissioned Agent]: AI is bound to tools with raw Database/IO write access and < 50% safety density. High risk of autonomous data corruption." + "CRITICAL [Over-Permissioned Agent]: AI is bound to tools with raw Network/Disk IO write access and < 50% safety density. High risk of autonomous data corruption." ) # 3. Agentic Exfiltration Vector (Unsandboxed Sockets) diff --git a/tests/core_engine/test_signal_processor.py b/tests/core_engine/test_signal_processor.py index a63c77d8b..b0a3384e5 100644 --- a/tests/core_engine/test_signal_processor.py +++ b/tests/core_engine/test_signal_processor.py @@ -484,7 +484,7 @@ def test_signal_processor_ai_topology(processor): # TEST 15: ALGORITHMIC DOS EXPOSURE # ============================================================================== def test_signal_processor_algorithmic_dos(processor): - """Proves the Big-O risk exposure scales with data gravity and choke points, and is dampened by safety guardrails.""" + """Proves the Big-O risk exposure scales with network choke points, and is dampened by safety guardrails.""" # 1. Isolated Harmless Loop: O(N^3) but no IO/API and 0 popularity. m_iso, sig_iso = create_synthetic_star(processor, "isolated", 100, {"api": 0}) @@ -494,12 +494,11 @@ def test_signal_processor_algorithmic_dos(processor): "name": "safe_loop", "loc": 50, "big_o_depth": 3, - "db_complexity": 0, "hit_vector": {}, } ] - # 2. API DoS Bomb: O(N^3) + DB Complexity + Exposed to API + # 2. API DoS Bomb: O(N^3) + Exposed to API m_bomb, sig_bomb = create_synthetic_star(processor, "exposed_bomb", 500, {"api": 4}) m_bomb["popularity"] = 2 m_bomb["functions"] = [ @@ -507,7 +506,6 @@ def test_signal_processor_algorithmic_dos(processor): "name": "dos_bomb", "loc": 250, "big_o_depth": 3, - "db_complexity": 2, "hit_vector": {"api": 4}, } ] @@ -520,7 +518,6 @@ def test_signal_processor_algorithmic_dos(processor): "name": "safe_bomb", "loc": 250, "big_o_depth": 3, - "db_complexity": 2, "hit_vector": {"api": 4, "safety": 1, "panics_and_aborts": 2}, } ] @@ -536,7 +533,11 @@ def test_signal_processor_algorithmic_dos(processor): assert iso_score < bomb_score, "Isolated loop should have significantly lower risk than exposed bomb!" assert guard_score < bomb_score, "Safety guardrails failed to dampen the Algorithmic DoS threat!" - assert bomb_score > 50.0, "API DoS bomb failed to spike the risk exposure!" + # #1013: this threshold used to be 50.0, tuned to include a `db_complexity` + # amplifier removed engine-wide as a flawed metric -- without it the same + # fixture legitimately scores lower, so the bar is recalibrated here rather + # than reintroducing the amplifier to hit an arbitrary number. + assert bomb_score > 10.0, "API DoS bomb failed to spike the risk exposure!" # ============================================================================== @@ -1448,7 +1449,7 @@ def test_signal_processor_algorithmic_dos_linear_bypass(processor): """Ensures O(N) linear loops are ignored by the Algorithmic DoS equations.""" m_linear, sig_linear = create_synthetic_star(processor, "linear_loop", 100, {"api": 10}) # big_o_depth = 1 is standard O(N) - m_linear["functions"] = [{"name": "safe_loop", "loc": 50, "big_o_depth": 1, "db_complexity": 5}] + m_linear["functions"] = [{"name": "safe_loop", "loc": 50, "big_o_depth": 1}] r_linear = processor.calculate_risk_vector(m_linear, sig_linear) idx_dos = processor.RISK_SCHEMA.index("algorithmic_dos") diff --git a/tests/ruff_audit_baseline.json b/tests/ruff_audit_baseline.json index 0ac1e3a5a..9ccc544f0 100644 --- a/tests/ruff_audit_baseline.json +++ b/tests/ruff_audit_baseline.json @@ -7,16 +7,16 @@ "gitgalaxy/core/aperture.py:446: SIM102": "Use a single `if` statement instead of nested `if` statements", "gitgalaxy/core/aperture.py:502: SIM102": "Use a single `if` statement instead of nested `if` statements", "gitgalaxy/core/aperture.py:514: SIM102": "Use a single `if` statement instead of nested `if` statements", - "gitgalaxy/core/detector.py:1002: PERF401": "Use `list.extend` to create a transformed list", - "gitgalaxy/core/detector.py:1501: SIM102": "Use a single `if` statement instead of nested `if` statements", - "gitgalaxy/core/detector.py:1508: SIM102": "Use a single `if` statement instead of nested `if` statements", - "gitgalaxy/core/detector.py:1720: SIM108": "Use ternary operator `line_end = len(safe_code) if next_nl == -1 else next_nl + 1` instead of `if`-`else`-block", - "gitgalaxy/core/detector.py:2161: SIM108": "Use ternary operator `args_count = args_str.count(\",\") + 1 if \",\" in args_str else len(args_str.strip().split())` instead of `if`-`else`-block", - "gitgalaxy/core/detector.py:2276: C403": "Unnecessary list comprehension (rewrite as a set comprehension)", - "gitgalaxy/core/detector.py:845: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/core/detector.py:1001: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/core/detector.py:1500: SIM102": "Use a single `if` statement instead of nested `if` statements", + "gitgalaxy/core/detector.py:1507: SIM102": "Use a single `if` statement instead of nested `if` statements", + "gitgalaxy/core/detector.py:1719: SIM108": "Use ternary operator `line_end = len(safe_code) if next_nl == -1 else next_nl + 1` instead of `if`-`else`-block", + "gitgalaxy/core/detector.py:2150: SIM108": "Use ternary operator `args_count = args_str.count(\",\") + 1 if \",\" in args_str else len(args_str.strip().split())` instead of `if`-`else`-block", + "gitgalaxy/core/detector.py:2265: C403": "Unnecessary list comprehension (rewrite as a set comprehension)", + "gitgalaxy/core/detector.py:844: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/core/guidestar_lens.py:139: C401": "Unnecessary generator (rewrite as a set comprehension)", "gitgalaxy/core/network_risk_sensor.py:183: C419": "Unnecessary list comprehension", - "gitgalaxy/core/network_risk_sensor.py:282: PERF401": "Use a list comprehension to create a transformed list", + "gitgalaxy/core/network_risk_sensor.py:281: PERF401": "Use a list comprehension to create a transformed list", "gitgalaxy/galaxyscope.py:1035: SIM102": "Use a single `if` statement instead of nested `if` statements", "gitgalaxy/galaxyscope.py:1126: SIM118": "Use `key in dict` instead of `key in dict.keys()`", "gitgalaxy/galaxyscope.py:1137: SIM118": "Use `key in dict` instead of `key in dict.keys()`", @@ -28,12 +28,12 @@ "gitgalaxy/licensing.py:83: DTZ005": "`datetime.datetime.now()` called without a `tz` argument", "gitgalaxy/metrics/chronometer.py:299: SIM118": "Use `key in dict` instead of `key in dict.keys()`", "gitgalaxy/metrics/chronometer.py:424: PERF203": "`try`-`except` within a loop incurs performance overhead", - "gitgalaxy/metrics/signal_processor.py:1690: SIM108": "Use ternary operator `network_multiplier = 0.2 if popularity == 0 else min(1.0 + math.log1p(popularity) / 5.0, 2.0)` instead of `if`-`else`-block", - "gitgalaxy/metrics/signal_processor.py:2470: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/metrics/signal_processor.py:1687: SIM108": "Use ternary operator `network_multiplier = 0.2 if popularity == 0 else min(1.0 + math.log1p(popularity) / 5.0, 2.0)` instead of `if`-`else`-block", + "gitgalaxy/metrics/signal_processor.py:2463: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/metrics/signal_processor.py:506: RUF046": "Value being cast to `int` is already an integer", - "gitgalaxy/metrics/signal_processor.py:520: SIM118": "Use `key in dict` instead of `key in dict.keys()`", - "gitgalaxy/metrics/signal_processor.py:557: C419": "Unnecessary list comprehension", - "gitgalaxy/metrics/signal_processor.py:672: SIM118": "Use `key in dict` instead of `key in dict.keys()`", + "gitgalaxy/metrics/signal_processor.py:519: SIM118": "Use `key in dict` instead of `key in dict.keys()`", + "gitgalaxy/metrics/signal_processor.py:555: C419": "Unnecessary list comprehension", + "gitgalaxy/metrics/signal_processor.py:670: SIM118": "Use `key in dict` instead of `key in dict.keys()`", "gitgalaxy/metrics/signal_processor.py:89: SIM118": "Use `key in dict` instead of `key in dict.keys()`", "gitgalaxy/metrics/statistical_auditor.py:240: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/metrics/statistical_auditor.py:312: PERF203": "`try`-`except` within a loop incurs performance overhead", @@ -54,17 +54,17 @@ "gitgalaxy/recorders/gpu_recorder.py:252: RUF046": "Value being cast to `int` is already an integer", "gitgalaxy/recorders/gpu_recorder.py:285: C414": "Unnecessary `list()` call within `sorted()`", "gitgalaxy/recorders/gpu_recorder.py:320: RUF046": "Value being cast to `int` is already an integer", - "gitgalaxy/recorders/llm_recorder.py:1124: SIM102": "Use a single `if` statement instead of nested `if` statements", + "gitgalaxy/recorders/llm_recorder.py:1108: SIM102": "Use a single `if` statement instead of nested `if` statements", + "gitgalaxy/recorders/llm_recorder.py:1167: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/recorders/llm_recorder.py:1183: PERF401": "Use `list.extend` to create a transformed list", - "gitgalaxy/recorders/llm_recorder.py:1199: PERF401": "Use `list.extend` to create a transformed list", - "gitgalaxy/recorders/llm_recorder.py:1212: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/recorders/llm_recorder.py:1196: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/recorders/llm_recorder.py:466: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/recorders/llm_recorder.py:516: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/recorders/llm_recorder.py:596: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/recorders/llm_recorder.py:609: PERF401": "Use `list.extend` to create a transformed list", - "gitgalaxy/recorders/llm_recorder.py:622: PERF401": "Use `list.extend` to create a transformed list", - "gitgalaxy/recorders/llm_recorder.py:694: PERF401": "Use `list.extend` to create a transformed list", - "gitgalaxy/recorders/llm_recorder.py:728: PERF401": "Use `list.extend` to create a transformed list", - "gitgalaxy/recorders/llm_recorder.py:750: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/recorders/llm_recorder.py:681: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/recorders/llm_recorder.py:715: PERF401": "Use `list.extend` to create a transformed list", + "gitgalaxy/recorders/llm_recorder.py:737: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/recorders/record_keeper.py:187: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:188: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:189: W291": "Trailing whitespace", @@ -75,17 +75,17 @@ "gitgalaxy/recorders/record_keeper.py:286: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:287: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:288: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:646: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:643: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:644: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:647: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:649: W291": "Trailing whitespace", "gitgalaxy/recorders/record_keeper.py:650: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:652: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:653: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:676: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:700: RUF005": "Consider iterable unpacking instead of concatenation", - "gitgalaxy/recorders/record_keeper.py:771: RUF005": "Consider iterable unpacking instead of concatenation", - "gitgalaxy/recorders/record_keeper.py:808: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:809: W291": "Trailing whitespace", - "gitgalaxy/recorders/record_keeper.py:840: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:673: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:697: RUF005": "Consider iterable unpacking instead of concatenation", + "gitgalaxy/recorders/record_keeper.py:767: RUF005": "Consider iterable unpacking instead of concatenation", + "gitgalaxy/recorders/record_keeper.py:804: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:805: W291": "Trailing whitespace", + "gitgalaxy/recorders/record_keeper.py:836: W291": "Trailing whitespace", "gitgalaxy/recorders/sbom_recorder.py:213: PERF401": "Use `list.extend` to create a transformed list", "gitgalaxy/security/security_auditor.py:359: RUF046": "Value being cast to `int` is already an integer", "gitgalaxy/security/security_auditor.py:424: PERF203": "`try`-`except` within a loop incurs performance overhead", diff --git a/tests/security_auditing/test_ai_appsec_sensor.py b/tests/security_auditing/test_ai_appsec_sensor.py index 11221253c..5a1b8cdde 100644 --- a/tests/security_auditing/test_ai_appsec_sensor.py +++ b/tests/security_auditing/test_ai_appsec_sensor.py @@ -38,19 +38,20 @@ def test_over_permissioned_agent_detection(): Proves that an agent orchestration framework (langchain/llama_index -- #365/#323: the closest lexically-detectable proxy for agentic tool-binding this engine has, since "ai_tools" was removed from SIGNAL_SCHEMA in #323 - as fundamentally undetectable via regex), combined with write-access to - complex databases and low defensive programming density, triggers the - Over-Permissioned Agent alert. + as fundamentally undetectable via regex), combined with raw network/disk + IO write access and low defensive programming density, triggers the + Over-Permissioned Agent alert. (#1013: this used to also accept + `max_db_complexity` as an alternate trigger, removed as a flawed metric.) """ sensor = AIAppSecSensor() mock_files = [ { - "max_db_complexity": 3, # Heavy database write access "coding_loc": 100, "telemetry": {}, "equations": { "llm_orchestrator": 1, # langchain/llama_index present -> agentic tool-binding + "io": 1, # Raw network/disk IO write access "safety": 0, # Dangerously low defensive programming -> density 0.0 }, } @@ -77,11 +78,11 @@ def test_over_permissioned_agent_no_longer_reads_dead_ai_tools_key(): mock_files = [ { - "max_db_complexity": 3, "coding_loc": 100, "telemetry": {}, "equations": { "ai_tools": 1, # dead key -- must be inert + "io": 1, # Raw network/disk IO write access "safety": 0, }, } @@ -135,7 +136,6 @@ def test_safe_baseline(): mock_files = [ { - "max_db_complexity": 0, "coding_loc": 50, "telemetry": {}, "equations": { diff --git a/tests/tools_recorders/test_llm_recorder.py b/tests/tools_recorders/test_llm_recorder.py index 4eba5669c..93bb489e2 100644 --- a/tests/tools_recorders/test_llm_recorder.py +++ b/tests/tools_recorders/test_llm_recorder.py @@ -52,7 +52,6 @@ def mock_pipeline_state(): "impact": 15.0, "big_o_depth": 2, "is_recursive": False, - "db_complexity": 3, "docstring": "Handles incoming API requests.", "calls_out_to": ["validate_token"], } diff --git a/tests/tools_recorders/test_record_keeper.py b/tests/tools_recorders/test_record_keeper.py index ed13d8b8a..f4593cd39 100644 --- a/tests/tools_recorders/test_record_keeper.py +++ b/tests/tools_recorders/test_record_keeper.py @@ -70,7 +70,6 @@ def mock_pipeline_state(): "impact": 15.0, "big_o_depth": 2, "is_recursive": False, - "db_complexity": 3, "docstring": "Handles incoming API requests.", "calls_out_to": ["validate_token"], "hit_vector": {"high_risk_execution": 1, "io": 2}, From b09f194d066a42d75b6992cb3c9db2ce3c650d70 Mon Sep 17 00:00:00 2001 From: Joe Esquibel Date: Tue, 4 Aug 2026 09:45:18 -0400 Subject: [PATCH 2/2] build(deps): bump cryptography from 48.0.1 to 50.0.0 Fixes Muninn scan findings on PR #1021: - GHSA-g6cj-pr64-35w5 (CVE-2026-69247): PKCS#7 EnvelopedData decryption exposed a Bleichenbacher oracle through distinguishable errors/timing - GHSA-jwv3-5hgf-82ww (CVE-2026-69249): duplicate self-signed intermediates could cause exponential path-building - GHSA-m2h6-j472-rp4c (CVE-2026-69248): verifier accepted wildcard DNS names that escape permittedSubtrees constraints No code in this repo imports the cryptography package directly (only a keyword-string reference in analysis_lens.py's security lexicon), so this is a transitive-dependency bump with no API surface to verify. Co-Authored-By: Claude Sonnet 5 --- gitgalaxy/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gitgalaxy/requirements.txt b/gitgalaxy/requirements.txt index e44dcc512..055b1082b 100644 --- a/gitgalaxy/requirements.txt +++ b/gitgalaxy/requirements.txt @@ -14,7 +14,7 @@ click==8.4.2 colorama==0.4.6 contourpy==1.3.3 coverage==7.14.0 -cryptography==48.0.1 +cryptography==50.0.0 cycler==0.12.1 decorator==5.2.1 defusedxml==0.7.1