torusguard 2.1.0 → 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (152) hide show
  1. package/.torusguard/.manifest.json +55 -5
  2. package/.torusguard/core/__init__.py +146 -0
  3. package/.torusguard/core/agent_roles.py +104 -0
  4. package/.torusguard/core/ast_walker.py +283 -0
  5. package/.torusguard/core/authorization.py +218 -0
  6. package/.torusguard/core/browser_verifier.py +128 -0
  7. package/.torusguard/core/bundle.py +141 -0
  8. package/.torusguard/core/call_graph.py +184 -0
  9. package/.torusguard/core/clustering.py +275 -0
  10. package/.torusguard/core/confidence.py +120 -0
  11. package/.torusguard/core/cross_file_taint.py +101 -0
  12. package/.torusguard/core/exploit_checker.py +317 -0
  13. package/.torusguard/core/formatter.py +351 -0
  14. package/.torusguard/core/governance.py +210 -0
  15. package/.torusguard/core/identity.py +104 -0
  16. package/.torusguard/core/import_resolver.py +91 -0
  17. package/.torusguard/core/incremental.py +102 -0
  18. package/.torusguard/core/lifecycle.py +137 -0
  19. package/.torusguard/core/models.py +425 -0
  20. package/.torusguard/core/parallel.py +56 -0
  21. package/.torusguard/core/parser.py +202 -0
  22. package/.torusguard/core/rechecker.py +107 -0
  23. package/.torusguard/core/replay_trace.py +178 -0
  24. package/.torusguard/core/rules_registry.py +131 -0
  25. package/.torusguard/core/run_folder.py +60 -0
  26. package/.torusguard/core/run_manager.py +163 -0
  27. package/.torusguard/core/runtime_evidence.py +175 -0
  28. package/.torusguard/core/runtime_validator.py +246 -0
  29. package/.torusguard/core/safety_gate.py +139 -0
  30. package/.torusguard/core/sarif.py +189 -0
  31. package/.torusguard/core/stack_profiler.py +184 -0
  32. package/.torusguard/core/symbol_table.py +91 -0
  33. package/.torusguard/core/taint.py +133 -0
  34. package/.torusguard/core/taint_graph.py +235 -0
  35. package/.torusguard/core/taint_rules.py +268 -0
  36. package/.torusguard/core/v070_reporter.py +102 -0
  37. package/.torusguard/core/v070_workflow.py +339 -0
  38. package/.torusguard/core/v6_reporter.py +180 -0
  39. package/.torusguard/core/v6_workflow.py +221 -0
  40. package/.torusguard/core/watcher.py +58 -0
  41. package/.torusguard/custom_rules/README.md +24 -0
  42. package/.torusguard/rules/TG-INPUT-007-unvalidated-redirect.md +53 -0
  43. package/.torusguard/rules/TG-INPUT-008-insecure-deserialization.md +52 -0
  44. package/.torusguard/scripts/__pycache__/audit_runner.cpython-314.pyc +0 -0
  45. package/.torusguard/scripts/__pycache__/finding_scorer.cpython-314.pyc +0 -0
  46. package/.torusguard/scripts/__pycache__/manifest_builder.cpython-314.pyc +0 -0
  47. package/.torusguard/scripts/__pycache__/rules_sync.cpython-314.pyc +0 -0
  48. package/.torusguard/scripts/audit_runner.py +108 -10
  49. package/.torusguard/scripts/deliberation_tournament.py +136 -0
  50. package/.torusguard/scripts/finding_scorer.py +43 -13
  51. package/.torusguard/scripts/reachability_analyzer.py +165 -0
  52. package/.torusguard/scripts/skill_profiler.py +26 -0
  53. package/.torusguard/scripts/stride_generator.py +198 -0
  54. package/.torusguard/skills/torusguard/SKILL.md +6 -2
  55. package/.torusguard/skills/torusguard-audit/SKILL.md +109 -84
  56. package/.torusguard/skills/torusguard-review/SKILL.md +27 -0
  57. package/.torusguard/skills/torusguard-threatmodel/SKILL.md +27 -0
  58. package/.torusguard/workflows/audit.md +21 -17
  59. package/.torusguard/workflows/review.md +11 -0
  60. package/.torusguard/workflows/threatmodel.md +9 -0
  61. package/README.md +149 -48
  62. package/package.json +10 -2
  63. package/skills/torusguard/SKILL.md +6 -2
  64. package/skills/torusguard/__pycache__/bootstrap.cpython-314.pyc +0 -0
  65. package/skills/torusguard/bootstrap.py +3 -3
  66. package/skills/torusguard/payload/.manifest.json +56 -7
  67. package/skills/torusguard/payload/core/__init__.py +146 -0
  68. package/skills/torusguard/payload/core/agent_roles.py +104 -0
  69. package/skills/torusguard/payload/core/ast_walker.py +283 -0
  70. package/skills/torusguard/payload/core/authorization.py +218 -0
  71. package/skills/torusguard/payload/core/browser_verifier.py +128 -0
  72. package/skills/torusguard/payload/core/bundle.py +141 -0
  73. package/skills/torusguard/payload/core/call_graph.py +184 -0
  74. package/skills/torusguard/payload/core/clustering.py +275 -0
  75. package/skills/torusguard/payload/core/confidence.py +120 -0
  76. package/skills/torusguard/payload/core/cross_file_taint.py +101 -0
  77. package/skills/torusguard/payload/core/exploit_checker.py +317 -0
  78. package/skills/torusguard/payload/core/formatter.py +351 -0
  79. package/skills/torusguard/payload/core/governance.py +210 -0
  80. package/skills/torusguard/payload/core/identity.py +104 -0
  81. package/skills/torusguard/payload/core/import_resolver.py +91 -0
  82. package/skills/torusguard/payload/core/incremental.py +102 -0
  83. package/skills/torusguard/payload/core/lifecycle.py +137 -0
  84. package/skills/torusguard/payload/core/models.py +425 -0
  85. package/skills/torusguard/payload/core/parallel.py +56 -0
  86. package/skills/torusguard/payload/core/parser.py +202 -0
  87. package/skills/torusguard/payload/core/rechecker.py +107 -0
  88. package/skills/torusguard/payload/core/replay_trace.py +178 -0
  89. package/skills/torusguard/payload/core/rules_registry.py +131 -0
  90. package/skills/torusguard/payload/core/run_folder.py +60 -0
  91. package/skills/torusguard/payload/core/run_manager.py +163 -0
  92. package/skills/torusguard/payload/core/runtime_evidence.py +175 -0
  93. package/skills/torusguard/payload/core/runtime_validator.py +246 -0
  94. package/skills/torusguard/payload/core/safety_gate.py +139 -0
  95. package/skills/torusguard/payload/core/sarif.py +189 -0
  96. package/skills/torusguard/payload/core/stack_profiler.py +184 -0
  97. package/skills/torusguard/payload/core/symbol_table.py +91 -0
  98. package/skills/torusguard/payload/core/taint.py +133 -0
  99. package/skills/torusguard/payload/core/taint_graph.py +235 -0
  100. package/skills/torusguard/payload/core/taint_rules.py +268 -0
  101. package/skills/torusguard/payload/core/v070_reporter.py +102 -0
  102. package/skills/torusguard/payload/core/v070_workflow.py +339 -0
  103. package/skills/torusguard/payload/core/v6_reporter.py +180 -0
  104. package/skills/torusguard/payload/core/v6_workflow.py +221 -0
  105. package/skills/torusguard/payload/core/watcher.py +58 -0
  106. package/skills/torusguard/payload/custom_rules/README.md +15 -0
  107. package/skills/torusguard/payload/rules/TG-INPUT-007-unvalidated-redirect.md +53 -0
  108. package/skills/torusguard/payload/rules/TG-INPUT-008-insecure-deserialization.md +52 -0
  109. package/skills/torusguard/payload/rules/container/TG-CONT-001-root-user-execution.md +50 -50
  110. package/skills/torusguard/payload/rules/container/TG-CONT-002-docker-socket-mount.md +47 -47
  111. package/skills/torusguard/payload/rules/container/TG-CONT-003-privileged-container-mode.md +53 -53
  112. package/skills/torusguard/payload/rules/container/TG-CONT-004-build-arg-secret-exposure.md +43 -43
  113. package/skills/torusguard/payload/rules/git/TG-GIT-001-historical-secret-in-git-commit.md +44 -44
  114. package/skills/torusguard/payload/rules/git/TG-GIT-002-plaintext-credentials-in-git-config.md +41 -41
  115. package/skills/torusguard/payload/rules/git/TG-GIT-003-sensitive-tracked-file-gitignore-breach.md +40 -40
  116. package/skills/torusguard/payload/rules/rag/TG-RAG-001-untrusted-rag-context-injection.md +72 -72
  117. package/skills/torusguard/payload/rules/rag/TG-RAG-002-autonomous-llm-tool-unsandboxed-call.md +51 -51
  118. package/skills/torusguard/payload/rules/rag/TG-RAG-003-unpartitioned-vector-tenant-lookup.md +51 -51
  119. package/skills/torusguard/payload/rules/redos/TG-REDOS-001-catastrophic-exponential-backtracking.md +46 -46
  120. package/skills/torusguard/payload/rules/redos/TG-REDOS-002-unbounded-nested-quantifier.md +43 -43
  121. package/skills/torusguard/payload/scripts/audit_runner.py +108 -10
  122. package/skills/torusguard/payload/scripts/deliberation_tournament.py +127 -0
  123. package/skills/torusguard/payload/scripts/finding_scorer.py +43 -13
  124. package/skills/torusguard/payload/scripts/reachability_analyzer.py +164 -0
  125. package/skills/torusguard/payload/scripts/stride_generator.py +196 -0
  126. package/skills/torusguard/payload/skills/torusguard/SKILL.md +6 -2
  127. package/skills/torusguard/payload/skills/torusguard/bootstrap.py +3 -3
  128. package/skills/torusguard/payload/skills/torusguard-ai-guard/SKILL.md +95 -95
  129. package/skills/torusguard/payload/skills/torusguard-audit/SKILL.md +109 -84
  130. package/skills/torusguard/payload/skills/torusguard-container/SKILL.md +94 -94
  131. package/skills/torusguard/payload/skills/torusguard-git-mine/SKILL.md +92 -92
  132. package/skills/torusguard/payload/skills/torusguard-ocr-scan/SKILL.md +94 -94
  133. package/skills/torusguard/payload/skills/torusguard-redos/SKILL.md +91 -91
  134. package/skills/torusguard/payload/skills/torusguard-review/SKILL.md +27 -0
  135. package/skills/torusguard/payload/skills/torusguard-threatmodel/SKILL.md +27 -0
  136. package/skills/torusguard/payload/workflows/ai-guard.md +31 -31
  137. package/skills/torusguard/payload/workflows/audit.md +21 -17
  138. package/skills/torusguard/payload/workflows/container.md +29 -29
  139. package/skills/torusguard/payload/workflows/git-mine.md +25 -25
  140. package/skills/torusguard/payload/workflows/ocr-scan.md +25 -25
  141. package/skills/torusguard/payload/workflows/redos.md +27 -27
  142. package/skills/torusguard/payload/workflows/review.md +11 -0
  143. package/skills/torusguard/payload/workflows/threatmodel.md +9 -0
  144. package/skills/torusguard/payload/workflows/torusguard-audit.md +35 -55
  145. package/skills/torusguard/references/csharp-security.md +41 -41
  146. package/skills/torusguard/references/go-security.md +41 -41
  147. package/skills/torusguard/references/java-security.md +40 -40
  148. package/skills/torusguard/references/polyglot-security-matrix.md +25 -25
  149. package/skills/torusguard/references/rust-security.md +40 -40
  150. package/skills/torusguard-audit/SKILL.md +107 -83
  151. package/skills/torusguard-review/SKILL.md +27 -0
  152. package/skills/torusguard-threatmodel/SKILL.md +27 -0
