@neurcode-ai/cli 0.9.64 → 0.9.66

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 (323) hide show
  1. package/LICENSE +201 -0
  2. package/dist/commands/bootstrap-policy.d.ts +29 -0
  3. package/dist/commands/bootstrap-policy.d.ts.map +1 -0
  4. package/dist/commands/bootstrap-policy.js +334 -0
  5. package/dist/commands/bootstrap-policy.js.map +1 -0
  6. package/dist/commands/brain.d.ts.map +1 -1
  7. package/dist/commands/brain.js +273 -0
  8. package/dist/commands/brain.js.map +1 -1
  9. package/dist/commands/doctor.d.ts.map +1 -1
  10. package/dist/commands/doctor.js +82 -0
  11. package/dist/commands/doctor.js.map +1 -1
  12. package/dist/commands/pilot-report.d.ts +9 -0
  13. package/dist/commands/pilot-report.d.ts.map +1 -0
  14. package/dist/commands/pilot-report.js +176 -0
  15. package/dist/commands/pilot-report.js.map +1 -0
  16. package/dist/commands/quickstart.d.ts +21 -0
  17. package/dist/commands/quickstart.d.ts.map +1 -0
  18. package/dist/commands/quickstart.js +178 -0
  19. package/dist/commands/quickstart.js.map +1 -0
  20. package/dist/commands/remediate-export.d.ts +31 -0
  21. package/dist/commands/remediate-export.d.ts.map +1 -0
  22. package/dist/commands/remediate-export.js +283 -0
  23. package/dist/commands/remediate-export.js.map +1 -0
  24. package/dist/commands/remediate-governance.d.ts +54 -0
  25. package/dist/commands/remediate-governance.d.ts.map +1 -0
  26. package/dist/commands/remediate-governance.js +375 -0
  27. package/dist/commands/remediate-governance.js.map +1 -0
  28. package/dist/commands/remediate.d.ts.map +1 -1
  29. package/dist/commands/remediate.js.map +1 -1
  30. package/dist/commands/replay.d.ts.map +1 -1
  31. package/dist/commands/replay.js +30 -0
  32. package/dist/commands/replay.js.map +1 -1
  33. package/dist/commands/verify.d.ts.map +1 -1
  34. package/dist/commands/verify.js +409 -30
  35. package/dist/commands/verify.js.map +1 -1
  36. package/dist/daemon/server.d.ts.map +1 -1
  37. package/dist/daemon/server.js +1078 -0
  38. package/dist/daemon/server.js.map +1 -1
  39. package/dist/explainability/DeterminismClassifier.d.ts +34 -0
  40. package/dist/explainability/DeterminismClassifier.d.ts.map +1 -0
  41. package/dist/explainability/DeterminismClassifier.js +104 -0
  42. package/dist/explainability/DeterminismClassifier.js.map +1 -0
  43. package/dist/explainability/ViolationFormatter.d.ts +32 -0
  44. package/dist/explainability/ViolationFormatter.d.ts.map +1 -0
  45. package/dist/explainability/ViolationFormatter.js +252 -0
  46. package/dist/explainability/ViolationFormatter.js.map +1 -0
  47. package/dist/explainability/index.d.ts +15 -0
  48. package/dist/explainability/index.d.ts.map +1 -0
  49. package/dist/explainability/index.js +94 -0
  50. package/dist/explainability/index.js.map +1 -0
  51. package/dist/explainability/types.d.ts +37 -0
  52. package/dist/explainability/types.d.ts.map +1 -0
  53. package/dist/explainability/types.js +3 -0
  54. package/dist/explainability/types.js.map +1 -0
  55. package/dist/governance/canonical-invariants.d.ts +88 -0
  56. package/dist/governance/canonical-invariants.d.ts.map +1 -0
  57. package/dist/governance/canonical-invariants.js +197 -0
  58. package/dist/governance/canonical-invariants.js.map +1 -0
  59. package/dist/governance/canonical-ordering.d.ts +76 -0
  60. package/dist/governance/canonical-ordering.d.ts.map +1 -0
  61. package/dist/governance/canonical-ordering.js +189 -0
  62. package/dist/governance/canonical-ordering.js.map +1 -0
  63. package/dist/governance/canonical-pipeline.d.ts +45 -0
  64. package/dist/governance/canonical-pipeline.d.ts.map +1 -0
  65. package/dist/governance/canonical-pipeline.js +616 -0
  66. package/dist/governance/canonical-pipeline.js.map +1 -0
  67. package/dist/governance/diff-line-provenance.d.ts +59 -0
  68. package/dist/governance/diff-line-provenance.d.ts.map +1 -0
  69. package/dist/governance/diff-line-provenance.js +118 -0
  70. package/dist/governance/diff-line-provenance.js.map +1 -0
  71. package/dist/governance/pilot-readiness.d.ts +34 -0
  72. package/dist/governance/pilot-readiness.d.ts.map +1 -0
  73. package/dist/governance/pilot-readiness.js +226 -0
  74. package/dist/governance/pilot-readiness.js.map +1 -0
  75. package/dist/governance/policy-parity-validator.d.ts +62 -0
  76. package/dist/governance/policy-parity-validator.d.ts.map +1 -0
  77. package/dist/governance/policy-parity-validator.js +137 -0
  78. package/dist/governance/policy-parity-validator.js.map +1 -0
  79. package/dist/governance/remediation-boundary.d.ts +55 -0
  80. package/dist/governance/remediation-boundary.d.ts.map +1 -0
  81. package/dist/governance/remediation-boundary.js +120 -0
  82. package/dist/governance/remediation-boundary.js.map +1 -0
  83. package/dist/governance/structural-cache.d.ts +103 -0
  84. package/dist/governance/structural-cache.d.ts.map +1 -0
  85. package/dist/governance/structural-cache.js +240 -0
  86. package/dist/governance/structural-cache.js.map +1 -0
  87. package/dist/governance/structural-on-diff.d.ts +33 -0
  88. package/dist/governance/structural-on-diff.d.ts.map +1 -0
  89. package/dist/governance/structural-on-diff.js +67 -0
  90. package/dist/governance/structural-on-diff.js.map +1 -0
  91. package/dist/governance/structural-policy-merge.d.ts +22 -0
  92. package/dist/governance/structural-policy-merge.d.ts.map +1 -0
  93. package/dist/governance/structural-policy-merge.js +32 -0
  94. package/dist/governance/structural-policy-merge.js.map +1 -0
  95. package/dist/governance/verify-runtime-guard.d.ts +99 -0
  96. package/dist/governance/verify-runtime-guard.d.ts.map +1 -0
  97. package/dist/governance/verify-runtime-guard.js +129 -0
  98. package/dist/governance/verify-runtime-guard.js.map +1 -0
  99. package/dist/index.js +107 -0
  100. package/dist/index.js.map +1 -1
  101. package/dist/integrations/review-compression/index.d.ts +50 -0
  102. package/dist/integrations/review-compression/index.d.ts.map +1 -0
  103. package/dist/integrations/review-compression/index.js +158 -0
  104. package/dist/integrations/review-compression/index.js.map +1 -0
  105. package/dist/intent-engine/domain-taxonomy.d.ts +42 -0
  106. package/dist/intent-engine/domain-taxonomy.d.ts.map +1 -0
  107. package/dist/intent-engine/domain-taxonomy.js +534 -0
  108. package/dist/intent-engine/domain-taxonomy.js.map +1 -0
  109. package/dist/intent-engine/index.d.ts +1 -0
  110. package/dist/intent-engine/index.d.ts.map +1 -1
  111. package/dist/intent-engine/index.js +6 -1
  112. package/dist/intent-engine/index.js.map +1 -1
  113. package/dist/intent-engine/parser.d.ts.map +1 -1
  114. package/dist/intent-engine/parser.js +47 -0
  115. package/dist/intent-engine/parser.js.map +1 -1
  116. package/dist/intent-engine/repo-classifier.d.ts +64 -0
  117. package/dist/intent-engine/repo-classifier.d.ts.map +1 -0
  118. package/dist/intent-engine/repo-classifier.js +178 -0
  119. package/dist/intent-engine/repo-classifier.js.map +1 -0
  120. package/dist/intent-engine/semantic-expander.d.ts +104 -0
  121. package/dist/intent-engine/semantic-expander.d.ts.map +1 -0
  122. package/dist/intent-engine/semantic-expander.js +480 -0
  123. package/dist/intent-engine/semantic-expander.js.map +1 -0
  124. package/dist/patch-engine/patterns.d.ts.map +1 -1
  125. package/dist/patch-engine/patterns.js +8 -4
  126. package/dist/patch-engine/patterns.js.map +1 -1
  127. package/dist/semantic/index.d.ts +14 -0
  128. package/dist/semantic/index.d.ts.map +1 -0
  129. package/dist/semantic/index.js +30 -0
  130. package/dist/semantic/index.js.map +1 -0
  131. package/dist/semantic/tfidf-engine.d.ts +81 -0
  132. package/dist/semantic/tfidf-engine.d.ts.map +1 -0
  133. package/dist/semantic/tfidf-engine.js +278 -0
  134. package/dist/semantic/tfidf-engine.js.map +1 -0
  135. package/dist/semantic/vector-store.d.ts +108 -0
  136. package/dist/semantic/vector-store.d.ts.map +1 -0
  137. package/dist/semantic/vector-store.js +321 -0
  138. package/dist/semantic/vector-store.js.map +1 -0
  139. package/dist/structural-rules/context-severity.d.ts +46 -0
  140. package/dist/structural-rules/context-severity.d.ts.map +1 -0
  141. package/dist/structural-rules/context-severity.js +115 -0
  142. package/dist/structural-rules/context-severity.js.map +1 -0
  143. package/dist/structural-rules/distributed/DS001-saga-rollback-absence.d.ts +11 -0
  144. package/dist/structural-rules/distributed/DS001-saga-rollback-absence.d.ts.map +1 -0
  145. package/dist/structural-rules/distributed/DS001-saga-rollback-absence.js +212 -0
  146. package/dist/structural-rules/distributed/DS001-saga-rollback-absence.js.map +1 -0
  147. package/dist/structural-rules/distributed/DS002-missing-correlation-id.d.ts +11 -0
  148. package/dist/structural-rules/distributed/DS002-missing-correlation-id.d.ts.map +1 -0
  149. package/dist/structural-rules/distributed/DS002-missing-correlation-id.js +213 -0
  150. package/dist/structural-rules/distributed/DS002-missing-correlation-id.js.map +1 -0
  151. package/dist/structural-rules/distributed/index.d.ts +3 -0
  152. package/dist/structural-rules/distributed/index.d.ts.map +1 -0
  153. package/dist/structural-rules/distributed/index.js +8 -0
  154. package/dist/structural-rules/distributed/index.js.map +1 -0
  155. package/dist/structural-rules/engine.d.ts +25 -0
  156. package/dist/structural-rules/engine.d.ts.map +1 -0
  157. package/dist/structural-rules/engine.js +90 -0
  158. package/dist/structural-rules/engine.js.map +1 -0
  159. package/dist/structural-rules/index.d.ts +45 -0
  160. package/dist/structural-rules/index.d.ts.map +1 -0
  161. package/dist/structural-rules/index.js +158 -0
  162. package/dist/structural-rules/index.js.map +1 -0
  163. package/dist/structural-rules/python/PY001-asyncio-task-without-cancel.d.ts +11 -0
  164. package/dist/structural-rules/python/PY001-asyncio-task-without-cancel.d.ts.map +1 -0
  165. package/dist/structural-rules/python/PY001-asyncio-task-without-cancel.js +66 -0
  166. package/dist/structural-rules/python/PY001-asyncio-task-without-cancel.js.map +1 -0
  167. package/dist/structural-rules/python/PY002-unbounded-dict-singleton.d.ts +11 -0
  168. package/dist/structural-rules/python/PY002-unbounded-dict-singleton.d.ts.map +1 -0
  169. package/dist/structural-rules/python/PY002-unbounded-dict-singleton.js +135 -0
  170. package/dist/structural-rules/python/PY002-unbounded-dict-singleton.js.map +1 -0
  171. package/dist/structural-rules/python/PY003-broad-except-clause.d.ts +32 -0
  172. package/dist/structural-rules/python/PY003-broad-except-clause.d.ts.map +1 -0
  173. package/dist/structural-rules/python/PY003-broad-except-clause.js +277 -0
  174. package/dist/structural-rules/python/PY003-broad-except-clause.js.map +1 -0
  175. package/dist/structural-rules/python/PY004-swallowed-async-exception.d.ts +11 -0
  176. package/dist/structural-rules/python/PY004-swallowed-async-exception.d.ts.map +1 -0
  177. package/dist/structural-rules/python/PY004-swallowed-async-exception.js +167 -0
  178. package/dist/structural-rules/python/PY004-swallowed-async-exception.js.map +1 -0
  179. package/dist/structural-rules/python/PY005-fastapi-without-pydantic.d.ts +11 -0
  180. package/dist/structural-rules/python/PY005-fastapi-without-pydantic.d.ts.map +1 -0
  181. package/dist/structural-rules/python/PY005-fastapi-without-pydantic.js +154 -0
  182. package/dist/structural-rules/python/PY005-fastapi-without-pydantic.js.map +1 -0
  183. package/dist/structural-rules/python/PY006-blocking-io-in-async.d.ts +11 -0
  184. package/dist/structural-rules/python/PY006-blocking-io-in-async.d.ts.map +1 -0
  185. package/dist/structural-rules/python/PY006-blocking-io-in-async.js +130 -0
  186. package/dist/structural-rules/python/PY006-blocking-io-in-async.js.map +1 -0
  187. package/dist/structural-rules/python/PY007-sqlalchemy-session-leak.d.ts +11 -0
  188. package/dist/structural-rules/python/PY007-sqlalchemy-session-leak.d.ts.map +1 -0
  189. package/dist/structural-rules/python/PY007-sqlalchemy-session-leak.js +93 -0
  190. package/dist/structural-rules/python/PY007-sqlalchemy-session-leak.js.map +1 -0
  191. package/dist/structural-rules/python/PY008-celery-task-without-retry.d.ts +11 -0
  192. package/dist/structural-rules/python/PY008-celery-task-without-retry.d.ts.map +1 -0
  193. package/dist/structural-rules/python/PY008-celery-task-without-retry.js +154 -0
  194. package/dist/structural-rules/python/PY008-celery-task-without-retry.js.map +1 -0
  195. package/dist/structural-rules/python/PY009-unsafe-pickle-deserialization.d.ts +11 -0
  196. package/dist/structural-rules/python/PY009-unsafe-pickle-deserialization.d.ts.map +1 -0
  197. package/dist/structural-rules/python/PY009-unsafe-pickle-deserialization.js +133 -0
  198. package/dist/structural-rules/python/PY009-unsafe-pickle-deserialization.js.map +1 -0
  199. package/dist/structural-rules/python/PY010-leaked-aiohttp-session.d.ts +11 -0
  200. package/dist/structural-rules/python/PY010-leaked-aiohttp-session.d.ts.map +1 -0
  201. package/dist/structural-rules/python/PY010-leaked-aiohttp-session.js +80 -0
  202. package/dist/structural-rules/python/PY010-leaked-aiohttp-session.js.map +1 -0
  203. package/dist/structural-rules/python/PY011-thread-lifecycle.d.ts +11 -0
  204. package/dist/structural-rules/python/PY011-thread-lifecycle.d.ts.map +1 -0
  205. package/dist/structural-rules/python/PY011-thread-lifecycle.js +97 -0
  206. package/dist/structural-rules/python/PY011-thread-lifecycle.js.map +1 -0
  207. package/dist/structural-rules/python/PY012-asyncio-run-misuse.d.ts +11 -0
  208. package/dist/structural-rules/python/PY012-asyncio-run-misuse.d.ts.map +1 -0
  209. package/dist/structural-rules/python/PY012-asyncio-run-misuse.js +83 -0
  210. package/dist/structural-rules/python/PY012-asyncio-run-misuse.js.map +1 -0
  211. package/dist/structural-rules/python/PY013-mutable-default-arg.d.ts +11 -0
  212. package/dist/structural-rules/python/PY013-mutable-default-arg.d.ts.map +1 -0
  213. package/dist/structural-rules/python/PY013-mutable-default-arg.js +73 -0
  214. package/dist/structural-rules/python/PY013-mutable-default-arg.js.map +1 -0
  215. package/dist/structural-rules/python/PY014-fixed-sleep-retry.d.ts +11 -0
  216. package/dist/structural-rules/python/PY014-fixed-sleep-retry.d.ts.map +1 -0
  217. package/dist/structural-rules/python/PY014-fixed-sleep-retry.js +115 -0
  218. package/dist/structural-rules/python/PY014-fixed-sleep-retry.js.map +1 -0
  219. package/dist/structural-rules/rules/SR001-swallowed-async-rejection.d.ts +11 -0
  220. package/dist/structural-rules/rules/SR001-swallowed-async-rejection.d.ts.map +1 -0
  221. package/dist/structural-rules/rules/SR001-swallowed-async-rejection.js +145 -0
  222. package/dist/structural-rules/rules/SR001-swallowed-async-rejection.js.map +1 -0
  223. package/dist/structural-rules/rules/SR002-unbounded-collection.d.ts +11 -0
  224. package/dist/structural-rules/rules/SR002-unbounded-collection.d.ts.map +1 -0
  225. package/dist/structural-rules/rules/SR002-unbounded-collection.js +196 -0
  226. package/dist/structural-rules/rules/SR002-unbounded-collection.js.map +1 -0
  227. package/dist/structural-rules/rules/SR003-timer-without-cleanup.d.ts +11 -0
  228. package/dist/structural-rules/rules/SR003-timer-without-cleanup.d.ts.map +1 -0
  229. package/dist/structural-rules/rules/SR003-timer-without-cleanup.js +148 -0
  230. package/dist/structural-rules/rules/SR003-timer-without-cleanup.js.map +1 -0
  231. package/dist/structural-rules/rules/SR004-request-boundary-no-validation.d.ts +11 -0
  232. package/dist/structural-rules/rules/SR004-request-boundary-no-validation.d.ts.map +1 -0
  233. package/dist/structural-rules/rules/SR004-request-boundary-no-validation.js +162 -0
  234. package/dist/structural-rules/rules/SR004-request-boundary-no-validation.js.map +1 -0
  235. package/dist/structural-rules/rules/SR005-halfopen-probe-gate.d.ts +11 -0
  236. package/dist/structural-rules/rules/SR005-halfopen-probe-gate.d.ts.map +1 -0
  237. package/dist/structural-rules/rules/SR005-halfopen-probe-gate.js +150 -0
  238. package/dist/structural-rules/rules/SR005-halfopen-probe-gate.js.map +1 -0
  239. package/dist/structural-rules/rules/SR006-fanout-error-sanitization.d.ts +11 -0
  240. package/dist/structural-rules/rules/SR006-fanout-error-sanitization.d.ts.map +1 -0
  241. package/dist/structural-rules/rules/SR006-fanout-error-sanitization.js +161 -0
  242. package/dist/structural-rules/rules/SR006-fanout-error-sanitization.js.map +1 -0
  243. package/dist/structural-rules/rules/SR007-cross-request-error.d.ts +11 -0
  244. package/dist/structural-rules/rules/SR007-cross-request-error.d.ts.map +1 -0
  245. package/dist/structural-rules/rules/SR007-cross-request-error.js +175 -0
  246. package/dist/structural-rules/rules/SR007-cross-request-error.js.map +1 -0
  247. package/dist/structural-rules/rules/SR008-background-task-orphan.d.ts +11 -0
  248. package/dist/structural-rules/rules/SR008-background-task-orphan.d.ts.map +1 -0
  249. package/dist/structural-rules/rules/SR008-background-task-orphan.js +176 -0
  250. package/dist/structural-rules/rules/SR008-background-task-orphan.js.map +1 -0
  251. package/dist/structural-rules/rules/SR009-missing-retry-backoff.d.ts +11 -0
  252. package/dist/structural-rules/rules/SR009-missing-retry-backoff.d.ts.map +1 -0
  253. package/dist/structural-rules/rules/SR009-missing-retry-backoff.js +168 -0
  254. package/dist/structural-rules/rules/SR009-missing-retry-backoff.js.map +1 -0
  255. package/dist/structural-rules/rules/SR010-retry-storm.d.ts +11 -0
  256. package/dist/structural-rules/rules/SR010-retry-storm.d.ts.map +1 -0
  257. package/dist/structural-rules/rules/SR010-retry-storm.js +181 -0
  258. package/dist/structural-rules/rules/SR010-retry-storm.js.map +1 -0
  259. package/dist/structural-rules/rules/SR011-event-listener-leak.d.ts +11 -0
  260. package/dist/structural-rules/rules/SR011-event-listener-leak.d.ts.map +1 -0
  261. package/dist/structural-rules/rules/SR011-event-listener-leak.js +208 -0
  262. package/dist/structural-rules/rules/SR011-event-listener-leak.js.map +1 -0
  263. package/dist/structural-rules/rules/SR012-promise-race-leak.d.ts +11 -0
  264. package/dist/structural-rules/rules/SR012-promise-race-leak.d.ts.map +1 -0
  265. package/dist/structural-rules/rules/SR012-promise-race-leak.js +191 -0
  266. package/dist/structural-rules/rules/SR012-promise-race-leak.js.map +1 -0
  267. package/dist/structural-rules/rules/SR013-missing-idempotency-key.d.ts +11 -0
  268. package/dist/structural-rules/rules/SR013-missing-idempotency-key.d.ts.map +1 -0
  269. package/dist/structural-rules/rules/SR013-missing-idempotency-key.js +219 -0
  270. package/dist/structural-rules/rules/SR013-missing-idempotency-key.js.map +1 -0
  271. package/dist/structural-rules/rules/SR014-mutable-closure-async.d.ts +11 -0
  272. package/dist/structural-rules/rules/SR014-mutable-closure-async.d.ts.map +1 -0
  273. package/dist/structural-rules/rules/SR014-mutable-closure-async.js +208 -0
  274. package/dist/structural-rules/rules/SR014-mutable-closure-async.js.map +1 -0
  275. package/dist/structural-rules/rules/SR015-dangling-abort-controller.d.ts +11 -0
  276. package/dist/structural-rules/rules/SR015-dangling-abort-controller.d.ts.map +1 -0
  277. package/dist/structural-rules/rules/SR015-dangling-abort-controller.js +190 -0
  278. package/dist/structural-rules/rules/SR015-dangling-abort-controller.js.map +1 -0
  279. package/dist/structural-rules/rules/SR016-unsafe-json-parse.d.ts +11 -0
  280. package/dist/structural-rules/rules/SR016-unsafe-json-parse.d.ts.map +1 -0
  281. package/dist/structural-rules/rules/SR016-unsafe-json-parse.js +187 -0
  282. package/dist/structural-rules/rules/SR016-unsafe-json-parse.js.map +1 -0
  283. package/dist/structural-rules/suppressions.d.ts +43 -0
  284. package/dist/structural-rules/suppressions.d.ts.map +1 -0
  285. package/dist/structural-rules/suppressions.js +115 -0
  286. package/dist/structural-rules/suppressions.js.map +1 -0
  287. package/dist/structural-rules/types.d.ts +55 -0
  288. package/dist/structural-rules/types.d.ts.map +1 -0
  289. package/dist/structural-rules/types.js +3 -0
  290. package/dist/structural-rules/types.js.map +1 -0
  291. package/dist/utils/brain-cache.d.ts +100 -0
  292. package/dist/utils/brain-cache.d.ts.map +1 -0
  293. package/dist/utils/brain-cache.js +346 -0
  294. package/dist/utils/brain-cache.js.map +1 -0
  295. package/dist/utils/governance-provenance.d.ts +95 -0
  296. package/dist/utils/governance-provenance.d.ts.map +1 -0
  297. package/dist/utils/governance-provenance.js +187 -0
  298. package/dist/utils/governance-provenance.js.map +1 -0
  299. package/dist/utils/pilot-metrics.d.ts +46 -0
  300. package/dist/utils/pilot-metrics.d.ts.map +1 -0
  301. package/dist/utils/pilot-metrics.js +240 -0
  302. package/dist/utils/pilot-metrics.js.map +1 -0
  303. package/dist/utils/replay-runtime.d.ts +34 -0
  304. package/dist/utils/replay-runtime.d.ts.map +1 -1
  305. package/dist/utils/replay-runtime.js +207 -0
  306. package/dist/utils/replay-runtime.js.map +1 -1
  307. package/dist/utils/verify-runtime-stability.d.ts +142 -0
  308. package/dist/utils/verify-runtime-stability.d.ts.map +1 -0
  309. package/dist/utils/verify-runtime-stability.js +230 -0
  310. package/dist/utils/verify-runtime-stability.js.map +1 -0
  311. package/dist/workspace/cross-repo-graph.d.ts +111 -0
  312. package/dist/workspace/cross-repo-graph.d.ts.map +1 -0
  313. package/dist/workspace/cross-repo-graph.js +450 -0
  314. package/dist/workspace/cross-repo-graph.js.map +1 -0
  315. package/dist/workspace/federated-context.d.ts +144 -0
  316. package/dist/workspace/federated-context.d.ts.map +1 -0
  317. package/dist/workspace/federated-context.js +347 -0
  318. package/dist/workspace/federated-context.js.map +1 -0
  319. package/dist/workspace/index.d.ts +38 -0
  320. package/dist/workspace/index.d.ts.map +1 -0
  321. package/dist/workspace/index.js +48 -0
  322. package/dist/workspace/index.js.map +1 -0
  323. package/package.json +9 -9
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PY003BroadExceptClause = void 0;
4
+ exports.stripCommentsAndStrings = stripCommentsAndStrings;
5
+ exports.classifyExceptionFlow = classifyExceptionFlow;
6
+ // Matches: except Exception: or except Exception as e:
7
+ const BROAD_EXCEPT_RE = /^(\s*)except\s+Exception(\s+as\s+\w+)?\s*:/;
8
+ // Logging/reporting call patterns — only checked AFTER stripping comments
9
+ const LOGGING_RE = /\b(?:log|logger|logging|error|warn|warning|report|track|capture|sentry|bugsnag|rollbar|print)\s*[\.(]/i;
10
+ /**
11
+ * Strip Python comment lines and string literal regions from source lines.
12
+ *
13
+ * Algorithm (deterministic state machine):
14
+ * - Track whether we are inside a triple-quoted string (""" or ''')
15
+ * - Track whether we are inside a single-quoted string (" or ')
16
+ * - If a line starts with # (after stripping indent) → replace with empty string
17
+ * - Content inside string regions is neutralized (replaced with spaces of same length)
18
+ *
19
+ * This is NOT a full Python tokenizer — it handles the common cases that enable
20
+ * bypass of governance checks while remaining O(n) and dependency-free.
21
+ */
22
+ function stripCommentsAndStrings(lines) {
23
+ const result = [];
24
+ let inTripleDouble = false; // inside """..."""
25
+ let inTripleSingle = false; // inside '''...'''
26
+ for (const line of lines) {
27
+ const trimmed = line.trimStart();
28
+ // If we are inside a triple-quoted block, look for the closing delimiter
29
+ if (inTripleDouble) {
30
+ const closeIdx = line.indexOf('"""');
31
+ if (closeIdx !== -1) {
32
+ inTripleDouble = false;
33
+ // Neutralize up to and including the closing delimiter
34
+ result.push(' '.repeat(line.length));
35
+ }
36
+ else {
37
+ result.push(' '.repeat(line.length));
38
+ }
39
+ continue;
40
+ }
41
+ if (inTripleSingle) {
42
+ const closeIdx = line.indexOf("'''");
43
+ if (closeIdx !== -1) {
44
+ inTripleSingle = false;
45
+ result.push(' '.repeat(line.length));
46
+ }
47
+ else {
48
+ result.push(' '.repeat(line.length));
49
+ }
50
+ continue;
51
+ }
52
+ // Full-line comment — blank it entirely
53
+ if (trimmed.startsWith('#')) {
54
+ result.push('');
55
+ continue;
56
+ }
57
+ // Scan for string/comment delimiters character by character
58
+ let out = '';
59
+ let i = 0;
60
+ let inSingleQ = false;
61
+ let inDoubleQ = false;
62
+ while (i < line.length) {
63
+ const ch = line[i];
64
+ const remaining = line.slice(i);
65
+ if (!inSingleQ && !inDoubleQ) {
66
+ // Check for triple-quote opening
67
+ if (remaining.startsWith('"""')) {
68
+ const rest = line.slice(i + 3);
69
+ const closeInSameLine = rest.indexOf('"""');
70
+ if (closeInSameLine !== -1) {
71
+ // Triple-quote opens and closes on same line — neutralize it
72
+ out += ' '.repeat(3 + closeInSameLine + 3);
73
+ i += 3 + closeInSameLine + 3;
74
+ continue;
75
+ }
76
+ else {
77
+ inTripleDouble = true;
78
+ // Neutralize rest of line
79
+ out += ' '.repeat(line.length - i);
80
+ break;
81
+ }
82
+ }
83
+ if (remaining.startsWith("'''")) {
84
+ const rest = line.slice(i + 3);
85
+ const closeInSameLine = rest.indexOf("'''");
86
+ if (closeInSameLine !== -1) {
87
+ out += ' '.repeat(3 + closeInSameLine + 3);
88
+ i += 3 + closeInSameLine + 3;
89
+ continue;
90
+ }
91
+ else {
92
+ inTripleSingle = true;
93
+ out += ' '.repeat(line.length - i);
94
+ break;
95
+ }
96
+ }
97
+ // Start of single-line string
98
+ if (ch === '"') {
99
+ inDoubleQ = true;
100
+ out += ' ';
101
+ i++;
102
+ continue;
103
+ }
104
+ if (ch === "'") {
105
+ inSingleQ = true;
106
+ out += ' ';
107
+ i++;
108
+ continue;
109
+ }
110
+ // Inline comment
111
+ if (ch === '#') {
112
+ // Rest of line is comment — stop
113
+ break;
114
+ }
115
+ out += ch;
116
+ }
117
+ else if (inDoubleQ) {
118
+ if (ch === '\\') {
119
+ out += ' ';
120
+ i += 2;
121
+ continue;
122
+ } // escape
123
+ if (ch === '"') {
124
+ inDoubleQ = false;
125
+ }
126
+ out += ' ';
127
+ }
128
+ else if (inSingleQ) {
129
+ if (ch === '\\') {
130
+ out += ' ';
131
+ i += 2;
132
+ continue;
133
+ }
134
+ if (ch === "'") {
135
+ inSingleQ = false;
136
+ }
137
+ out += ' ';
138
+ }
139
+ i++;
140
+ }
141
+ result.push(out);
142
+ }
143
+ return result;
144
+ }
145
+ /**
146
+ * Classify the exception-handling flow of an except block body.
147
+ *
148
+ * Input: stripped lines (comments and strings already neutralized).
149
+ * Returns the strictest applicable classification.
150
+ */
151
+ function classifyExceptionFlow(strippedBodyLines, exceptIndent) {
152
+ // Only consider lines that are within the except block's indentation scope
153
+ const blockLines = strippedBodyLines.filter(l => {
154
+ const t = l.trimStart();
155
+ if (t.length === 0)
156
+ return false;
157
+ const indent = l.length - t.length;
158
+ return indent > exceptIndent;
159
+ });
160
+ if (blockLines.length === 0)
161
+ return 'swallow';
162
+ const bodyText = blockLines.join('\n');
163
+ // Detect raise statements — only real Python raise keywords at statement level
164
+ // (not inside strings or comments, already stripped above)
165
+ const RAISE_STMT_RE = /^\s*raise\b/m;
166
+ const hasRaise = RAISE_STMT_RE.test(bodyText);
167
+ // Detect "raise X from e" or "raise NewException(" — transformed rethrow
168
+ const TRANSFORM_RAISE_RE = /^\s*raise\s+\w+\s*(?:\(|from)/m;
169
+ const hasTransformRaise = TRANSFORM_RAISE_RE.test(bodyText);
170
+ // Detect bare "raise" (re-raises current exception)
171
+ const BARE_RAISE_RE = /^\s*raise\s*$/m;
172
+ const hasBareRaise = BARE_RAISE_RE.test(bodyText);
173
+ // Detect conditional raise (raise inside if block at deeper indent)
174
+ const CONDITIONAL_RAISE_RE = /^\s+raise\b/m;
175
+ const hasConditionalRaise = !hasBareRaise && !hasTransformRaise && CONDITIONAL_RAISE_RE.test(bodyText);
176
+ const hasLogging = LOGGING_RE.test(bodyText);
177
+ if (hasBareRaise) {
178
+ // Clean re-raise — not a violation
179
+ return 'partial-rethrow'; // partial because could be log+reraise
180
+ }
181
+ if (hasTransformRaise)
182
+ return 'transformed-rethrow';
183
+ if (hasConditionalRaise)
184
+ return 'partial-rethrow';
185
+ if (!hasRaise && hasLogging)
186
+ return 'log-only';
187
+ if (!hasRaise && !hasLogging)
188
+ return 'swallow';
189
+ // raise present but not bare/transform/conditional — treat as partial
190
+ return 'partial-rethrow';
191
+ }
192
+ class PY003BroadExceptClause {
193
+ id = 'PY003';
194
+ name = 'Broad except clause swallowing errors';
195
+ policyRef = 'P017';
196
+ severity = 'BLOCKING';
197
+ languages = ['python'];
198
+ description = 'except Exception: blocks that neither re-raise nor log silently swallow all exceptions including system errors.';
199
+ check(filePath, sourceText) {
200
+ try {
201
+ const violations = [];
202
+ const lines = sourceText.split('\n');
203
+ for (let i = 0; i < lines.length; i++) {
204
+ const line = lines[i];
205
+ const match = BROAD_EXCEPT_RE.exec(line);
206
+ if (!match)
207
+ continue;
208
+ const exceptIndent = match[1].length;
209
+ // Collect the raw except block body lines (indented deeper than the except)
210
+ const rawBodyLines = [];
211
+ let j = i + 1;
212
+ while (j < lines.length) {
213
+ const bodyLine = lines[j];
214
+ const bodyTrimmed = bodyLine.trimStart();
215
+ // Empty line — continue collecting
216
+ if (bodyTrimmed.length === 0) {
217
+ rawBodyLines.push(bodyLine);
218
+ j++;
219
+ continue;
220
+ }
221
+ const bodyIndent = bodyLine.length - bodyTrimmed.length;
222
+ // If indent is less than or equal to the except indent, block ended
223
+ if (bodyIndent <= exceptIndent)
224
+ break;
225
+ rawBodyLines.push(bodyLine);
226
+ j++;
227
+ }
228
+ if (rawBodyLines.length === 0)
229
+ continue;
230
+ // ── AST-level analysis: strip comments and strings before checking ──
231
+ const strippedLines = stripCommentsAndStrings(rawBodyLines);
232
+ const flowClass = classifyExceptionFlow(strippedLines, exceptIndent);
233
+ // Only violations: swallow and log-only (log without re-raise)
234
+ // transformed-rethrow and partial-rethrow are handled by the engineer
235
+ if (flowClass === 'partial-rethrow' || flowClass === 'transformed-rethrow') {
236
+ continue;
237
+ }
238
+ // Bare re-raise check: if bare raise exists in RAW lines (before stripping)
239
+ // this is not a swallow — the stripCommentsAndStrings already handles
240
+ // comment stripping, so we just trust classifyExceptionFlow here.
241
+ if (flowClass !== 'swallow' && flowClass !== 'log-only')
242
+ continue;
243
+ const nonEmptyNonPass = strippedLines
244
+ .map(l => l.trim())
245
+ .filter(l => l.length > 0 && l !== 'pass');
246
+ const confidence = flowClass === 'swallow'
247
+ ? (nonEmptyNonPass.length === 0 ? 0.97 : 0.88)
248
+ : 0.82; // log-only
249
+ const evidence = line.slice(0, 120);
250
+ violations.push({
251
+ ruleId: this.id,
252
+ ruleName: this.name,
253
+ policyRef: this.policyRef,
254
+ severity: this.severity,
255
+ filePath,
256
+ line: i + 1,
257
+ column: exceptIndent + 1,
258
+ evidence: `${evidence} [flow:${flowClass}]`,
259
+ operationalRisk: `except Exception: block classified as '${flowClass}'. ` +
260
+ 'Catches ALL exceptions (including SystemExit, KeyboardInterrupt, MemoryError) without ' +
261
+ 're-raising. Silent failures make debugging impossible and hide operational issues.',
262
+ remediation: 'Either re-raise after handling: `except Exception as e: logger.error(e); raise` ' +
263
+ 'or narrow the exception type. Avoid bare `except Exception` without at minimum logging.',
264
+ determinism: 'deterministic-structural',
265
+ confidence,
266
+ language: 'python',
267
+ });
268
+ }
269
+ return violations;
270
+ }
271
+ catch {
272
+ return [];
273
+ }
274
+ }
275
+ }
276
+ exports.PY003BroadExceptClause = PY003BroadExceptClause;
277
+ //# sourceMappingURL=PY003-broad-except-clause.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PY003-broad-except-clause.js","sourceRoot":"","sources":["../../../src/structural-rules/python/PY003-broad-except-clause.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { StructuralRule, StructuralViolation, RuleLanguage } from '../types';
2
+ export declare class PY004SwallowedAsyncException implements StructuralRule {
3
+ id: string;
4
+ name: string;
5
+ policyRef: string;
6
+ severity: "ADVISORY";
7
+ languages: RuleLanguage[];
8
+ description: string;
9
+ check(filePath: string, sourceText: string): StructuralViolation[];
10
+ }
11
+ //# sourceMappingURL=PY004-swallowed-async-exception.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PY004-swallowed-async-exception.d.ts","sourceRoot":"","sources":["../../../src/structural-rules/python/PY004-swallowed-async-exception.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAc7E,qBAAa,4BAA6B,YAAW,cAAc;IACjE,EAAE,SAAW;IACb,IAAI,SAAiC;IACrC,SAAS,SAAU;IACnB,QAAQ,EAAG,UAAU,CAAU;IAC/B,SAAS,EAAE,YAAY,EAAE,CAAc;IACvC,WAAW,SAEsE;IAEjF,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,mBAAmB,EAAE;CAkJnE"}
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PY004SwallowedAsyncException = void 0;
4
+ // Detect asyncio.gather( calls
5
+ const GATHER_RE = /asyncio\.gather\s*\(/g;
6
+ // return_exceptions=True present
7
+ const RETURN_EXCEPTIONS_RE = /return_exceptions\s*=\s*True/;
8
+ // Detect: await task inside try blocks where except only logs (no re-raise)
9
+ const AWAIT_IN_TRY_RE = /\bawait\s+\w+/;
10
+ const EXCEPT_RE = /^\s*except\b/;
11
+ const RERAISE_RE = /\braise\b/;
12
+ const LOGGING_RE = /\b(?:log|logger|logging|error|warn|report|track|capture|print)\s*[\.(]/i;
13
+ class PY004SwallowedAsyncException {
14
+ id = 'PY004';
15
+ name = 'Swallowed asyncio exception';
16
+ policyRef = 'P018';
17
+ severity = 'ADVISORY';
18
+ languages = ['python'];
19
+ description = 'asyncio.gather() without return_exceptions=True raises on first failure, silently cancelling other tasks. ' +
20
+ 'Also detects try/await blocks where the except only logs without re-raising.';
21
+ check(filePath, sourceText) {
22
+ try {
23
+ const violations = [];
24
+ const lines = sourceText.split('\n');
25
+ // --- Pattern 1: asyncio.gather( without return_exceptions=True ---
26
+ for (let i = 0; i < lines.length; i++) {
27
+ const line = lines[i];
28
+ if (!line.includes('asyncio.gather('))
29
+ continue;
30
+ // Collect the full gather call (may span multiple lines)
31
+ let gatherCall = line;
32
+ let j = i + 1;
33
+ // Scan up to 10 lines ahead to find closing )
34
+ while (j < Math.min(i + 10, lines.length) && !gatherCall.includes(')')) {
35
+ gatherCall += '\n' + lines[j];
36
+ j++;
37
+ }
38
+ if (RETURN_EXCEPTIONS_RE.test(gatherCall))
39
+ continue;
40
+ // Check if it's wrapped in a try block (look backwards up to 5 lines)
41
+ let insideTry = false;
42
+ for (let k = Math.max(0, i - 5); k < i; k++) {
43
+ if (/^\s*try\s*:/.test(lines[k])) {
44
+ insideTry = true;
45
+ break;
46
+ }
47
+ }
48
+ if (insideTry)
49
+ continue;
50
+ const evidence = line.slice(0, 120);
51
+ violations.push({
52
+ ruleId: this.id,
53
+ ruleName: this.name,
54
+ policyRef: this.policyRef,
55
+ severity: this.severity,
56
+ filePath,
57
+ line: i + 1,
58
+ column: line.indexOf('asyncio.gather') + 1,
59
+ evidence,
60
+ operationalRisk: 'asyncio.gather() without return_exceptions=True raises on the first task failure, ' +
61
+ 'but remaining tasks continue running as orphans. Their results and exceptions are lost, ' +
62
+ 'and resources they hold remain locked.',
63
+ remediation: 'Use `results = await asyncio.gather(*tasks, return_exceptions=True)` then inspect ' +
64
+ 'results: `errors = [r for r in results if isinstance(r, BaseException)]`.',
65
+ determinism: 'deterministic-structural',
66
+ confidence: 0.75,
67
+ language: 'python',
68
+ });
69
+ }
70
+ // --- Pattern 2: await inside try/except where except only logs, no re-raise ---
71
+ let i = 0;
72
+ while (i < lines.length) {
73
+ const line = lines[i];
74
+ const trimmed = line.trimStart();
75
+ if (!/^\s*try\s*:/.test(line)) {
76
+ i++;
77
+ continue;
78
+ }
79
+ const tryIndent = line.length - trimmed.length;
80
+ // Collect try body
81
+ const tryBody = [];
82
+ let j = i + 1;
83
+ while (j < lines.length) {
84
+ const bl = lines[j];
85
+ const bt = bl.trimStart();
86
+ if (bt.length === 0) {
87
+ j++;
88
+ continue;
89
+ }
90
+ const bi = bl.length - bt.length;
91
+ if (bi <= tryIndent && bt.length > 0)
92
+ break;
93
+ tryBody.push(bl);
94
+ j++;
95
+ }
96
+ // Does the try body contain an await?
97
+ if (!AWAIT_IN_TRY_RE.test(tryBody.join('\n'))) {
98
+ i = j;
99
+ continue;
100
+ }
101
+ // Now look for except block
102
+ while (j < lines.length) {
103
+ const el = lines[j];
104
+ const et = el.trimStart();
105
+ if (et.length === 0) {
106
+ j++;
107
+ continue;
108
+ }
109
+ const ei = el.length - et.length;
110
+ if (ei < tryIndent)
111
+ break;
112
+ if (!EXCEPT_RE.test(el)) {
113
+ j++;
114
+ continue;
115
+ }
116
+ // Found an except — collect its body
117
+ const exceptBody = [];
118
+ let k = j + 1;
119
+ while (k < lines.length) {
120
+ const kb = lines[k];
121
+ const kt = kb.trimStart();
122
+ if (kt.length === 0) {
123
+ k++;
124
+ continue;
125
+ }
126
+ const ki = kb.length - kt.length;
127
+ if (ki <= ei && kt.length > 0)
128
+ break;
129
+ exceptBody.push(kb);
130
+ k++;
131
+ }
132
+ const exceptText = exceptBody.join('\n');
133
+ if (!RERAISE_RE.test(exceptText) && LOGGING_RE.test(exceptText)) {
134
+ // Only logs, no re-raise — flag it
135
+ const evidence = el.slice(0, 120);
136
+ violations.push({
137
+ ruleId: this.id,
138
+ ruleName: this.name,
139
+ policyRef: this.policyRef,
140
+ severity: this.severity,
141
+ filePath,
142
+ line: j + 1,
143
+ column: ei + 1,
144
+ evidence,
145
+ operationalRisk: 'Exception from an awaited task is caught, logged, but not re-raised. ' +
146
+ 'The caller receives a successful return instead of an error, ' +
147
+ 'causing silent data inconsistency.',
148
+ remediation: 'Re-raise after logging: `except Exception as e: logger.error(e); raise` ' +
149
+ 'or let the exception propagate to a top-level handler.',
150
+ determinism: 'heuristic-advisory',
151
+ confidence: 0.75,
152
+ language: 'python',
153
+ });
154
+ }
155
+ j = k;
156
+ }
157
+ i = j;
158
+ }
159
+ return violations;
160
+ }
161
+ catch {
162
+ return [];
163
+ }
164
+ }
165
+ }
166
+ exports.PY004SwallowedAsyncException = PY004SwallowedAsyncException;
167
+ //# sourceMappingURL=PY004-swallowed-async-exception.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PY004-swallowed-async-exception.js","sourceRoot":"","sources":["../../../src/structural-rules/python/PY004-swallowed-async-exception.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { StructuralRule, StructuralViolation, RuleLanguage } from '../types';
2
+ export declare class PY005FastAPIWithoutPydantic implements StructuralRule {
3
+ id: string;
4
+ name: string;
5
+ policyRef: string;
6
+ severity: "BLOCKING";
7
+ languages: RuleLanguage[];
8
+ description: string;
9
+ check(filePath: string, sourceText: string): StructuralViolation[];
10
+ }
11
+ //# sourceMappingURL=PY005-fastapi-without-pydantic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PY005-fastapi-without-pydantic.d.ts","sourceRoot":"","sources":["../../../src/structural-rules/python/PY005-fastapi-without-pydantic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AA0D7E,qBAAa,2BAA4B,YAAW,cAAc;IAChE,EAAE,SAAW;IACb,IAAI,SAAuE;IAC3E,SAAS,SAAU;IACnB,QAAQ,EAAG,UAAU,CAAU;IAC/B,SAAS,EAAE,YAAY,EAAE,CAAc;IACvC,WAAW,SACmH;IAE9H,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,mBAAmB,EAAE;CAiGnE"}
@@ -0,0 +1,154 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PY005FastAPIWithoutPydantic = void 0;
4
+ // FastAPI route decorator patterns
5
+ const ROUTE_DECORATOR_RE = /^\s*@(?:app|router|api_router)\.(get|post|put|patch|delete|head|options|trace)\s*\(/;
6
+ // Detect request.body() or request.json() or await request.body() / await request.json()
7
+ const RAW_REQUEST_RE = /\b(?:request|req)\s*\.\s*(?:body|json)\s*\(\)/;
8
+ // Detect Pydantic model usage in function signature: param: ModelName
9
+ // where ModelName starts with uppercase and isn't Request/Response/BackgroundTasks
10
+ const PYDANTIC_PARAM_RE = /\w+\s*:\s*([A-Z][A-Za-z0-9]+)(?!\s*=\s*(?:None|True|False|\d))/;
11
+ // Common non-Pydantic FastAPI types to exclude
12
+ const NON_PYDANTIC_TYPES = new Set([
13
+ 'Request',
14
+ 'Response',
15
+ 'BackgroundTasks',
16
+ 'HTTPException',
17
+ 'Depends',
18
+ 'Optional',
19
+ 'List',
20
+ 'Dict',
21
+ 'Any',
22
+ 'str',
23
+ 'int',
24
+ 'float',
25
+ 'bool',
26
+ 'bytes',
27
+ ]);
28
+ function extractFunctionSignature(lines, startIdx) {
29
+ // Collect function definition until the colon is found
30
+ let sig = '';
31
+ let depth = 0;
32
+ for (let i = startIdx; i < Math.min(startIdx + 10, lines.length); i++) {
33
+ sig += lines[i] + '\n';
34
+ for (const ch of lines[i]) {
35
+ if (ch === '(')
36
+ depth++;
37
+ else if (ch === ')')
38
+ depth--;
39
+ }
40
+ if (depth <= 0 && sig.includes('('))
41
+ break;
42
+ }
43
+ return sig;
44
+ }
45
+ function hasPydanticModelParam(sig) {
46
+ // Find all type annotations in the signature
47
+ const paramRegex = /\w+\s*:\s*([A-Z][A-Za-z0-9_]*)/g;
48
+ let match;
49
+ while ((match = paramRegex.exec(sig)) !== null) {
50
+ const typeName = match[1];
51
+ if (!NON_PYDANTIC_TYPES.has(typeName)) {
52
+ return true;
53
+ }
54
+ }
55
+ return false;
56
+ }
57
+ class PY005FastAPIWithoutPydantic {
58
+ id = 'PY005';
59
+ name = 'FastAPI route handler accessing raw request body without Pydantic';
60
+ policyRef = 'P019';
61
+ severity = 'BLOCKING';
62
+ languages = ['python'];
63
+ description = 'FastAPI route handlers that access request.body() or request.json() without a Pydantic model parameter bypass validation.';
64
+ check(filePath, sourceText) {
65
+ try {
66
+ const violations = [];
67
+ const lines = sourceText.split('\n');
68
+ let i = 0;
69
+ while (i < lines.length) {
70
+ const line = lines[i];
71
+ // Look for route decorator
72
+ if (!ROUTE_DECORATOR_RE.test(line)) {
73
+ i++;
74
+ continue;
75
+ }
76
+ const decoratorLine = i;
77
+ // Find the function definition (next def after decorator, skip other decorators)
78
+ let funcDefLine = -1;
79
+ let j = i + 1;
80
+ while (j < Math.min(i + 10, lines.length)) {
81
+ const l = lines[j].trimStart();
82
+ if (/^(?:async\s+)?def\s+\w+\s*\(/.test(l)) {
83
+ funcDefLine = j;
84
+ break;
85
+ }
86
+ if (l.length > 0 && !l.startsWith('@') && !l.startsWith('#'))
87
+ break;
88
+ j++;
89
+ }
90
+ if (funcDefLine === -1) {
91
+ i = j + 1;
92
+ continue;
93
+ }
94
+ // Extract function signature
95
+ const sig = extractFunctionSignature(lines, funcDefLine);
96
+ // If signature has a Pydantic model param, no violation
97
+ if (hasPydanticModelParam(sig)) {
98
+ i = funcDefLine + 1;
99
+ continue;
100
+ }
101
+ // Find function body (collect until next function/class at same or lower indent)
102
+ const funcIndent = lines[funcDefLine].length - lines[funcDefLine].trimStart().length;
103
+ const bodyLines = [];
104
+ let k = funcDefLine + 1;
105
+ while (k < lines.length) {
106
+ const bl = lines[k];
107
+ const bt = bl.trimStart();
108
+ if (bt.length === 0) {
109
+ k++;
110
+ continue;
111
+ }
112
+ const bi = bl.length - bt.length;
113
+ if (bi <= funcIndent && bt.length > 0)
114
+ break;
115
+ bodyLines.push(bl);
116
+ k++;
117
+ }
118
+ const bodyText = bodyLines.join('\n');
119
+ // Check if body accesses request.body() or request.json()
120
+ if (!RAW_REQUEST_RE.test(bodyText)) {
121
+ i = k;
122
+ continue;
123
+ }
124
+ const evidence = lines[funcDefLine].slice(0, 120);
125
+ violations.push({
126
+ ruleId: this.id,
127
+ ruleName: this.name,
128
+ policyRef: this.policyRef,
129
+ severity: this.severity,
130
+ filePath,
131
+ line: funcDefLine + 1,
132
+ column: (lines[funcDefLine].match(/^\s*/)?.[0].length ?? 0) + 1,
133
+ evidence,
134
+ operationalRisk: 'Raw request body is accessed without Pydantic validation. ' +
135
+ 'Malformed, oversized, or malicious payloads reach business logic directly, ' +
136
+ 'causing runtime errors, type confusion, or injection vulnerabilities.',
137
+ remediation: 'Add a Pydantic model parameter to the route handler: ' +
138
+ '`async def handler(data: MyRequestModel)` and let FastAPI validate and parse the body automatically. ' +
139
+ 'Remove the manual `request.body()` / `request.json()` call.',
140
+ determinism: 'deterministic-structural',
141
+ confidence: 0.80,
142
+ language: 'python',
143
+ });
144
+ i = k;
145
+ }
146
+ return violations;
147
+ }
148
+ catch {
149
+ return [];
150
+ }
151
+ }
152
+ }
153
+ exports.PY005FastAPIWithoutPydantic = PY005FastAPIWithoutPydantic;
154
+ //# sourceMappingURL=PY005-fastapi-without-pydantic.js.map