attocode-code-intel 0.1.0__py3-none-any.whl

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 (345) hide show
  1. attocode_code_intel-0.1.0.dist-info/METADATA +221 -0
  2. attocode_code_intel-0.1.0.dist-info/RECORD +345 -0
  3. attocode_code_intel-0.1.0.dist-info/WHEEL +4 -0
  4. attocode_code_intel-0.1.0.dist-info/entry_points.txt +2 -0
  5. attocode_code_intel-0.1.0.dist-info/licenses/LICENSE +21 -0
  6. attocode_intel/GUIDELINES.md +432 -0
  7. attocode_intel/__init__.py +52 -0
  8. attocode_intel/_internal/__init__.py +1 -0
  9. attocode_intel/_internal/errors/__init__.py +126 -0
  10. attocode_intel/_internal/integrations/__init__.py +1 -0
  11. attocode_intel/_internal/integrations/context/__init__.py +1 -0
  12. attocode_intel/_internal/integrations/context/architecture_drift.py +354 -0
  13. attocode_intel/_internal/integrations/context/ast_chunker.py +202 -0
  14. attocode_intel/_internal/integrations/context/ast_service.py +1483 -0
  15. attocode_intel/_internal/integrations/context/blob_oid.py +423 -0
  16. attocode_intel/_internal/integrations/context/cache_manifest.py +221 -0
  17. attocode_intel/_internal/integrations/context/cas.py +466 -0
  18. attocode_intel/_internal/integrations/context/code_analyzer.py +528 -0
  19. attocode_intel/_internal/integrations/context/codebase_ast.py +1461 -0
  20. attocode_intel/_internal/integrations/context/codebase_context.py +1736 -0
  21. attocode_intel/_internal/integrations/context/cross_mode.py +263 -0
  22. attocode_intel/_internal/integrations/context/cross_references.py +616 -0
  23. attocode_intel/_internal/integrations/context/embedding_rotation.py +597 -0
  24. attocode_intel/_internal/integrations/context/embeddings.py +282 -0
  25. attocode_intel/_internal/integrations/context/frecency.py +427 -0
  26. attocode_intel/_internal/integrations/context/fuzzy.py +320 -0
  27. attocode_intel/_internal/integrations/context/hierarchical_explorer.py +331 -0
  28. attocode_intel/_internal/integrations/context/hydration.py +90 -0
  29. attocode_intel/_internal/integrations/context/index_repair.py +87 -0
  30. attocode_intel/_internal/integrations/context/index_store.py +586 -0
  31. attocode_intel/_internal/integrations/context/memory_store.py +602 -0
  32. attocode_intel/_internal/integrations/context/module_resolution.py +52 -0
  33. attocode_intel/_internal/integrations/context/query_constraints.py +361 -0
  34. attocode_intel/_internal/integrations/context/query_history.py +417 -0
  35. attocode_intel/_internal/integrations/context/reranker.py +351 -0
  36. attocode_intel/_internal/integrations/context/semantic_search.py +2931 -0
  37. attocode_intel/_internal/integrations/context/syntax_evidence.py +94 -0
  38. attocode_intel/_internal/integrations/context/systemone_ranker.py +245 -0
  39. attocode_intel/_internal/integrations/context/temporal_coupling.py +411 -0
  40. attocode_intel/_internal/integrations/context/trigram_index.py +820 -0
  41. attocode_intel/_internal/integrations/context/trigram_regex.py +171 -0
  42. attocode_intel/_internal/integrations/context/ts_parser.py +2199 -0
  43. attocode_intel/_internal/integrations/context/vector_store.py +795 -0
  44. attocode_intel/_internal/integrations/feature_flags/__init__.py +357 -0
  45. attocode_intel/_internal/integrations/lsp/__init__.py +1 -0
  46. attocode_intel/_internal/integrations/lsp/client.py +1140 -0
  47. attocode_intel/_internal/integrations/mcp/__init__.py +1 -0
  48. attocode_intel/_internal/integrations/mcp/client.py +253 -0
  49. attocode_intel/_internal/integrations/security/__init__.py +1 -0
  50. attocode_intel/_internal/integrations/security/custom_rules.py +176 -0
  51. attocode_intel/_internal/integrations/security/dataflow.py +522 -0
  52. attocode_intel/_internal/integrations/security/dependency_audit.py +303 -0
  53. attocode_intel/_internal/integrations/security/matcher.py +44 -0
  54. attocode_intel/_internal/integrations/security/patterns.py +724 -0
  55. attocode_intel/_internal/integrations/security/scanner.py +351 -0
  56. attocode_intel/_internal/integrations/utilities/__init__.py +1 -0
  57. attocode_intel/_internal/integrations/utilities/ignore.py +64 -0
  58. attocode_intel/_internal/integrations/utilities/token_estimate.py +51 -0
  59. attocode_intel/_internal/tricks/__init__.py +1 -0
  60. attocode_intel/_internal/tricks/recursive_context.py +357 -0
  61. attocode_intel/_internal/types/__init__.py +1 -0
  62. attocode_intel/_internal/types/events.py +289 -0
  63. attocode_intel/_internal/types/severity.py +15 -0
  64. attocode_intel/_shared.py +341 -0
  65. attocode_intel/analysis_coverage.py +56 -0
  66. attocode_intel/api/__init__.py +1 -0
  67. attocode_intel/api/access.py +32 -0
  68. attocode_intel/api/app.py +247 -0
  69. attocode_intel/api/auth/__init__.py +128 -0
  70. attocode_intel/api/auth/api_keys.py +61 -0
  71. attocode_intel/api/auth/context.py +34 -0
  72. attocode_intel/api/auth/jwt.py +66 -0
  73. attocode_intel/api/auth/oauth.py +146 -0
  74. attocode_intel/api/auth/passwords.py +15 -0
  75. attocode_intel/api/deps.py +351 -0
  76. attocode_intel/api/knowledge_bridge.py +13 -0
  77. attocode_intel/api/mcp_transport.py +51 -0
  78. attocode_intel/api/middleware.py +411 -0
  79. attocode_intel/api/models.py +698 -0
  80. attocode_intel/api/providers/__init__.py +1 -0
  81. attocode_intel/api/providers/db_provider.py +1106 -0
  82. attocode_intel/api/providers/local_provider.py +476 -0
  83. attocode_intel/api/providers/remote_provider.py +740 -0
  84. attocode_intel/api/repository_source.py +21 -0
  85. attocode_intel/api/routes/__init__.py +1 -0
  86. attocode_intel/api/routes/_pin_helper.py +158 -0
  87. attocode_intel/api/routes/activity.py +148 -0
  88. attocode_intel/api/routes/adr.py +102 -0
  89. attocode_intel/api/routes/analysis.py +515 -0
  90. attocode_intel/api/routes/api_keys.py +170 -0
  91. attocode_intel/api/routes/auth.py +428 -0
  92. attocode_intel/api/routes/branches.py +262 -0
  93. attocode_intel/api/routes/cross_repo_search.py +198 -0
  94. attocode_intel/api/routes/embeddings.py +386 -0
  95. attocode_intel/api/routes/files.py +132 -0
  96. attocode_intel/api/routes/files_v2.py +388 -0
  97. attocode_intel/api/routes/gc.py +322 -0
  98. attocode_intel/api/routes/git_v2.py +433 -0
  99. attocode_intel/api/routes/graph.py +174 -0
  100. attocode_intel/api/routes/graph_viz.py +160 -0
  101. attocode_intel/api/routes/health.py +62 -0
  102. attocode_intel/api/routes/history.py +175 -0
  103. attocode_intel/api/routes/jobs.py +84 -0
  104. attocode_intel/api/routes/learning.py +205 -0
  105. attocode_intel/api/routes/lsp.py +272 -0
  106. attocode_intel/api/routes/notify.py +623 -0
  107. attocode_intel/api/routes/operations.py +27 -0
  108. attocode_intel/api/routes/orgs.py +924 -0
  109. attocode_intel/api/routes/preferences.py +100 -0
  110. attocode_intel/api/routes/presence.py +42 -0
  111. attocode_intel/api/routes/projects.py +116 -0
  112. attocode_intel/api/routes/repos.py +47 -0
  113. attocode_intel/api/routes/search.py +180 -0
  114. attocode_intel/api/routes/snapshots.py +614 -0
  115. attocode_intel/api/routes/webhooks.py +313 -0
  116. attocode_intel/api/routes/websocket.py +116 -0
  117. attocode_intel/api/static/graph.html +239 -0
  118. attocode_intel/api/utils.py +96 -0
  119. attocode_intel/artifacts/__init__.py +45 -0
  120. attocode_intel/artifacts/hashing.py +230 -0
  121. attocode_intel/artifacts/provenance.py +190 -0
  122. attocode_intel/artifacts/retrieval_pin.py +159 -0
  123. attocode_intel/audit.py +59 -0
  124. attocode_intel/bug_finder.py +206 -0
  125. attocode_intel/bundle.py +80 -0
  126. attocode_intel/catalog.py +242 -0
  127. attocode_intel/change_review.py +75 -0
  128. attocode_intel/cli.py +3062 -0
  129. attocode_intel/community.py +63 -0
  130. attocode_intel/confidence/__init__.py +144 -0
  131. attocode_intel/confidence/jev.py +234 -0
  132. attocode_intel/confidence/llm.py +330 -0
  133. attocode_intel/confidence/redact.py +44 -0
  134. attocode_intel/confidence/settings.py +74 -0
  135. attocode_intel/config.py +256 -0
  136. attocode_intel/crypto.py +31 -0
  137. attocode_intel/db/__init__.py +7 -0
  138. attocode_intel/db/base.py +33 -0
  139. attocode_intel/db/engine.py +61 -0
  140. attocode_intel/db/models.py +529 -0
  141. attocode_intel/entrypoint.py +185 -0
  142. attocode_intel/focused_evidence.py +130 -0
  143. attocode_intel/freshness.py +154 -0
  144. attocode_intel/gateway.py +734 -0
  145. attocode_intel/git/__init__.py +7 -0
  146. attocode_intel/git/credentials.py +75 -0
  147. attocode_intel/git/manager.py +493 -0
  148. attocode_intel/git/models.py +96 -0
  149. attocode_intel/git/storage.py +98 -0
  150. attocode_intel/graph_query_parser.py +761 -0
  151. attocode_intel/helpers.py +712 -0
  152. attocode_intel/indexing/__init__.py +29 -0
  153. attocode_intel/indexing/debouncer.py +136 -0
  154. attocode_intel/indexing/delta_indexer.py +151 -0
  155. attocode_intel/indexing/full_indexer.py +323 -0
  156. attocode_intel/indexing/incremental.py +240 -0
  157. attocode_intel/indexing/parser.py +403 -0
  158. attocode_intel/indexing/summarizer.py +264 -0
  159. attocode_intel/installer.py +1896 -0
  160. attocode_intel/knowledge.py +269 -0
  161. attocode_intel/local_knowledge.py +77 -0
  162. attocode_intel/migrations/alembic.ini +36 -0
  163. attocode_intel/migrations/env.py +71 -0
  164. attocode_intel/migrations/script.py.mako +27 -0
  165. attocode_intel/migrations/versions/001_initial_schema.py +219 -0
  166. attocode_intel/migrations/versions/002_branch_version_and_embeddings.py +61 -0
  167. attocode_intel/migrations/versions/003_embedding_uniqueness_and_cascade.py +51 -0
  168. attocode_intel/migrations/versions/004_audit_events.py +38 -0
  169. attocode_intel/migrations/versions/005_user_preferences.py +30 -0
  170. attocode_intel/migrations/versions/006_google_oauth.py +27 -0
  171. attocode_intel/migrations/versions/007_pgvector_embeddings.py +28 -0
  172. attocode_intel/migrations/versions/008_symbol_references.py +37 -0
  173. attocode_intel/migrations/versions/009_webhook_secret_encryption.py +25 -0
  174. attocode_intel/migrations/versions/010_add_commits_table.py +40 -0
  175. attocode_intel/migrations/versions/011_commit_changed_files.py +25 -0
  176. attocode_intel/migrations/versions/012_branch_merged_at.py +25 -0
  177. attocode_intel/migrations/versions/013_revoked_tokens.py +32 -0
  178. attocode_intel/migrations/versions/014_blame_hunks.py +40 -0
  179. attocode_intel/migrations/versions/015_commit_file_stats.py +40 -0
  180. attocode_intel/migrations/versions/016_embedding_provenance_columns.py +179 -0
  181. attocode_intel/migrations/versions/017_provenance_table.py +102 -0
  182. attocode_intel/migrations/versions/018_repo_snapshots.py +178 -0
  183. attocode_intel/migrations/versions/019_symref_caller.py +42 -0
  184. attocode_intel/migrations/versions/020_shared_knowledge.py +29 -0
  185. attocode_intel/onboarding.py +258 -0
  186. attocode_intel/output.py +178 -0
  187. attocode_intel/precision.py +133 -0
  188. attocode_intel/presence.py +160 -0
  189. attocode_intel/probe.py +110 -0
  190. attocode_intel/project_dir.py +97 -0
  191. attocode_intel/pubsub.py +113 -0
  192. attocode_intel/query_ranking.py +242 -0
  193. attocode_intel/readiness.py +972 -0
  194. attocode_intel/redis.py +51 -0
  195. attocode_intel/reference_pages.py +60 -0
  196. attocode_intel/remote.py +131 -0
  197. attocode_intel/repo_ranker.py +246 -0
  198. attocode_intel/request_context.py +82 -0
  199. attocode_intel/rules/__init__.py +28 -0
  200. attocode_intel/rules/ci.py +320 -0
  201. attocode_intel/rules/combinators.py +293 -0
  202. attocode_intel/rules/enricher.py +127 -0
  203. attocode_intel/rules/evolution.py +452 -0
  204. attocode_intel/rules/executor.py +531 -0
  205. attocode_intel/rules/filters/__init__.py +5 -0
  206. attocode_intel/rules/filters/pipeline.py +151 -0
  207. attocode_intel/rules/formatter.py +198 -0
  208. attocode_intel/rules/hygiene.py +242 -0
  209. attocode_intel/rules/importers/__init__.py +1 -0
  210. attocode_intel/rules/importers/semgrep.py +382 -0
  211. attocode_intel/rules/loader.py +292 -0
  212. attocode_intel/rules/marketplace.py +375 -0
  213. attocode_intel/rules/metavar.py +181 -0
  214. attocode_intel/rules/model.py +317 -0
  215. attocode_intel/rules/packs/__init__.py +1 -0
  216. attocode_intel/rules/packs/community/_license_templates/Apache-2.0.txt +176 -0
  217. attocode_intel/rules/packs/community/_license_templates/MIT.txt +21 -0
  218. attocode_intel/rules/packs/community/bandit-python/LICENSE +176 -0
  219. attocode_intel/rules/packs/community/bandit-python/NOTICE +13 -0
  220. attocode_intel/rules/packs/community/bandit-python/PORTING.md +23 -0
  221. attocode_intel/rules/packs/community/bandit-python/manifest.yaml +14 -0
  222. attocode_intel/rules/packs/community/bandit-python/rules/B105-hardcoded-password.yaml +23 -0
  223. attocode_intel/rules/packs/community/bandit-python/rules/B303-weak-md5.yaml +21 -0
  224. attocode_intel/rules/packs/community/bandit-python/rules/B304-weak-des.yaml +20 -0
  225. attocode_intel/rules/packs/community/bandit-python/rules/B307-use-of-eval.yaml +22 -0
  226. attocode_intel/rules/packs/community/bandit-python/rules/B324-weak-sha1.yaml +21 -0
  227. attocode_intel/rules/packs/community/bandit-python/rules/B501-no-cert-validation.yaml +23 -0
  228. attocode_intel/rules/packs/community/bandit-python/rules/B602-shell-true.yaml +22 -0
  229. attocode_intel/rules/packs/community/bandit-python/rules/B608-sqli.yaml +22 -0
  230. attocode_intel/rules/packs/community/eslint-typescript/LICENSE +21 -0
  231. attocode_intel/rules/packs/community/eslint-typescript/NOTICE +13 -0
  232. attocode_intel/rules/packs/community/eslint-typescript/PORTING.md +23 -0
  233. attocode_intel/rules/packs/community/eslint-typescript/manifest.yaml +14 -0
  234. attocode_intel/rules/packs/community/eslint-typescript/rules/eqeqeq.yaml +20 -0
  235. attocode_intel/rules/packs/community/eslint-typescript/rules/no-eval.yaml +22 -0
  236. attocode_intel/rules/packs/community/eslint-typescript/rules/no-implied-eval.yaml +20 -0
  237. attocode_intel/rules/packs/community/eslint-typescript/rules/no-new-func.yaml +21 -0
  238. attocode_intel/rules/packs/community/eslint-typescript/rules/no-throw-literal.yaml +19 -0
  239. attocode_intel/rules/packs/community/eslint-typescript/rules/no-var.yaml +20 -0
  240. attocode_intel/rules/packs/community/eslint-typescript/rules/no-with.yaml +18 -0
  241. attocode_intel/rules/packs/community/gosec-go/LICENSE +176 -0
  242. attocode_intel/rules/packs/community/gosec-go/NOTICE +13 -0
  243. attocode_intel/rules/packs/community/gosec-go/PORTING.md +23 -0
  244. attocode_intel/rules/packs/community/gosec-go/manifest.yaml +14 -0
  245. attocode_intel/rules/packs/community/gosec-go/rules/G101-hardcoded-credentials.yaml +22 -0
  246. attocode_intel/rules/packs/community/gosec-go/rules/G102-bind-all-interfaces.yaml +21 -0
  247. attocode_intel/rules/packs/community/gosec-go/rules/G103-unsafe-block.yaml +21 -0
  248. attocode_intel/rules/packs/community/gosec-go/rules/G201-sql-format.yaml +21 -0
  249. attocode_intel/rules/packs/community/gosec-go/rules/G304-file-inclusion.yaml +23 -0
  250. attocode_intel/rules/packs/community/gosec-go/rules/G401-weak-cipher.yaml +20 -0
  251. attocode_intel/rules/packs/community/gosec-go/rules/G402-tls-min-version.yaml +20 -0
  252. attocode_intel/rules/packs/community/gosec-go/rules/G501-md5-import.yaml +21 -0
  253. attocode_intel/rules/packs/examples/cpp/manifest.yaml +4 -0
  254. attocode_intel/rules/packs/examples/cpp/rules/security.yaml +90 -0
  255. attocode_intel/rules/packs/examples/cpp/rules/structural.yaml +42 -0
  256. attocode_intel/rules/packs/examples/csharp/manifest.yaml +4 -0
  257. attocode_intel/rules/packs/examples/csharp/rules/security.yaml +69 -0
  258. attocode_intel/rules/packs/examples/csharp/rules/structural.yaml +42 -0
  259. attocode_intel/rules/packs/examples/go/manifest.yaml +4 -0
  260. attocode_intel/rules/packs/examples/go/rules/performance.yaml +136 -0
  261. attocode_intel/rules/packs/examples/go/rules/security.yaml +48 -0
  262. attocode_intel/rules/packs/examples/go/rules/structural.yaml +58 -0
  263. attocode_intel/rules/packs/examples/go/taint/sanitizers.yaml +24 -0
  264. attocode_intel/rules/packs/examples/go/taint/sinks.yaml +51 -0
  265. attocode_intel/rules/packs/examples/go/taint/sources.yaml +26 -0
  266. attocode_intel/rules/packs/examples/java/manifest.yaml +4 -0
  267. attocode_intel/rules/packs/examples/java/rules/correctness.yaml +55 -0
  268. attocode_intel/rules/packs/examples/java/rules/structural.yaml +48 -0
  269. attocode_intel/rules/packs/examples/java/taint/sinks.yaml +37 -0
  270. attocode_intel/rules/packs/examples/java/taint/sources.yaml +21 -0
  271. attocode_intel/rules/packs/examples/kotlin/manifest.yaml +4 -0
  272. attocode_intel/rules/packs/examples/kotlin/rules/correctness.yaml +50 -0
  273. attocode_intel/rules/packs/examples/kotlin/rules/structural.yaml +38 -0
  274. attocode_intel/rules/packs/examples/php/manifest.yaml +4 -0
  275. attocode_intel/rules/packs/examples/php/rules/security.yaml +66 -0
  276. attocode_intel/rules/packs/examples/php/rules/structural.yaml +41 -0
  277. attocode_intel/rules/packs/examples/python/manifest.yaml +4 -0
  278. attocode_intel/rules/packs/examples/python/rules/performance.yaml +90 -0
  279. attocode_intel/rules/packs/examples/python/rules/structural.yaml +55 -0
  280. attocode_intel/rules/packs/examples/ruby/manifest.yaml +4 -0
  281. attocode_intel/rules/packs/examples/ruby/rules/security.yaml +73 -0
  282. attocode_intel/rules/packs/examples/ruby/rules/structural.yaml +41 -0
  283. attocode_intel/rules/packs/examples/rust/manifest.yaml +4 -0
  284. attocode_intel/rules/packs/examples/rust/rules/correctness.yaml +51 -0
  285. attocode_intel/rules/packs/examples/rust/rules/structural.yaml +49 -0
  286. attocode_intel/rules/packs/examples/typescript/manifest.yaml +4 -0
  287. attocode_intel/rules/packs/examples/typescript/rules/performance.yaml +70 -0
  288. attocode_intel/rules/packs/examples/typescript/rules/structural.yaml +48 -0
  289. attocode_intel/rules/packs/pack_loader.py +244 -0
  290. attocode_intel/rules/plugins/__init__.py +1 -0
  291. attocode_intel/rules/plugins/plugin_loader.py +132 -0
  292. attocode_intel/rules/profiling.py +332 -0
  293. attocode_intel/rules/registry.py +213 -0
  294. attocode_intel/rules/sarif.py +187 -0
  295. attocode_intel/rules/synthesis.py +420 -0
  296. attocode_intel/rules/testing.py +326 -0
  297. attocode_intel/server.py +734 -0
  298. attocode_intel/service.py +3416 -0
  299. attocode_intel/sharing.py +57 -0
  300. attocode_intel/storage/__init__.py +6 -0
  301. attocode_intel/storage/branch_overlay.py +352 -0
  302. attocode_intel/storage/content_store.py +220 -0
  303. attocode_intel/storage/dependency_store.py +143 -0
  304. attocode_intel/storage/diff_engine.py +187 -0
  305. attocode_intel/storage/embedding_store.py +594 -0
  306. attocode_intel/storage/provenance_store.py +137 -0
  307. attocode_intel/storage/security_scanner_db.py +164 -0
  308. attocode_intel/storage/symbol_store.py +160 -0
  309. attocode_intel/structural_evidence.py +144 -0
  310. attocode_intel/symbol_inspection.py +141 -0
  311. attocode_intel/symbol_links.py +167 -0
  312. attocode_intel/telemetry.py +42 -0
  313. attocode_intel/test_ranking.py +73 -0
  314. attocode_intel/testing/__init__.py +43 -0
  315. attocode_intel/testing/fixtures.py +122 -0
  316. attocode_intel/testing/helpers.py +75 -0
  317. attocode_intel/testing/mocks.py +99 -0
  318. attocode_intel/tools/__init__.py +2 -0
  319. attocode_intel/tools/adr_tools.py +628 -0
  320. attocode_intel/tools/analysis_tools.py +382 -0
  321. attocode_intel/tools/composite_tools.py +695 -0
  322. attocode_intel/tools/cross_mode_tools.py +173 -0
  323. attocode_intel/tools/dead_code_tools.py +582 -0
  324. attocode_intel/tools/distill_tools.py +353 -0
  325. attocode_intel/tools/frecency_tools.py +220 -0
  326. attocode_intel/tools/fuzzy_tools.py +255 -0
  327. attocode_intel/tools/history_tools.py +486 -0
  328. attocode_intel/tools/learning_tools.py +222 -0
  329. attocode_intel/tools/lsp_tools.py +218 -0
  330. attocode_intel/tools/maintenance_tools.py +1662 -0
  331. attocode_intel/tools/navigation_tools.py +220 -0
  332. attocode_intel/tools/overlay_tools.py +540 -0
  333. attocode_intel/tools/pin_store.py +432 -0
  334. attocode_intel/tools/pin_tools.py +266 -0
  335. attocode_intel/tools/query_constraints_tools.py +262 -0
  336. attocode_intel/tools/query_history_tools.py +158 -0
  337. attocode_intel/tools/readiness_tools.py +44 -0
  338. attocode_intel/tools/rule_tools.py +992 -0
  339. attocode_intel/tools/search_tools.py +770 -0
  340. attocode_intel/tools/snapshot_tools.py +624 -0
  341. attocode_intel/workers/__init__.py +1 -0
  342. attocode_intel/workers/job_utils.py +111 -0
  343. attocode_intel/workers/jobs.py +573 -0
  344. attocode_intel/workers/run.py +30 -0
  345. attocode_intel/workers/settings.py +61 -0