@@ -0,0 +1,146 @@
1
+ """
2
+ TorusGuard Core Architecture & Security Workflow Engine (v0.7.0)
3
+
4
+ Tiered architectural layers:
5
+ - Tier 1: Canonical Data Models, Lifecycle State Machine & Reporting (v0.5 base)
6
+ - Tier 2: Governed Remediation, Clustering, Patch Policy & SARIF Export (v0.6 engine)
7
+ - Tier 3: Authorized Runtime Validation, Exploitability Confirmation & Multi-Agent Roles (v0.7 engine)
8
+ """
9
+
10
+ # ==============================================================================
11
+ # Tier 1: Canonical Data Models, Lifecycle & Provenance (v0.5 base)
12
+ # ==============================================================================
13
+ from .models import (
14
+ Finding,
15
+ Evidence,
16
+ Remediation,
17
+ FrameworkPattern,
18
+ AffectedComponent,
19
+ ReproductionMethod,
20
+ RetestRecord,
21
+ NotesRecord,
22
+ FindingTimestamps,
23
+ ProvenanceChain,
24
+ ConfidenceScore,
25
+ ConfidenceFactors,
26
+ ConfidenceBand,
27
+ SeverityLevel,
28
+ SeverityInfo,
29
+ RemediationPriority,
30
+ FindingStatus,
31
+ LifecycleStage,
32
+ TaxonomyCategory,
33
+ EvidenceType,
34
+ AuditReport,
35
+ mask_sensitive_data,
36
+ )
37
+ from .lifecycle import FindingLifecycleManager, LifecycleTransitionError
38
+ from .formatter import ReportFormatter
39
+ from .run_folder import RunFolder
40
+
41
+ # ==============================================================================
42
+ # Tier 2: Governed Remediation, Clustering, Patch Policy & SARIF (v0.6 engine)
43
+ # ==============================================================================
44
+ from .identity import IdentityEngine, FindingFingerprint
45
+ from .clustering import ClusteringEngine, RootCauseCluster
46
+ from .bundle import BundleManager, RemediationBundle
47
+ from .governance import PatchGovernor, PatchPolicyDecision, HIGH_RISK_KEYWORDS, SENSITIVE_CATEGORIES
48
+ from .rechecker import TargetedRechecker, TargetedRecheckResult, RecheckOutcome
49
+ from .run_manager import RunManager
50
+ from .sarif import SarifExporter
51
+ from .v6_reporter import V6Reporter
52
+ from .v6_workflow import V6Workflow
53
+ from .stack_profiler import StackProfiler, StackProfile
54
+
55
+ # ==============================================================================
56
+ # Tier 3: Authorized Runtime Validation & Exploitability Confirmation (v0.7 engine)
57
+ # ==============================================================================
58
+ from .authorization import AuthorizationManager, AuthorizationRecord, TargetScope, AuthorizationError
59
+ from .safety_gate import SafetyGate, SafetyDecision, SafetyReviewLevel
60
+ from .runtime_evidence import EvidenceCollector, RuntimeEvidenceItem, RedactionEngine
61
+ from .runtime_validator import WebValidator, SessionState
62
+ from .exploit_checker import ExploitChecker, ExploitCheckResult, ExploitabilityStatus
63
+ from .browser_verifier import BrowserVerifier, BrowserAction
64
+ from .agent_roles import RoleOrchestrator, RoleHandoff, AgentRole
65
+ from .replay_trace import ReplayManager, ReplayTrace, ReplayStep
66
+ from .v070_reporter import V070Reporter
67
+ from .v070_workflow import V070Workflow
68
+
69
+ __version__ = "0.7.0"
70
+
71
+ __all__ = [
72
+ # Tier 1: Canonical Models & Lifecycle
73
+ "Finding",
74
+ "Evidence",
75
+ "Remediation",
76
+ "FrameworkPattern",
77
+ "AffectedComponent",
78
+ "ReproductionMethod",
79
+ "RetestRecord",
80
+ "NotesRecord",
81
+ "FindingTimestamps",
82
+ "ProvenanceChain",
83
+ "ConfidenceScore",
84
+ "ConfidenceFactors",
85
+ "ConfidenceBand",
86
+ "SeverityLevel",
87
+ "SeverityInfo",
88
+ "RemediationPriority",
89
+ "FindingStatus",
90
+ "LifecycleStage",
91
+ "TaxonomyCategory",
92
+ "EvidenceType",
93
+ "AuditReport",
94
+ "mask_sensitive_data",
95
+ "FindingLifecycleManager",
96
+ "LifecycleTransitionError",
97
+ "ReportFormatter",
98
+ "RunFolder",
99
+ # Tier 2: Governed Remediation & v0.6 Engine
100
+ "IdentityEngine",
101
+ "FindingFingerprint",
102
+ "ClusteringEngine",
103
+ "RootCauseCluster",
104
+ "BundleManager",
105
+ "RemediationBundle",
106
+ "PatchGovernor",
107
+ "PatchPolicyDecision",
108
+ "HIGH_RISK_KEYWORDS",
109
+ "SENSITIVE_CATEGORIES",
110
+ "TargetedRechecker",
111
+ "TargetedRecheckResult",
112
+ "RecheckOutcome",
113
+ "RunManager",
114
+ "SarifExporter",
115
+ "V6Reporter",
116
+ "V6Workflow",
117
+ "StackProfiler",
118
+ "StackProfile",
119
+ # Tier 3: Authorized Runtime Validation & v0.7 Engine
120
+ "AuthorizationManager",
121
+ "AuthorizationRecord",
122
+ "TargetScope",
123
+ "AuthorizationError",
124
+ "SafetyGate",
125
+ "SafetyDecision",
126
+ "SafetyReviewLevel",
127
+ "EvidenceCollector",
128
+ "RuntimeEvidenceItem",
129
+ "RedactionEngine",
130
+ "WebValidator",
131
+ "SessionState",
132
+ "ExploitChecker",
133
+ "ExploitCheckResult",
134
+ "ExploitabilityStatus",
135
+ "BrowserVerifier",
136
+ "BrowserAction",
137
+ "RoleOrchestrator",
138
+ "RoleHandoff",
139
+ "AgentRole",
140
+ "ReplayManager",
141
+ "ReplayTrace",
142
+ "ReplayStep",
143
+ "V070Reporter",
144
+ "V070Workflow",
145
+ ]
146
+
@@ -0,0 +1,104 @@
1
+ """
2
+ TorusGuard v0.7.0 Role-Based Multi-Agent Workflow Engine
3
+ Coordinates explicit handoff contracts between Profiler, Validator, Remediator,
4
+ and Reviewer agent roles, ensuring strict authority separation and full auditability.
5
+ """
6
+
7
+ import json
8
+ from enum import Enum
9
+ from datetime import datetime, timezone
10
+ from pathlib import Path
11
+ from typing import Dict, Any, List, Optional, Tuple
12
+ from dataclasses import dataclass, asdict
13
+
14
+
15
+ class AgentRole(str, Enum):
16
+ PROFILER = "Profiler"
17
+ VALIDATOR = "Validator"
18
+ REMEDIATOR = "Remediator"
19
+ REVIEWER = "Reviewer"
20
+
21
+
22
+ @dataclass
23
+ class RoleHandoff:
24
+ handoff_id: str
25
+ from_role: str
26
+ to_role: str
27
+ timestamp: str
28
+ contract_goal: str
29
+ inputs_passed: Dict[str, Any]
30
+ outputs_produced: Dict[str, Any]
31
+ status: str = "Completed"
32
+
33
+ def to_dict(self) -> Dict[str, Any]:
34
+ return asdict(self)
35
+
36
+
37
+ class RoleOrchestrator:
38
+ """
39
+ Coordinates the 4-role multi-agent workflow and records audit logs.
40
+ """
41
+
42
+ def __init__(self, run_id: str):
43
+ self.run_id = run_id
44
+ self.handoffs: List[RoleHandoff] = []
45
+
46
+ def record_handoff(
47
+ self,
48
+ from_role: AgentRole,
49
+ to_role: AgentRole,
50
+ contract_goal: str,
51
+ inputs: Dict[str, Any],
52
+ outputs: Dict[str, Any]
53
+ ) -> RoleHandoff:
54
+ now = datetime.now(timezone.utc).isoformat()
55
+ h_id = f"HDF-{len(self.handoffs) + 1:02d}"
56
+ handoff = RoleHandoff(
57
+ handoff_id=h_id,
58
+ from_role=from_role.value,
59
+ to_role=to_role.value,
60
+ timestamp=now,
61
+ contract_goal=contract_goal,
62
+ inputs_passed=inputs,
63
+ outputs_produced=outputs,
64
+ status="Completed"
65
+ )
66
+ self.handoffs.append(handoff)
67
+ return handoff
68
+
69
+ def write_artifacts(self, run_dir: Path) -> Tuple[Path, Path]:
70
+ run_dir.mkdir(parents=True, exist_ok=True)
71
+ audit_file = run_dir / "role-audit.json"
72
+ handoff_file = run_dir / "agent-handoffs.md"
73
+
74
+ # 1. role-audit.json
75
+ with open(audit_file, "w", encoding="utf-8") as f:
76
+ json.dump([h.to_dict() for h in self.handoffs], f, indent=2)
77
+
78
+ # 2. agent-handoffs.md
79
+ lines = [
80
+ "# TorusGuard v0.7.0 Multi-Agent Role Handoff & Governance Trail",
81
+ f"\n**Run Identifier:** `{self.run_id}`",
82
+ f"**Total Role Handoffs:** `{len(self.handoffs)}`",
83
+ "\n---",
84
+ "\n## 👥 Multi-Agent Responsibility Matrix\n",
85
+ "| Agent Role | Explicit Authority & Purpose | Forbidden Actions |",
86
+ "|---|---|---|",
87
+ "| **🔍 Profiler** | Detects tech stack, framework versions, route ASTs, and storage boundaries. | Cannot execute active probes or generate code patches. |",
88
+ "| **⚡ Validator** | Executes authorized, bounded runtime HTTP/browser probes; gathers evidence. | Cannot apply code changes or override reviewer policies. |",
89
+ "| **🛠️ Remediator** | Formulates minimal bounded remediation bundles enriched with runtime context. | Cannot dispatch runtime network probes or declare final sign-off. |",
90
+ "| **🛡️ Reviewer** | Verifies technical evidence, reviews safety decisions, and issues sign-off. | Does not execute probes or author patches. |",
91
+ "\n---",
92
+ "\n## 🔄 Chronological Handoff Log\n",
93
+ "| Handoff ID | From Role | To Role | Contract Goal | Timestamp | Status |",
94
+ "|---|---|---|---|---|:---:|",
95
+ ]
96
+ for h in self.handoffs:
97
+ lines.append(
98
+ f"| `{h.handoff_id}` | **{h.from_role}** | **{h.to_role}** | {h.contract_goal} | `{h.timestamp}` | ✅ {h.status} |"
99
+ )
100
+
101
+ with open(handoff_file, "w", encoding="utf-8") as f:
102
+ f.write("\n".join(lines) + "\n")
103
+
104
+ return audit_file, handoff_file
@@ -0,0 +1,283 @@
1
+ """
2
+ TorusGuard Polyglot AST Walker
3
+ Extracts structured primitives (FunctionCall, Assignment, ImportStatement, StringLiteral)
4
+ from Tree-sitter AST nodes and fallback parsers across polyglot languages.
5
+ """
6
+
7
+ from dataclasses import dataclass, field, asdict
8
+ from typing import List, Dict, Any, Optional
9
+
10
+
11
+ @dataclass
12
+ class FunctionCall:
13
+ name: str
14
+ full_call: str
15
+ arguments: List[str]
16
+ line_number: int
17
+ column: int
18
+ receiver: Optional[str] = None
19
+
20
+ def to_dict(self) -> Dict[str, Any]:
21
+ return asdict(self)
22
+
23
+
24
+ @dataclass
25
+ class Assignment:
26
+ target: str
27
+ value_expression: str
28
+ line_number: int
29
+ column: int
30
+
31
+ def to_dict(self) -> Dict[str, Any]:
32
+ return asdict(self)
33
+
34
+
35
+ @dataclass
36
+ class ImportStatement:
37
+ module: str
38
+ imported_names: List[str]
39
+ alias_map: Dict[str, str] = field(default_factory=dict)
40
+ line_number: int = 0
41
+ is_relative: bool = False
42
+
43
+ def to_dict(self) -> Dict[str, Any]:
44
+ return asdict(self)
45
+
46
+
47
+ @dataclass
48
+ class StringLiteral:
49
+ value: str
50
+ line_number: int
51
+ column: int
52
+
53
+ def to_dict(self) -> Dict[str, Any]:
54
+ return asdict(self)
55
+
56
+
57
+ class TreeSitterWalker:
58
+ """
59
+ Traverses Tree-sitter parse trees and extracts normalized language constructs.
60
+ """
61
+
62
+ @staticmethod
63
+ def extract_function_calls(root_node, source_bytes: bytes) -> List[FunctionCall]:
64
+ calls: List[FunctionCall] = []
65
+
66
+ def visit(node):
67
+ # Python: call -> function, arguments
68
+ # JS/TS: call_expression -> function, arguments
69
+ # Go: call_expression -> function, argument_list
70
+ if node.type in ("call", "call_expression"):
71
+ func_child = node.child_by_field_name("function")
72
+ args_child = node.child_by_field_name("arguments") or node.child_by_field_name("argument_list")
73
+
74
+ func_name = ""
75
+ receiver = None
76
+ if func_child:
77
+ func_text = source_bytes[func_child.start_byte:func_child.end_byte].decode("utf-8", errors="replace")
78
+ func_name = func_text
79
+ if "." in func_text:
80
+ parts = func_text.split(".")
81
+ receiver = ".".join(parts[:-1])
82
+
83
+ args_list: List[str] = []
84
+ if args_child:
85
+ for ch in args_child.children:
86
+ if ch.type not in (",", "(", ")"):
87
+ arg_text = source_bytes[ch.start_byte:ch.end_byte].decode("utf-8", errors="replace").strip()
88
+ if arg_text:
89
+ args_list.append(arg_text)
90
+
91
+ line_num = node.start_point[0] + 1
92
+ col = node.start_point[1]
93
+ call_text = source_bytes[node.start_byte:node.end_byte].decode("utf-8", errors="replace")
94
+
95
+ calls.append(FunctionCall(
96
+ name=func_name,
97
+ full_call=call_text,
98
+ arguments=args_list,
99
+ line_number=line_num,
100
+ column=col,
101
+ receiver=receiver
102
+ ))
103
+
104
+ for ch in node.children:
105
+ visit(ch)
106
+
107
+ visit(root_node)
108
+ return calls
109
+
110
+ @staticmethod
111
+ def extract_assignments(root_node, source_bytes: bytes) -> List[Assignment]:
112
+ assignments: List[Assignment] = []
113
+
114
+ def visit(node):
115
+ # Python: assignment -> left, right
116
+ # JS/TS: variable_declarator (name, value), assignment_expression (left, right)
117
+ # Go: short_var_declaration (left, right), assignment_statement (left, right)
118
+ target = ""
119
+ val_expr = ""
120
+ line_num = node.start_point[0] + 1
121
+ col = node.start_point[1]
122
+
123
+ if node.type == "assignment": # Python
124
+ left = node.child_by_field_name("left")
125
+ right = node.child_by_field_name("right")
126
+ if left and right:
127
+ target = source_bytes[left.start_byte:left.end_byte].decode("utf-8", errors="replace").strip()
128
+ val_expr = source_bytes[right.start_byte:right.end_byte].decode("utf-8", errors="replace").strip()
129
+ assignments.append(Assignment(target=target, value_expression=val_expr, line_number=line_num, column=col))
130
+
131
+ elif node.type == "variable_declarator": # JS/TS
132
+ name = node.child_by_field_name("name")
133
+ val = node.child_by_field_name("value")
134
+ if name:
135
+ target = source_bytes[name.start_byte:name.end_byte].decode("utf-8", errors="replace").strip()
136
+ if val:
137
+ val_expr = source_bytes[val.start_byte:val.end_byte].decode("utf-8", errors="replace").strip()
138
+ assignments.append(Assignment(target=target, value_expression=val_expr, line_number=line_num, column=col))
139
+
140
+ elif node.type == "assignment_expression": # JS/TS
141
+ left = node.child_by_field_name("left")
142
+ right = node.child_by_field_name("right")
143
+ if left and right:
144
+ target = source_bytes[left.start_byte:left.end_byte].decode("utf-8", errors="replace").strip()
145
+ val_expr = source_bytes[right.start_byte:right.end_byte].decode("utf-8", errors="replace").strip()
146
+ assignments.append(Assignment(target=target, value_expression=val_expr, line_number=line_num, column=col))
147
+
148
+ elif node.type in ("short_var_declaration", "assignment_statement"): # Go
149
+ left = node.child_by_field_name("left")
150
+ right = node.child_by_field_name("right")
151
+ if left and right:
152
+ target = source_bytes[left.start_byte:left.end_byte].decode("utf-8", errors="replace").strip()
153
+ val_expr = source_bytes[right.start_byte:right.end_byte].decode("utf-8", errors="replace").strip()
154
+ assignments.append(Assignment(target=target, value_expression=val_expr, line_number=line_num, column=col))
155
+
156
+ for ch in node.children:
157
+ visit(ch)
158
+
159
+ visit(root_node)
160
+ return assignments
161
+
162
+ @staticmethod
163
+ def extract_imports(root_node, source_bytes: bytes, language: str) -> List[ImportStatement]:
164
+ imports: List[ImportStatement] = []
165
+
166
+ def visit(node):
167
+ line_num = node.start_point[0] + 1
168
+
169
+ # Python imports
170
+ if node.type == "import_statement":
171
+ raw = source_bytes[node.start_byte:node.end_byte].decode("utf-8", errors="replace").strip()
172
+ # e.g. import os, sys as s
173
+ names = []
174
+ alias_map = {}
175
+ for ch in node.children:
176
+ if ch.type == "dotted_name":
177
+ mod = source_bytes[ch.start_byte:ch.end_byte].decode("utf-8", errors="replace").strip()
178
+ names.append(mod)
179
+ elif ch.type == "aliased_import":
180
+ n = ch.child_by_field_name("name")
181
+ a = ch.child_by_field_name("alias")
182
+ if n and a:
183
+ name_str = source_bytes[n.start_byte:n.end_byte].decode("utf-8", errors="replace").strip()
184
+ alias_str = source_bytes[a.start_byte:a.end_byte].decode("utf-8", errors="replace").strip()
185
+ names.append(name_str)
186
+ alias_map[alias_str] = name_str
187
+ for n in names:
188
+ imports.append(ImportStatement(module=n, imported_names=[n], alias_map=alias_map, line_number=line_num))
189
+
190
+ elif node.type == "import_from_statement":
191
+ # from module import a, b as c
192
+ module_name = ""
193
+ mod_child = node.child_by_field_name("module_name")
194
+ if mod_child:
195
+ module_name = source_bytes[mod_child.start_byte:mod_child.end_byte].decode("utf-8", errors="replace").strip()
196
+ names = []
197
+ alias_map = {}
198
+ for ch in node.children:
199
+ if ch.type == "dotted_name" and ch != mod_child:
200
+ names.append(source_bytes[ch.start_byte:ch.end_byte].decode("utf-8", errors="replace").strip())
201
+ elif ch.type == "aliased_import":
202
+ n = ch.child_by_field_name("name")
203
+ a = ch.child_by_field_name("alias")
204
+ if n and a:
205
+ n_str = source_bytes[n.start_byte:n.end_byte].decode("utf-8", errors="replace").strip()
206
+ a_str = source_bytes[a.start_byte:a.end_byte].decode("utf-8", errors="replace").strip()
207
+ names.append(n_str)
208
+ alias_map[a_str] = n_str
209
+ is_rel = module_name.startswith(".")
210
+ imports.append(ImportStatement(
211
+ module=module_name,
212
+ imported_names=names,
213
+ alias_map=alias_map,
214
+ line_number=line_num,
215
+ is_relative=is_rel
216
+ ))
217
+
218
+ # JS/TS imports: import_statement
219
+ elif node.type == "import_statement":
220
+ raw = source_bytes[node.start_byte:node.end_byte].decode("utf-8", errors="replace").strip()
221
+ source_child = node.child_by_field_name("source")
222
+ source_mod = ""
223
+ if source_child:
224
+ source_mod = source_bytes[source_child.start_byte:source_child.end_byte].decode("utf-8", errors="replace").strip().strip("'\"")
225
+ names = []
226
+ alias_map = {}
227
+ for ch in node.children:
228
+ if ch.type == "import_clause":
229
+ clause_str = source_bytes[ch.start_byte:ch.end_byte].decode("utf-8", errors="replace").strip()
230
+ names.append(clause_str)
231
+ is_rel = source_mod.startswith(".")
232
+ imports.append(ImportStatement(
233
+ module=source_mod,
234
+ imported_names=names,
235
+ alias_map=alias_map,
236
+ line_number=line_num,
237
+ is_relative=is_rel
238
+ ))
239
+
240
+ # Go imports: import_declaration
241
+ elif node.type == "import_declaration":
242
+ for ch in node.children:
243
+ if ch.type in ("import_spec", "import_spec_list"):
244
+ for spec in (ch.children if ch.type == "import_spec_list" else [ch]):
245
+ if spec.type == "import_spec":
246
+ path_child = spec.child_by_field_name("path")
247
+ if path_child:
248
+ path_str = source_bytes[path_child.start_byte:path_child.end_byte].decode("utf-8", errors="replace").strip().strip('"')
249
+ name_child = spec.child_by_field_name("name")
250
+ alias = ""
251
+ if name_child:
252
+ alias = source_bytes[name_child.start_byte:name_child.end_byte].decode("utf-8", errors="replace").strip()
253
+ alias_map = {alias: path_str} if alias else {}
254
+ imports.append(ImportStatement(
255
+ module=path_str,
256
+ imported_names=[path_str],
257
+ alias_map=alias_map,
258
+ line_number=spec.start_point[0] + 1
259
+ ))
260
+
261
+ for ch in node.children:
262
+ visit(ch)
263
+
264
+ visit(root_node)
265
+ return imports
266
+
267
+ @staticmethod
268
+ def extract_string_literals(root_node, source_bytes: bytes) -> List[StringLiteral]:
269
+ literals: List[StringLiteral] = []
270
+
271
+ def visit(node):
272
+ if node.type in ("string", "string_literal", "raw_string_literal", "template_string"):
273
+ text = source_bytes[node.start_byte:node.end_byte].decode("utf-8", errors="replace")
274
+ literals.append(StringLiteral(
275
+ value=text.strip("\"'`"),
276
+ line_number=node.start_point[0] + 1,
277
+ column=node.start_point[1]
278
+ ))
279
+ for ch in node.children:
280
+ visit(ch)
281
+
282
+ visit(root_node)
283
+ return literals