@@ -0,0 +1,522 @@
1
+ """Lightweight intra-procedural data flow analysis.
2
+
3
+ Tracks taint from sources (user input, request parameters) through
4
+ assignments and function calls to sinks (SQL execution, shell commands,
5
+ HTML output) within individual functions. Uses tree-sitter ASTs for
6
+ Python and JavaScript/TypeScript.
7
+
8
+ This is NOT a full compiler-grade taint tracker. It provides best-effort
9
+ detection of common vulnerability patterns (CWE-89, CWE-78, CWE-79,
10
+ CWE-22, CWE-918) without requiring compilation or type information.
11
+
12
+ Limitations:
13
+ - Intra-procedural only (does not follow function calls across files)
14
+ - No alias analysis (reassignment through containers not tracked)
15
+ - No type inference (relies on naming conventions and API patterns)
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ import os
22
+ import re
23
+ from dataclasses import dataclass
24
+ from pathlib import Path
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ # ---------------------------------------------------------------------------
30
+ # Source / Sink definitions
31
+ # ---------------------------------------------------------------------------
32
+
33
+ @dataclass(slots=True, frozen=True)
34
+ class TaintSource:
35
+ """A source of tainted (user-controlled) data."""
36
+ name: str
37
+ pattern: re.Pattern[str]
38
+ language: str # "python", "javascript", or "" for all
39
+
40
+
41
+ @dataclass(slots=True, frozen=True)
42
+ class TaintSink:
43
+ """A dangerous function that should not receive tainted data."""
44
+ name: str
45
+ pattern: re.Pattern[str]
46
+ cwe: str
47
+ message: str
48
+ language: str
49
+
50
+
51
+ @dataclass(slots=True)
52
+ class DataFlowFinding:
53
+ """A taint flow from source to sink within a function."""
54
+ file_path: str
55
+ function_name: str
56
+ source_line: int
57
+ source_desc: str
58
+ sink_line: int
59
+ sink_desc: str
60
+ tainted_var: str
61
+ cwe: str
62
+ message: str
63
+ severity: str = "high"
64
+
65
+
66
+ @dataclass(slots=True)
67
+ class DataFlowReport:
68
+ """Results of data flow analysis."""
69
+ findings: list[DataFlowFinding]
70
+ functions_analyzed: int
71
+ files_analyzed: int
72
+ scan_time_ms: float = 0.0
73
+
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Python sources and sinks
77
+ # ---------------------------------------------------------------------------
78
+
79
+ _PYTHON_SOURCES: list[TaintSource] = [
80
+ TaintSource("request_param", re.compile(
81
+ r"""\b(?:request\.(?:args|form|json|data|values|files|headers|cookies|GET|POST)|"""
82
+ r"""flask\.request\.\w+|"""
83
+ r"""self\.request\.\w+)"""
84
+ ), "python"),
85
+ TaintSource("input_builtin", re.compile(r"""\binput\s*\("""), "python"),
86
+ TaintSource("sys_argv", re.compile(r"""\bsys\.argv\b"""), "python"),
87
+ TaintSource("environ_get", re.compile(r"""\bos\.environ\b"""), "python"),
88
+ TaintSource("query_param", re.compile(
89
+ r"""\b(?:params|query_params|request_data)\b"""
90
+ ), "python"),
91
+ ]
92
+
93
+ _PYTHON_SINKS: list[TaintSink] = [
94
+ TaintSink("sql_execute", re.compile(
95
+ r"""\b(?:cursor\.execute|\.execute|\.executemany|\.raw)\s*\("""
96
+ ), "CWE-89", "SQL injection: tainted data reaches SQL execution", "python"),
97
+ TaintSink("os_system", re.compile(
98
+ r"""\b(?:os\.system|os\.popen|subprocess\.(?:call|run|Popen|check_output|check_call))\s*\("""
99
+ ), "CWE-78", "Command injection: tainted data reaches shell execution", "python"),
100
+ TaintSink("open_file", re.compile(
101
+ r"""\bopen\s*\("""
102
+ ), "CWE-22", "Path traversal: tainted data used in file path", "python"),
103
+ TaintSink("url_request", re.compile(
104
+ r"""\b(?:requests\.(?:get|post|put|delete|patch|head)|urllib\.request\.urlopen|httpx\.(?:get|post))\s*\("""
105
+ ), "CWE-918", "SSRF: tainted data used in URL construction", "python"),
106
+ TaintSink("html_output", re.compile(
107
+ r"""\b(?:render_template_string|Markup|mark_safe|SafeString)\s*\("""
108
+ ), "CWE-79", "XSS: tainted data rendered as HTML without escaping", "python"),
109
+ ]
110
+
111
+ # ---------------------------------------------------------------------------
112
+ # JavaScript/TypeScript sources and sinks
113
+ # ---------------------------------------------------------------------------
114
+
115
+ _JS_SOURCES: list[TaintSource] = [
116
+ TaintSource("req_param", re.compile(
117
+ r"""\b(?:req\.(?:body|params|query|headers|cookies)|"""
118
+ r"""request\.(?:body|params|query|headers))\b"""
119
+ ), "javascript"),
120
+ TaintSource("url_search_params", re.compile(
121
+ r"""\b(?:URLSearchParams|location\.search|location\.hash|window\.location)\b"""
122
+ ), "javascript"),
123
+ TaintSource("document_input", re.compile(
124
+ r"""\b(?:document\.getElementById|document\.querySelector|\.value)\b"""
125
+ ), "javascript"),
126
+ ]
127
+
128
+ _JS_SINKS: list[TaintSink] = [
129
+ TaintSink("sql_query", re.compile(
130
+ r"""\b(?:\.query|\.execute|\.run)\s*\("""
131
+ ), "CWE-89", "SQL injection: tainted data in database query", "javascript"),
132
+ TaintSink("exec_cmd", re.compile(
133
+ r"""\b(?:exec|execSync|spawn|spawnSync|execFile)\s*\("""
134
+ ), "CWE-78", "Command injection: tainted data in shell command", "javascript"),
135
+ TaintSink("inner_html", re.compile(
136
+ r"""\.innerHTML\s*="""
137
+ ), "CWE-79", "XSS: tainted data assigned to innerHTML", "javascript"),
138
+ TaintSink("redirect", re.compile(
139
+ r"""\b(?:res\.redirect|location\.href|window\.location)\s*="""
140
+ ), "CWE-601", "Open redirect: tainted data in redirect URL", "javascript"),
141
+ TaintSink("fs_access", re.compile(
142
+ r"""\b(?:fs\.(?:readFile|writeFile|readFileSync|writeFileSync|createReadStream|unlink)|"""
143
+ r"""path\.(?:join|resolve))\s*\("""
144
+ ), "CWE-22", "Path traversal: tainted data in file system path", "javascript"),
145
+ ]
146
+
147
+
148
+ # ---------------------------------------------------------------------------
149
+ # Taint analysis engine
150
+ # ---------------------------------------------------------------------------
151
+
152
+ _ASSIGNMENT_RE = re.compile(
153
+ r"""^\s*(?:(?:const|let|var)\s+)?(\w+)\s*=\s*(.+)$"""
154
+ )
155
+
156
+ _AUGMENTED_ASSIGN_RE = re.compile(
157
+ r"""^\s*(?:(?:const|let|var)\s+)?(\w+)\s*(?:\+=|\|=|\.append\(|\.extend\(|\.update\()(.+)"""
158
+ )
159
+
160
+ _FSTRING_VAR_RE = re.compile(r"""\{(\w+)""")
161
+ _FORMAT_VAR_RE = re.compile(r"""\.format\s*\([^)]*?(\w+)""")
162
+ _PERCENT_VAR_RE = re.compile(r"""%\s*(?:\((\w+)\)|(\w+))""")
163
+ _TEMPLATE_VAR_RE = re.compile(r"""\$\{(\w+)\}""")
164
+ _CONCAT_VAR_RE = re.compile(r"""\+\s*(\w+)""")
165
+
166
+
167
+ def _extract_variables_from_expr(expr: str) -> set[str]:
168
+ """Extract variable names referenced in an expression."""
169
+ variables: set[str] = set()
170
+
171
+ # f-string interpolation: f"...{var}..."
172
+ variables.update(_FSTRING_VAR_RE.findall(expr))
173
+
174
+ # .format() calls
175
+ variables.update(_FORMAT_VAR_RE.findall(expr))
176
+
177
+ # %-formatting
178
+ for groups in _PERCENT_VAR_RE.findall(expr):
179
+ variables.update(g for g in groups if g)
180
+
181
+ # Template literal interpolation: `...${var}...`
182
+ variables.update(_TEMPLATE_VAR_RE.findall(expr))
183
+
184
+ # String concatenation: "..." + var
185
+ variables.update(_CONCAT_VAR_RE.findall(expr))
186
+
187
+ # Direct variable reference (simple identifier on RHS)
188
+ tokens = re.findall(r'\b([a-zA-Z_]\w*)\b', expr)
189
+ variables.update(tokens)
190
+
191
+ # Filter out keywords and builtins
192
+ _KEYWORDS = frozenset({
193
+ "True", "False", "None", "true", "false", "null", "undefined",
194
+ "if", "else", "for", "while", "return", "import", "from",
195
+ "def", "class", "function", "const", "let", "var", "new",
196
+ "not", "and", "or", "in", "is", "as", "with", "try", "except",
197
+ "finally", "raise", "yield", "async", "await", "self", "cls",
198
+ "this", "super", "typeof", "instanceof",
199
+ })
200
+ return variables - _KEYWORDS
201
+
202
+
203
+ def analyze_function_taint(
204
+ lines: list[str],
205
+ sources: list[TaintSource],
206
+ sinks: list[TaintSink],
207
+ function_name: str,
208
+ start_line: int,
209
+ ) -> list[tuple[str, int, str, int, str, str, str]]:
210
+ """Analyze a single function body for source-to-sink taint flows.
211
+
212
+ Returns list of (tainted_var, source_line, source_desc, sink_line, sink_desc, cwe, message).
213
+ """
214
+ # Phase 1: Identify tainted variables (variables assigned from sources)
215
+ tainted: dict[str, tuple[int, str]] = {} # var_name → (line_no, source_desc)
216
+
217
+ for i, line in enumerate(lines):
218
+ line_no = start_line + i
219
+ stripped = line.strip()
220
+ if not stripped or stripped.startswith("#") or stripped.startswith("//"):
221
+ continue
222
+
223
+ # Check if line contains a source
224
+ for source in sources:
225
+ if source.pattern.search(line):
226
+ # Find the variable being assigned from this source
227
+ m = _ASSIGNMENT_RE.match(line)
228
+ if m:
229
+ var_name = m.group(1)
230
+ tainted[var_name] = (line_no, source.name)
231
+ else:
232
+ # Source used directly (e.g., as function parameter)
233
+ # Track any variable on the left side of any assignment-like pattern
234
+ am = _AUGMENTED_ASSIGN_RE.match(line)
235
+ if am:
236
+ tainted[am.group(1)] = (line_no, source.name)
237
+
238
+ if not tainted:
239
+ return []
240
+
241
+ # Phase 2: Propagate taint through assignments
242
+ # Simple forward propagation: if `b = f(a)` and `a` is tainted, `b` is tainted
243
+ changed = True
244
+ max_iterations = 10
245
+ iteration = 0
246
+ while changed and iteration < max_iterations:
247
+ changed = False
248
+ iteration += 1
249
+ for i, line in enumerate(lines):
250
+ m = _ASSIGNMENT_RE.match(line)
251
+ if not m:
252
+ continue
253
+ var_name = m.group(1)
254
+ if var_name in tainted:
255
+ continue # already tainted
256
+ rhs = m.group(2)
257
+ rhs_vars = _extract_variables_from_expr(rhs)
258
+ for rv in rhs_vars:
259
+ if rv in tainted:
260
+ tainted[var_name] = (start_line + i, f"propagated from {rv}")
261
+ changed = True
262
+ break
263
+
264
+ # Phase 3: Check if tainted variables reach sinks
265
+ findings: list[tuple[str, int, str, int, str, str, str]] = []
266
+
267
+ for i, line in enumerate(lines):
268
+ line_no = start_line + i
269
+ for sink in sinks:
270
+ if not sink.pattern.search(line):
271
+ continue
272
+ # Check if any tainted variable appears in this sink call
273
+ line_vars = _extract_variables_from_expr(line)
274
+ for var in line_vars:
275
+ if var in tainted:
276
+ source_line, source_desc = tainted[var]
277
+ findings.append((
278
+ var, source_line, source_desc,
279
+ line_no, sink.name, sink.cwe, sink.message,
280
+ ))
281
+ break # one finding per sink line
282
+
283
+ return findings
284
+
285
+
286
+ # ---------------------------------------------------------------------------
287
+ # File-level analysis
288
+ # ---------------------------------------------------------------------------
289
+
290
+ def _extract_function_bodies(content: str, language: str) -> list[tuple[str, int, int]]:
291
+ """Extract function name, start line, end line from source content.
292
+
293
+ Uses simple regex-based extraction (not tree-sitter) for portability.
294
+ Returns list of (function_name, start_line, end_line) 1-indexed.
295
+ """
296
+ functions: list[tuple[str, int, int]] = []
297
+ lines = content.splitlines()
298
+
299
+ if language == "python":
300
+ func_re = re.compile(r"""^(\s*)def\s+(\w+)\s*\(""")
301
+ in_func = False
302
+ func_name = ""
303
+ func_start = 0
304
+ func_indent = 0
305
+
306
+ for i, line in enumerate(lines, 1):
307
+ m = func_re.match(line)
308
+ if m:
309
+ if in_func:
310
+ functions.append((func_name, func_start, i - 1))
311
+ func_indent = len(m.group(1))
312
+ func_name = m.group(2)
313
+ func_start = i
314
+ in_func = True
315
+ elif in_func and line.strip() and not line.startswith(" " * (func_indent + 1)) and not line.strip().startswith("#"):
316
+ # Dedented line = end of function
317
+ if not line[0].isspace() or (len(line) - len(line.lstrip())) <= func_indent:
318
+ functions.append((func_name, func_start, i - 1))
319
+ in_func = False
320
+
321
+ if in_func:
322
+ functions.append((func_name, func_start, len(lines)))
323
+
324
+ elif language in ("javascript", "typescript"):
325
+ # Match: function name(...), const name = (...) =>, async function name(...)
326
+ func_re = re.compile(
327
+ r"""(?:(?:async\s+)?function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\([^)]*\)\s*=>)"""
328
+ )
329
+ # Simple brace-counting for JS function boundaries
330
+ for i, line in enumerate(lines, 1):
331
+ m = func_re.search(line)
332
+ if m:
333
+ func_name = m.group(1) or m.group(2)
334
+ # Find the end by counting braces
335
+ brace_count = 0
336
+ started = False
337
+ end_line = i
338
+ for j in range(i - 1, len(lines)):
339
+ for ch in lines[j]:
340
+ if ch == "{":
341
+ brace_count += 1
342
+ started = True
343
+ elif ch == "}":
344
+ brace_count -= 1
345
+ if started and brace_count <= 0:
346
+ end_line = j + 1
347
+ break
348
+ else:
349
+ end_line = len(lines)
350
+ functions.append((func_name, i, end_line))
351
+
352
+ return functions
353
+
354
+
355
+ def analyze_file(
356
+ file_path: str,
357
+ language: str = "",
358
+ ) -> list[DataFlowFinding]:
359
+ """Analyze a single file for data flow vulnerabilities.
360
+
361
+ Args:
362
+ file_path: Absolute path to the source file.
363
+ language: Language hint (auto-detected from extension if empty).
364
+
365
+ Returns:
366
+ List of DataFlowFinding instances.
367
+ """
368
+ if not language:
369
+ ext = os.path.splitext(file_path)[1].lower()
370
+ lang_map = {
371
+ ".py": "python", ".pyi": "python",
372
+ ".js": "javascript", ".jsx": "javascript",
373
+ ".ts": "typescript", ".tsx": "typescript",
374
+ ".mjs": "javascript", ".cjs": "javascript",
375
+ }
376
+ language = lang_map.get(ext, "")
377
+
378
+ if language not in ("python", "javascript", "typescript"):
379
+ return []
380
+
381
+ try:
382
+ content = Path(file_path).read_text(encoding="utf-8", errors="replace")
383
+ except OSError:
384
+ return []
385
+
386
+ # Select sources and sinks for the language
387
+ if language == "python":
388
+ sources = _PYTHON_SOURCES
389
+ sinks = _PYTHON_SINKS
390
+ else:
391
+ sources = _JS_SOURCES
392
+ sinks = _JS_SINKS
393
+
394
+ # Extract functions and analyze each
395
+ functions = _extract_function_bodies(content, language)
396
+ lines = content.splitlines()
397
+ findings: list[DataFlowFinding] = []
398
+
399
+ for func_name, start, end in functions:
400
+ func_lines = lines[start - 1:end]
401
+ raw_findings = analyze_function_taint(
402
+ func_lines, sources, sinks, func_name, start,
403
+ )
404
+ for tvar, src_line, src_desc, snk_line, snk_desc, cwe, msg in raw_findings:
405
+ findings.append(DataFlowFinding(
406
+ file_path=file_path,
407
+ function_name=func_name,
408
+ source_line=src_line,
409
+ source_desc=src_desc,
410
+ sink_line=snk_line,
411
+ sink_desc=snk_desc,
412
+ tainted_var=tvar,
413
+ cwe=cwe,
414
+ message=msg,
415
+ ))
416
+
417
+ return findings
418
+
419
+
420
+ # ---------------------------------------------------------------------------
421
+ # Project-level analysis
422
+ # ---------------------------------------------------------------------------
423
+
424
+ def analyze_project(
425
+ project_dir: str,
426
+ paths: list[str] | None = None,
427
+ ) -> DataFlowReport:
428
+ """Run data flow analysis across a project or specific files.
429
+
430
+ Args:
431
+ project_dir: Project root directory.
432
+ paths: Specific file paths to analyze (relative to project root).
433
+ If None, scans all Python and JavaScript files.
434
+ """
435
+ import time
436
+
437
+ start = time.monotonic()
438
+ findings: list[DataFlowFinding] = []
439
+ files_analyzed = 0
440
+ functions_analyzed = 0
441
+
442
+ _SKIP_DIRS = frozenset({
443
+ ".git", "node_modules", "__pycache__", ".venv", "venv",
444
+ ".tox", "dist", "build", ".next", ".nuxt",
445
+ })
446
+ _SCAN_EXTS = frozenset({".py", ".js", ".ts", ".jsx", ".tsx", ".mjs", ".cjs"})
447
+
448
+ if paths:
449
+ file_list = [os.path.join(project_dir, p) for p in paths]
450
+ else:
451
+ file_list = []
452
+ for dirpath, dirnames, filenames in os.walk(project_dir):
453
+ dirnames[:] = [d for d in dirnames if d not in _SKIP_DIRS]
454
+ for fname in filenames:
455
+ ext = os.path.splitext(fname)[1].lower()
456
+ if ext in _SCAN_EXTS:
457
+ file_list.append(os.path.join(dirpath, fname))
458
+
459
+ for abs_path in file_list:
460
+ if not os.path.isfile(abs_path):
461
+ continue
462
+ file_findings = analyze_file(abs_path)
463
+ # Fix relative paths in findings
464
+ for f in file_findings:
465
+ try:
466
+ f.file_path = os.path.relpath(abs_path, project_dir)
467
+ except ValueError:
468
+ f.file_path = abs_path
469
+ findings.extend(file_findings)
470
+ files_analyzed += 1
471
+
472
+ elapsed = (time.monotonic() - start) * 1000
473
+
474
+ return DataFlowReport(
475
+ findings=findings,
476
+ functions_analyzed=functions_analyzed,
477
+ files_analyzed=files_analyzed,
478
+ scan_time_ms=round(elapsed, 1),
479
+ )
480
+
481
+
482
+ def format_report(report: DataFlowReport) -> str:
483
+ """Format a DataFlowReport as human-readable text."""
484
+ lines: list[str] = []
485
+
486
+ lines.append("Data Flow Analysis Report")
487
+ lines.append(f"Files: {report.files_analyzed} | "
488
+ f"Findings: {len(report.findings)} | "
489
+ f"Time: {report.scan_time_ms:.0f}ms")
490
+ lines.append("")
491
+
492
+ if not report.findings:
493
+ lines.append("No data flow vulnerabilities detected.")
494
+ return "\n".join(lines)
495
+
496
+ # Group by CWE
497
+ by_cwe: dict[str, list[DataFlowFinding]] = {}
498
+ for f in report.findings:
499
+ by_cwe.setdefault(f.cwe, []).append(f)
500
+
501
+ cwe_labels = {
502
+ "CWE-89": "SQL Injection",
503
+ "CWE-78": "Command Injection",
504
+ "CWE-79": "Cross-Site Scripting (XSS)",
505
+ "CWE-22": "Path Traversal",
506
+ "CWE-918": "Server-Side Request Forgery (SSRF)",
507
+ "CWE-601": "Open Redirect",
508
+ }
509
+
510
+ for cwe, cwe_findings in sorted(by_cwe.items()):
511
+ label = cwe_labels.get(cwe, cwe)
512
+ lines.append(f"## {label} [{cwe}] ({len(cwe_findings)} finding(s))")
513
+ for f in cwe_findings[:10]:
514
+ lines.append(f" {f.file_path}:{f.sink_line} in {f.function_name}()")
515
+ lines.append(f" Tainted variable '{f.tainted_var}' flows from "
516
+ f"line {f.source_line} ({f.source_desc}) to sink ({f.sink_desc})")
517
+ lines.append(f" {f.message}")
518
+ if len(cwe_findings) > 10:
519
+ lines.append(f" ... and {len(cwe_findings) - 10} more")
520
+ lines.append("")
521
+
522
+ return "\n".join(lines)