2sagaco48 1.0.0
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.
- package/diagnostics.mjs +141 -0
- package/dist/compute.worker-j5ahIpF-.js +1 -0
- package/dist/graph.worker-DG6iGCB9.js +1 -0
- package/dist/index.html +2314 -0
- package/flatten-guide.md +248 -0
- package/index.html +12 -0
- package/package.json +38 -0
- package/src/App.orig.tsx +204 -0
- package/src/App.orig1.tsx +27 -0
- package/src/App.orig2.tsx +25 -0
- package/src/App.tsx +3 -0
- package/src/BaseApp.tsx +18 -0
- package/src/INTEGRATION_LEDGER.md +22 -0
- package/src/PERSIST_CANARY.orig.txt +1 -0
- package/src/PERSIST_CANARY.txt +1 -0
- package/src/REGRESSION_LEDGER.md +651 -0
- package/src/TEMPLATE_PIPELINE_AUDIT.md +208 -0
- package/src/WILLIAMS_PERSONA_GUIDE.md +62 -0
- package/src/chat/VeritasChat.tsx +69 -0
- package/src/chat/synthesis.ts +31 -0
- package/src/chat/tier.ts +45 -0
- package/src/components/AdaptersPage.orig.tsx +99 -0
- package/src/components/AdaptersPage.tsx +3 -0
- package/src/components/AdversarialPanel.tsx +136 -0
- package/src/components/AnchorBaselinePanel.tsx +75 -0
- package/src/components/AnswerPanel.tsx +60 -0
- package/src/components/ArtifactPanel.tsx +43 -0
- package/src/components/ChatApp.orig.tsx +1582 -0
- package/src/components/ChatApp.orig1.tsx +722 -0
- package/src/components/ChatApp.tsx +95 -0
- package/src/components/ChatAugmentPanels.tsx +73 -0
- package/src/components/CitationLedgerPanel.tsx +313 -0
- package/src/components/ComputeSandboxPanel.tsx +130 -0
- package/src/components/ConfigPanel.tsx +110 -0
- package/src/components/ControlPlanePage.tsx +195 -0
- package/src/components/CreativeTreeLifePage.tsx +234 -0
- package/src/components/CreativeTreeOfLifePanel.tsx +149 -0
- package/src/components/DebugTracePanel.tsx +137 -0
- package/src/components/DeepReasoningTrace.tsx +48 -0
- package/src/components/EntitySheetPanel.tsx +51 -0
- package/src/components/FailureModesPanel.tsx +104 -0
- package/src/components/GBSDashboard.tsx +239 -0
- package/src/components/GraphView.tsx +116 -0
- package/src/components/HUD.tsx +125 -0
- package/src/components/HypothesisPanel.tsx +67 -0
- package/src/components/InnovationGenomeEngine.tsx +792 -0
- package/src/components/InnovationPersonaGuide.tsx +348 -0
- package/src/components/InnovationPersonaPanel.tsx +289 -0
- package/src/components/LiveResourceHUD.tsx +109 -0
- package/src/components/LongWriterPanel.tsx +112 -0
- package/src/components/MainPipelineV10Bridge.tsx +72 -0
- package/src/components/MarkdownLite.tsx +63 -0
- package/src/components/MemoryInspector.tsx +38 -0
- package/src/components/MemoryMonitor.tsx +52 -0
- package/src/components/MemoryStressPanel.tsx +176 -0
- package/src/components/ModulesPage.tsx +171 -0
- package/src/components/PipelineDebugConsole.orig.tsx +1856 -0
- package/src/components/PipelineDebugConsole.tsx +3 -0
- package/src/components/PreFlightHUD.tsx +67 -0
- package/src/components/PrismaFetchTracePanel.tsx +87 -0
- package/src/components/ReportOSPanel.tsx +165 -0
- package/src/components/ResourceEstimatorPage.tsx +327 -0
- package/src/components/RichText.tsx +181 -0
- package/src/components/SagaOsPanel.tsx +605 -0
- package/src/components/SagaOsReference.tsx +421 -0
- package/src/components/SagaOsV2Panel.tsx +594 -0
- package/src/components/SagaStyleTab.tsx +382 -0
- package/src/components/SageBlueprintPanel.tsx +416 -0
- package/src/components/SharedChatInput.tsx +77 -0
- package/src/components/StatFinancePanel.tsx +190 -0
- package/src/components/StylePersonaPanel.tsx +128 -0
- package/src/components/TemplatesPage.tsx +300 -0
- package/src/components/TestPanel.tsx +63 -0
- package/src/components/TraceLog.tsx +89 -0
- package/src/components/V15CalibrationAugment.tsx +1134 -0
- package/src/components/V15CalibrationDialog.tsx +1871 -0
- package/src/components/V15Overlay.orig.tsx +120 -0
- package/src/components/V15Overlay.tsx +4 -0
- package/src/components/V15OverlayWrapper.tsx +362 -0
- package/src/components/V15Toggle.tsx +34 -0
- package/src/console/DiagnosticRecoveryPanel.tsx +358 -0
- package/src/console/EngineeringConsole.tsx +103 -0
- package/src/console/FictionSystemPanels.tsx +882 -0
- package/src/console/InspectorPanels.tsx +254 -0
- package/src/console/ManuscriptGraderPanel.tsx +182 -0
- package/src/console/RecursiveReviserPanel.tsx +245 -0
- package/src/console/SeedControlPanel.tsx +135 -0
- package/src/console/StoryControllerPanel.tsx +443 -0
- package/src/console/engineBridge.ts +258 -0
- package/src/console/gemini.ts +106 -0
- package/src/console/grading.ts +250 -0
- package/src/console/util.ts +45 -0
- package/src/fiction/continuity.ts +372 -0
- package/src/fiction/crash-protection.ts +111 -0
- package/src/fiction/diagnostics.ts +208 -0
- package/src/fiction/diff.ts +74 -0
- package/src/fiction/entity-state-machine.ts +214 -0
- package/src/fiction/nive-engine.ts +404 -0
- package/src/fiction/pipeline-selftest.ts +132 -0
- package/src/fiction/seed-control.ts +193 -0
- package/src/fiction/story-engine.ts +326 -0
- package/src/fiction/types.ts +68 -0
- package/src/flatten-guide.md +379 -0
- package/src/index.css +14 -0
- package/src/index.orig.css +13 -0
- package/src/lib/academic-sources.base.ts +188 -0
- package/src/lib/academic-sources.orig.ts +28 -0
- package/src/lib/academic-sources.ts +10 -0
- package/src/lib/advanced-math.ts +119 -0
- package/src/lib/adversarial-engine.base.ts +409 -0
- package/src/lib/adversarial-engine.ts +82 -0
- package/src/lib/ai-revision.ts +623 -0
- package/src/lib/app-state.tsx +426 -0
- package/src/lib/artifact-registry.ts +97 -0
- package/src/lib/artifacts.ts +180 -0
- package/src/lib/atlas-dr.ts +167 -0
- package/src/lib/browser-mtls.ts +127 -0
- package/src/lib/browser-queue.ts +221 -0
- package/src/lib/browser-search-scraper.base.ts +382 -0
- package/src/lib/browser-search-scraper.orig.ts +90 -0
- package/src/lib/browser-search-scraper.ts +10 -0
- package/src/lib/calc-interceptor.ts +82 -0
- package/src/lib/citation-lane-tap.ts +78 -0
- package/src/lib/citation-ledger-store.ts +369 -0
- package/src/lib/citation-ledger.ts +203 -0
- package/src/lib/compute-sandbox.ts +215 -0
- package/src/lib/compute.worker.ts +153 -0
- package/src/lib/connectors/gemini.ts +120 -0
- package/src/lib/connectors/jina.ts +233 -0
- package/src/lib/connectors/marketdata.ts +51 -0
- package/src/lib/connectors/prismafetch.base.ts +175 -0
- package/src/lib/connectors/prismafetch.ts +1 -0
- package/src/lib/connectors/serpapi.ts +65 -0
- package/src/lib/connectors/wikidata.ts +44 -0
- package/src/lib/constraints.ts +331 -0
- package/src/lib/continuation-detector.base.ts +153 -0
- package/src/lib/continuation-detector.ts +48 -0
- package/src/lib/contradraft.ts +168 -0
- package/src/lib/cors-proxy.ts +120 -0
- package/src/lib/coverage.ts +81 -0
- package/src/lib/debug/architecture-prescription.ts +556 -0
- package/src/lib/debug/covea-repair.ts +543 -0
- package/src/lib/debug/helios-ground.ts +319 -0
- package/src/lib/debug/intent-decomposer.ts +281 -0
- package/src/lib/debug/intent-lattice.ts +349 -0
- package/src/lib/debug/pipeline-diagnosis.ts +1149 -0
- package/src/lib/debug/pipeline-trace-bus.ts +424 -0
- package/src/lib/debug/prompt-forge.ts +678 -0
- package/src/lib/debug/repair-sites.ts +513 -0
- package/src/lib/debug/research-phase.ts +266 -0
- package/src/lib/debug/retrieval-context.ts +76 -0
- package/src/lib/debug/retrieval-hardener.ts +142 -0
- package/src/lib/debug/scraper-debug-runner.ts +576 -0
- package/src/lib/debug/scraper-forensics.ts +418 -0
- package/src/lib/debug/scraper-lane-roadmap.ts +136 -0
- package/src/lib/debug/self-test.ts +415 -0
- package/src/lib/debug/step-attribution.ts +211 -0
- package/src/lib/debug/template-rubric.ts +301 -0
- package/src/lib/debug/unified-innovation.ts +126 -0
- package/src/lib/debug/veritas-hybrid-scraper.ts +335 -0
- package/src/lib/defense-registry.ts +104 -0
- package/src/lib/defenses.ts +162 -0
- package/src/lib/deterministic-citation-ledger.ts +174 -0
- package/src/lib/elo-registry.base.ts +117 -0
- package/src/lib/elo-registry.ts +1 -0
- package/src/lib/entity-resolver.ts +291 -0
- package/src/lib/failure-modes.ts +267 -0
- package/src/lib/feature-registry.ts +69 -0
- package/src/lib/flaw-registry.ts +143 -0
- package/src/lib/flaws/_template.ts +52 -0
- package/src/lib/flaws/builtins.ts +63 -0
- package/src/lib/flaws/finance.ts +169 -0
- package/src/lib/flaws/fixers.ts +10 -0
- package/src/lib/flaws/index.ts +32 -0
- package/src/lib/flaws/legal.ts +157 -0
- package/src/lib/flaws/medical.ts +188 -0
- package/src/lib/flaws/original-defenses-pack.ts +81 -0
- package/src/lib/flaws/sample-declarative-pack.json +38 -0
- package/src/lib/flaws/selftest.ts +201 -0
- package/src/lib/flaws/software-extended.ts +125 -0
- package/src/lib/flaws/software-rn-webgl.ts +182 -0
- package/src/lib/flaws/statistics-advanced.ts +46 -0
- package/src/lib/flaws/statistics.ts +61 -0
- package/src/lib/gbse/config.ts +20 -0
- package/src/lib/gbse/engine.ts +226 -0
- package/src/lib/gbse/graph.ts +118 -0
- package/src/lib/gbse/graph.worker.ts +21 -0
- package/src/lib/gbse/tests.ts +117 -0
- package/src/lib/gbse/tiers.ts +65 -0
- package/src/lib/gbse/types.ts +94 -0
- package/src/lib/innovation-genome-engine-v2.orig.ts +1001 -0
- package/src/lib/innovation-genome-engine-v2.ts +3 -0
- package/src/lib/innovation-genome-engine.orig.ts +330 -0
- package/src/lib/innovation-genome-engine.ts +3 -0
- package/src/lib/innovation-genome-v10.orig.ts +314 -0
- package/src/lib/innovation-genome-v10.ts +2 -0
- package/src/lib/innovation-genome-v3.orig.ts +2015 -0
- package/src/lib/innovation-genome-v3.ts +2 -0
- package/src/lib/innovation-genome-v4.orig.ts +1959 -0
- package/src/lib/innovation-genome-v4.ts +2 -0
- package/src/lib/innovation-genome-v5.orig.ts +1210 -0
- package/src/lib/innovation-genome-v5.ts +2 -0
- package/src/lib/innovation-genome-v7.orig.ts +2549 -0
- package/src/lib/innovation-genome-v7.ts +2 -0
- package/src/lib/innovation-genome-v8.orig.ts +1485 -0
- package/src/lib/innovation-genome-v8.ts +2 -0
- package/src/lib/innovation-genome-v9.orig.ts +643 -0
- package/src/lib/innovation-genome-v9.ts +2 -0
- package/src/lib/jina.base.ts +293 -0
- package/src/lib/jina.ts +1 -0
- package/src/lib/live-telemetry.ts +61 -0
- package/src/lib/longwriter.ts +84 -0
- package/src/lib/manuscript-grade-v2.ts +298 -0
- package/src/lib/memory-governor.ts +173 -0
- package/src/lib/memory-stress-tests.ts +248 -0
- package/src/lib/model-intelligence.ts +48 -0
- package/src/lib/model-rotator.ts +75 -0
- package/src/lib/models.orig.ts +513 -0
- package/src/lib/models.ts +151 -0
- package/src/lib/n-deep.base.ts +592 -0
- package/src/lib/n-deep.ts +51 -0
- package/src/lib/nih-simulator.ts +56 -0
- package/src/lib/nih-vulnerability-fixes.ts +460 -0
- package/src/lib/omega-templates.base.ts +294 -0
- package/src/lib/omega-templates.ts +225 -0
- package/src/lib/omni-nexus.ts +64 -0
- package/src/lib/oracle-adapters.ts +67 -0
- package/src/lib/oracle-registry.ts +163 -0
- package/src/lib/orchestrator.ts +357 -0
- package/src/lib/output-boundary.ts +235 -0
- package/src/lib/overrides/vite-native-chaos-harness.ts +154 -0
- package/src/lib/overrides/vite-native-contract-plane.ts +245 -0
- package/src/lib/overrides/vite-native-doctor-plane.ts +208 -0
- package/src/lib/overrides/vite-native-knowledge-store.ts +267 -0
- package/src/lib/overrides/vite-native-policy-plane.ts +190 -0
- package/src/lib/overrides/vite-native-replay-plane.ts +197 -0
- package/src/lib/overrides/vite-native-runtime-plane.ts +106 -0
- package/src/lib/overrides/vite-native-scraper.ts +848 -0
- package/src/lib/overrides/vite-native-selftest-functional.ts +217 -0
- package/src/lib/overrides/vite-native-selftest.ts +183 -0
- package/src/lib/overrides/vite-native-snapshot-plane.ts +123 -0
- package/src/lib/philosophy-toolkit.ts +75 -0
- package/src/lib/pipeline.orig.ts +659 -0
- package/src/lib/pipeline.ts +42 -0
- package/src/lib/precache.ts +128 -0
- package/src/lib/py-sandbox.ts +205 -0
- package/src/lib/quality-score.ts +96 -0
- package/src/lib/quant-engine.ts +231 -0
- package/src/lib/quant-lib.ts +80 -0
- package/src/lib/reportos.ts +175 -0
- package/src/lib/research-os.ts +306 -0
- package/src/lib/resource-estimator.ts +114 -0
- package/src/lib/rpm-governor.ts +93 -0
- package/src/lib/runtime-estimator.ts +135 -0
- package/src/lib/sage/empty-space-v6.ts +560 -0
- package/src/lib/sage/empty-space-v7.ts +222 -0
- package/src/lib/sage/empty-space.ts +733 -0
- package/src/lib/sage/engine.ts +786 -0
- package/src/lib/sage/index.ts +17 -0
- package/src/lib/sage/judge.ts +246 -0
- package/src/lib/sage/prng.ts +53 -0
- package/src/lib/sage/reality-grounding.ts +380 -0
- package/src/lib/sage/repetition.ts +151 -0
- package/src/lib/sage/saga-os-v2-engines.ts +1870 -0
- package/src/lib/sage/saga-os-v2.ts +274 -0
- package/src/lib/sage/saga-os-v3-core.ts +1491 -0
- package/src/lib/sage/saga-os-v3-release.ts +739 -0
- package/src/lib/sage/saga-os-v4-aureate.ts +1161 -0
- package/src/lib/sage/saga-os-v4.ts +212 -0
- package/src/lib/sage/saga-os-v5.ts +205 -0
- package/src/lib/sage/saga-os-v6.ts +346 -0
- package/src/lib/sage/saga-os-v7.ts +314 -0
- package/src/lib/sage/saga-os-v8.ts +505 -0
- package/src/lib/sage/saga-os.ts +329 -0
- package/src/lib/sage/taxonomy.ts +403 -0
- package/src/lib/sage/unified-roll.ts +524 -0
- package/src/lib/sage/worked-sketches.ts +44 -0
- package/src/lib/sage.ts +166 -0
- package/src/lib/scraper-debug-bus.ts +53 -0
- package/src/lib/scraper-enhanced.orig.ts +153 -0
- package/src/lib/scraper-enhanced.ts +10 -0
- package/src/lib/scraper-hardener.base.orig.ts +155 -0
- package/src/lib/scraper-hardener.base.ts +317 -0
- package/src/lib/scraper-hardener.ts +1 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.orig.ts +419 -0
- package/src/lib/scraper-palisade/palisade-adjudicator.ts +13 -0
- package/src/lib/scraper-vnext/arbiter-omega.orig.ts +688 -0
- package/src/lib/scraper-vnext/arbiter-omega.ts +13 -0
- package/src/lib/scraper-vnext/canonical-portfolio-augments.ts +479 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.orig.ts +1529 -0
- package/src/lib/scraper-vnext/canonical-portfolio-orchestrator.ts +13 -0
- package/src/lib/scraper-vnext/conclave-omega.ts +1830 -0
- package/src/lib/scraper-vnext/content-extractor-v2.ts +226 -0
- package/src/lib/scraper-vnext/diagnostics-suite.ts +36 -0
- package/src/lib/scraper-vnext/epistemic-packer.ts +97 -0
- package/src/lib/scraper-vnext/fusion-v2.ts +65 -0
- package/src/lib/scraper-vnext/hydra-reader.orig.ts +1216 -0
- package/src/lib/scraper-vnext/hydra-reader.ts +13 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.orig.ts +91 -0
- package/src/lib/scraper-vnext/native-scraper-browser-vnext.ts +13 -0
- package/src/lib/scraper-vnext/nexus-consensus.orig.ts +905 -0
- package/src/lib/scraper-vnext/nexus-consensus.ts +13 -0
- package/src/lib/scraper-vnext/portfolio-consensus-adjudicator.ts +804 -0
- package/src/lib/scraper-vnext/portfolio-consensus-memory.ts +377 -0
- package/src/lib/scraper-vnext/portfolio-terminal-governor.ts +548 -0
- package/src/lib/scraper-vnext/query-strategist.orig.ts +224 -0
- package/src/lib/scraper-vnext/query-strategist.ts +423 -0
- package/src/lib/scraper-vnext/retrieval-accelerator.ts +708 -0
- package/src/lib/scraper-vnext/retrieval-audit-augments.ts +477 -0
- package/src/lib/scraper-vnext/retrieval-control-plane.ts +904 -0
- package/src/lib/scraper-vnext/retrieval-policy-augments.ts +358 -0
- package/src/lib/scraper-vnext/safe-fetch-v2.ts +157 -0
- package/src/lib/scraper-vnext/sentinel-omega.ts +56 -0
- package/src/lib/scraper-vnext/sentinel-orchestrator.ts +69 -0
- package/src/lib/scraper-vnext/sibyl-oracle.orig.ts +413 -0
- package/src/lib/scraper-vnext/sibyl-oracle.ts +13 -0
- package/src/lib/scraper-vnext/smart-read-v2.ts +55 -0
- package/src/lib/scraper-vnext/spa-rescue-bridge.ts +608 -0
- package/src/lib/scraper-vnext/strata-engine.orig.ts +2666 -0
- package/src/lib/scraper-vnext/strata-engine.ts +13 -0
- package/src/lib/scraper-vnext/structured-source-adapter.orig.ts +369 -0
- package/src/lib/scraper-vnext/structured-source-adapter.ts +34 -0
- package/src/lib/scraper-vnext/terminal-complete.ts +224 -0
- package/src/lib/scraper-vnext/terminal-final.ts +277 -0
- package/src/lib/scraper-vnext/terminal-saturation.ts +345 -0
- package/src/lib/scraper-vnext/terminal-wire.ts +425 -0
- package/src/lib/scraper-vnext/vanguard-titanium.orig.ts +467 -0
- package/src/lib/scraper-vnext/vanguard-titanium.ts +17 -0
- package/src/lib/search-cache.ts +138 -0
- package/src/lib/sloop-runner.base.ts +223 -0
- package/src/lib/sloop-runner.ts +104 -0
- package/src/lib/sloop.ts +114 -0
- package/src/lib/small-model-adapters.ts +82 -0
- package/src/lib/sscp.ts +86 -0
- package/src/lib/system-map.ts +54 -0
- package/src/lib/uni-node.ts +86 -0
- package/src/lib/universal-rigor-guard.ts +433 -0
- package/src/lib/v15-gate-testbed.ts +274 -0
- package/src/lib/v15-gemini.base.ts +89 -0
- package/src/lib/v15-gemini.ts +1 -0
- package/src/lib/v15-grounding.orig.ts +241 -0
- package/src/lib/v15-grounding.ts +79 -0
- package/src/lib/v15-pipeline.base.ts +769 -0
- package/src/lib/v15-pipeline.orig.ts +1154 -0
- package/src/lib/v15-pipeline.ts +674 -0
- package/src/lib/v15-questions.ts +64 -0
- package/src/lib/v15-rate-limiter.orig.ts +91 -0
- package/src/lib/v15-rate-limiter.ts +48 -0
- package/src/lib/v15-state.base.ts +87 -0
- package/src/lib/v15-state.ts +28 -0
- package/src/lib/visual-table-generator.tsx +207 -0
- package/src/lib/wasm-runtime.ts +133 -0
- package/src/lib/williams-persona-guide.ts +265 -0
- package/src/lib/williams-style.base.ts +464 -0
- package/src/lib/williams-style.ts +59 -0
- package/src/lib/worker-pool.ts +96 -0
- package/src/lib/writing-tiers.ts +125 -0
- package/src/main.orig.tsx +10 -0
- package/src/main.tsx +10 -0
- package/src/utils/cn.orig.ts +6 -0
- package/src/utils/cn.ts +6 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +31 -0
- package/unify.mjs +537 -0
- package/vite.config.ts +18 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Software Engineering & AI Codegen Pack — v15.1
|
|
3
|
+
* - Deduplicated soft.swift-actor-protocol-mismatch (was declared twice)
|
|
4
|
+
* - Added V13-V15 web-verified detectors: CORS wildcard, debug endpoint leak,
|
|
5
|
+
* Rust tokio::select cancel-unsafe, Rust blocking-in-async, npm prod sourcemap.
|
|
6
|
+
* - Added soft.node-then-without-catch (V14 distinct from unhandled-promise).
|
|
7
|
+
*/
|
|
8
|
+
import { type FlawDetector, type FlawIssue, type ScanContext } from "../flaw-registry";
|
|
9
|
+
|
|
10
|
+
const mk = (s: FlawIssue["severity"], c: string, m: string, r: string): FlawIssue => ({ severity: s, code: c, message: m, remediation: r });
|
|
11
|
+
|
|
12
|
+
function T(c: ScanContext): string { return `${c.prompt}\n${c.answer}`; }
|
|
13
|
+
function isNode(c: ScanContext) { return /\b(Node\.js|Express|Fastify|Koa|http\.createServer|app\.get|app\.post|router)\b/i.test(T(c)); }
|
|
14
|
+
function isPython(c: ScanContext) { return /\b(python|flask|django|fastapi|\.py\b|def\s+\w+\s*\()\b/i.test(T(c)); }
|
|
15
|
+
function isGo(c: ScanContext) { return /\b(go\s+func|goroutine|chan\s+\w+|<-\s*\w+|make\s*\(\s*chan)\b/i.test(T(c)); }
|
|
16
|
+
function isRust(c: ScanContext) { return /\b(rust|tokio|\.await|async\s+fn|cargo|impl|trait)\b/i.test(T(c)); }
|
|
17
|
+
function isSwift(c: ScanContext) { return /\b(Swift|SwiftUI|UIKit|@MainActor|Actor|Sendable)\b/i.test(T(c)); }
|
|
18
|
+
function isTailwind(c: ScanContext) { return /\b(tailwind|className|class=)\b/i.test(T(c)); }
|
|
19
|
+
function isWebContext(c: ScanContext) { return /\b(cors|Access-Control-Allow-Origin|express|fastify|koa|fetch|api endpoint|http server|route)\b/i.test(T(c)); }
|
|
20
|
+
|
|
21
|
+
export const SOFTWARE_EXTENDED_FLAWS: FlawDetector[] = [
|
|
22
|
+
// ── Node.js ───────────────────────────────────────────────────────────────
|
|
23
|
+
{ id: "soft.node-event-loop-blocking", domain: "domain", appliesTo: isNode, scan: c => /\b(app\.(?:get|post|put|delete|patch)|router\.(?:get|post)|server\.on\(['"]request)\b[\s\S]{0,500}\b(fs\.(?:readFileSync|writeFileSync|existsSync)|execSync|spawnSync|child_process\.execSync)\b/i.test(T(c)) ? [mk("major", "NODE_EVENT_LOOP_BLOCKING", "Synchronous blocking call inside a request handler — stalls the entire event loop.", "Move CPU/blocking I/O to worker_threads or libuv pool (async fs).")] : [] },
|
|
24
|
+
{ id: "soft.node-pipeline-http-destroy", domain: "domain", appliesTo: isNode, scan: c => /\bpipeline\s*\([\s\S]{0,300}\b(?:res|response)\b[\s\S]{0,200}(?:err|error)\s*(?:=>|\))/i.test(T(c)) && /\b(?:res|response)\.(?:end|send|status|writeHead|json)\s*\(/i.test(T(c)) && !/\b(?:source|file|readStream)\.on\s*\(\s*['"]error['"]/i.test(T(c)) ? [mk("major", "NODE_PIPELINE_HTTP_DESTROY", "stream.pipeline() destroys the destination socket on error — fallback res.end() is unreachable.", "Handle source stream errors separately before pipeline().")] : [] },
|
|
25
|
+
{ id: "soft.node-cjs-esm-interop", domain: "domain", appliesTo: isNode, scan: c => { const esm = /"type"\s*:\s*"module"|\.mjs\b/i.test(T(c)); const cjs = /\b(__dirname|__filename|module\.exports\s*=|require\s*\()\b/i.test(T(c)); return esm && cjs ? [mk("major", "NODE_CJS_ESM_INTEROP", "CJS globals (__dirname, require, module.exports) used in ESM context.", "Use import.meta.url + fileURLToPath() for __dirname, import for require, export for module.exports.")] : []; } },
|
|
26
|
+
{ id: "soft.node-toctou-fs", domain: "domain", appliesTo: isNode, scan: c => /\bfs\.(?:access|exists|existsSync)\s*\([\s\S]{0,200}\bfs\.(?:open|readFile|writeFile|appendFile|createWriteStream|createReadStream)\b/i.test(T(c)) ? [mk("major", "NODE_FS_TOCTOU", "fs.access()/exists() check before open/read/write — TOCTOU race condition.", "Open/read/write directly and handle ENOENT/EACCES atomically.")] : [] },
|
|
27
|
+
{ id: "soft.node-unhandled-promise", domain: "domain", appliesTo: isNode, scan: c => /\b(Promise\.all|Promise\.race|Promise\.allSettled)\s*\([\s\S]{0,300}\)\s*;/i.test(T(c)) && !/\.(catch|finally)\s*\(/.test(T(c)) && !/\bawait\b[\s\S]{0,50}\bPromise\.\b/i.test(T(c)) ? [mk("major", "NODE_UNHANDLED_PROMISE_REJECTION", "Promise combinator launched without .catch() or await.", "Await the Promise combinator or append .catch().")] : [] },
|
|
28
|
+
{ id: "soft.node-then-without-catch", domain: "domain", appliesTo: isNode, scan: c => { const thenChain = /\.then\s*\(\s*(?:async\s*)?(?:\([^)]*\)|\w+)\s*=>[\s\S]{0,200}\)\s*(?:;|\n|$)/i.test(c.answer); const hasCatch = /\.catch\s*\(|\.then\s*\([^,]+,\s*(?:async\s*)?(?:\(|\w+\s*=>)|\bawait\s+/i.test(c.answer); const global = /\bprocess\.on\s*\(\s*['"]unhandledRejection['"]/i.test(c.answer); return thenChain && !hasCatch && !global ? [mk("major", "NODE_THEN_WITHOUT_CATCH", ".then() chain has no .catch() handler — rejected promises become unhandledRejection.", "Append .catch(err => ...) or convert to async/await with try/catch.")] : []; } },
|
|
29
|
+
{ id: "soft.node-setmaxlisteners-mask", domain: "domain", appliesTo: isNode, scan: c => /\.setMaxListeners\s*\(\s*(?:0|Infinity)\s*\)/i.test(T(c)) ? [mk("major", "NODE_SETMAXLISTENERS_MASK", "setMaxListeners(0/Infinity) silences the EventEmitter leak warning without fixing the underlying leak.", "Fix listener ownership/cleanup; never disable the warning.")] : [] },
|
|
30
|
+
{ id: "soft.node-http-timeout-missing", domain: "domain", appliesTo: isNode, scan: c => /\b(http\.createServer|https\.createServer|app\.listen)\b/i.test(T(c)) && /\b(?:production|prod|server|api|express|fastify|koa)\b/i.test(T(c)) && !/\b(?:requestTimeout|headersTimeout|keepAliveTimeout|server\.timeout\s*=)/i.test(T(c)) ? [mk("warning", "NODE_HTTP_TIMEOUT_MISSING", "HTTP server without explicit timeout policy.", "Set server.requestTimeout, headersTimeout, keepAliveTimeout deliberately.")] : [] },
|
|
31
|
+
{ id: "soft.node-async-context-loss", domain: "domain", appliesTo: isNode, scan: c => /\bAsyncLocalStorage\b/i.test(T(c)) && /\b(?:\.on\s*\(\s*['"]|EventEmitter|setImmediate|setTimeout|callback)\b/i.test(T(c)) && !/\b(?:AsyncResource|util\.promisify|\.bind\s*\()\b/i.test(T(c)) ? [mk("major", "NODE_ASYNC_CONTEXT_LOSS", "AsyncLocalStorage across callback/EventEmitter without AsyncResource.", "Wrap callbacks with `new AsyncResource('name').bind(fn)`.")] : [] },
|
|
32
|
+
|
|
33
|
+
// ── TypeScript ────────────────────────────────────────────────────────────
|
|
34
|
+
{ id: "soft.ts-any-io-boundary", domain: "domain", appliesTo: c => /\b(TypeScript|fetch|axios|JSON\.parse|req\.body)\b/i.test(T(c)), scan: c => /\b(?:const|let|var)\s+\w+\s*:\s*any\s*=\s*(?:await\s+)?(?:fetch|res\.json\(\)|JSON\.parse|req\.body|response\.json)/i.test(T(c)) && !/\b(zod|joi|valibot|io-ts|safeParse|validate|schema\.parse)\b/i.test(T(c)) ? [mk("major", "TS_ANY_IO_BOUNDARY", "External I/O value typed as `any` without runtime schema validation.", "Use Zod/Valibot/io-ts schema validation.")] : [] },
|
|
35
|
+
{ id: "soft.ts-strict-disabled", domain: "domain", appliesTo: c => /\b(tsconfig|TypeScript)\b/i.test(T(c)), scan: c => /["\s](?:strict|strictNullChecks|noUncheckedIndexedAccess|exactOptionalPropertyTypes)["']?\s*:\s*false/i.test(T(c)) ? [mk("critical", "TS_STRICT_DISABLED", "TypeScript strictness flag disabled.", "Enable strict: true. Fix underlying type errors instead of disabling.")] : [] },
|
|
36
|
+
{ id: "soft.ts-discriminated-union-nonexhaustive", domain: "domain", appliesTo: c => /\b(TypeScript|switch)\b/i.test(T(c)), scan: c => /\bswitch\s*\([^)]+\)\s*\{[\s\S]{0,800}\bdefault\s*:\s*(?![\s\S]{0,60}\bconst\s+_:\s*never\b)/i.test(T(c)) && /\bcase\s+['"][A-Z_]+['"]\s*:/i.test(T(c)) ? [mk("warning", "TS_SWITCH_NONEXHAUSTIVE", "Discriminated union switch lacks never-based exhaustiveness check.", "Add `default: { const _: never = x; return _; }`.")] : [] },
|
|
37
|
+
{ id: "soft.ts-module-resolution-mismatch", domain: "domain", appliesTo: c => /\b(tsconfig|moduleResolution|package\.json)\b/i.test(T(c)), scan: c => /"moduleResolution"\s*:\s*"node(?:10)?"/i.test(T(c)) && /"type"\s*:\s*"module"|import\.meta|\.mjs\b|"module"\s*:\s*"(?:node16|nodenext|esnext|es20\d\d)"/i.test(T(c)) ? [mk("major", "TS_MODULE_RESOLUTION_MISMATCH", "Legacy moduleResolution 'node' (Node10) with ESM context — runtime resolution will fail.", "Set moduleResolution to 'node16', 'nodenext', or 'bundler'.")] : [] },
|
|
38
|
+
{ id: "soft.ts-no-unchecked-index", domain: "domain", appliesTo: c => /\b(Record<string,|Map<)\b/i.test(T(c)), scan: c => /\b\w+\s*\[(?:key|id|name|type|kind|prop|\w+)\s*\]\s*\.\s*\w+/i.test(T(c)) && !/\b(noUncheckedIndexedAccess|\?\?|if\s*\(\s*\w+\[)/i.test(T(c)) ? [mk("warning", "TS_NO_UNCHECKED_INDEX_MISSING", "Index lookup result used directly without null/undefined guard.", "Enable noUncheckedIndexedAccess; use optional chaining.")] : [] },
|
|
39
|
+
|
|
40
|
+
// ── Swift ─────────────────────────────────────────────────────────────────
|
|
41
|
+
{ id: "soft.swift-force-unwrap", domain: "domain", appliesTo: isSwift, scan: c => { const m = T(c).match(/\btry!|\bas!|\b[A-Za-z_]\w*!(?=[.\s),;\]])/g) ?? []; const forced = m.filter(x => x !== "!=").length; return forced >= 2 ? [mk("warning", "SWIFT_FORCE_UNWRAP", `Swift code uses force-unwrap / try! / as! ${forced} time(s).`, "Replace with guard let / if let / do-catch.")] : []; } },
|
|
42
|
+
{ id: "soft.swift-global-mutable-state", domain: "domain", appliesTo: isSwift, scan: c => /\bstatic\s+var\s+\w+/i.test(T(c)) && !/\b(@MainActor|actor\s+\w+|nonisolated\(unsafe\)|Mutex|NSLock|DispatchQueue)\b/i.test(T(c)) ? [mk("major", "SWIFT_GLOBAL_MUTABLE_STATE", "Mutable static var without actor isolation under Swift Concurrency.", "Isolate to an actor or mark @MainActor.")] : [] },
|
|
43
|
+
{ id: "soft.swift-mainactor-nonisolated", domain: "domain", appliesTo: isSwift, scan: c => /\bDispatchQueue\.main\.async\s*\{/i.test(T(c)) && /\b(ViewModel|ObservableObject|@Published|objectWillChange)\b/i.test(T(c)) && !/\b(@MainActor|await\s+MainActor\.run)\b/i.test(T(c)) ? [mk("major", "SWIFT_MAINACTOR_NONISOLATED", "DispatchQueue.main.async used for UI updates instead of @MainActor/MainActor.run.", "Use `await MainActor.run { ... }` or annotate the type with @MainActor.")] : [] },
|
|
44
|
+
{ id: "soft.swift-actor-protocol-mismatch", domain: "domain", appliesTo: isSwift, scan: c => /\bprotocol\s+\w+/i.test(T(c)) && /@MainActor\s+(?:final\s+)?class\s+\w+\s*:\s*\w+/i.test(T(c)) && !/\b@preconcurrency\b/i.test(T(c)) ? [mk("major", "SWIFT_ACTOR_PROTOCOL_MISMATCH", "@MainActor type conforms to a non-isolated protocol without @preconcurrency — Swift 6 compile error.", "Add @preconcurrency to the conformance, or annotate the protocol with @MainActor.")] : [] },
|
|
45
|
+
{ id: "soft.swift-nested-weak-self-no-guardlet", domain: "domain", appliesTo: isSwift, scan: c => { const nested = /\[weak\s+self\][\s\S]{0,250}(?:Timer\.scheduledTimer|\.sink\s*\{|\.map\s*\{|\.receive\s*on\s*:|\.scheduledTimer|withTimeInterval)[\s\S]{0,200}\bself\.\w+/i.test(T(c)); const hasGuard = /\bguard\s+let\s+self\s*=\s*self\b|\bguard\s+let\s+self\s+else\b/i.test(T(c)); return nested && !hasGuard ? [mk("major", "SWIFT_NESTED_WEAK_SELF_NO_GUARDLET", "Outer [weak self] but nested Combine/Timer closure references self directly — retain cycle persists.", "Add `guard let self = self else { return }` inside each closure.")] : []; } },
|
|
46
|
+
{ id: "soft.swift-task-detached-isolation", domain: "domain", appliesTo: isSwift, scan: c => /\bTask\.detached\s*\{/i.test(T(c)) && /\b(self\.|@MainActor|@Published|DispatchQueue\.main|viewModel\.)/i.test(T(c)) && !/\bawait MainActor\.run\b/i.test(T(c)) ? [mk("major", "SWIFT_TASK_DETACHED_ISOLATION", "Task.detached captures self/MainActor state, losing actor isolation.", "Use `Task { ... }` (inherits actor) unless explicitly leaving the actor context.")] : [] },
|
|
47
|
+
|
|
48
|
+
// ── Tailwind ──────────────────────────────────────────────────────────────
|
|
49
|
+
{ id: "soft.tw-container-no-center", domain: "domain", appliesTo: isTailwind, scan: c => /\bclass(?:Name)?\s*=\s*['"`][^'"`]*\bcontainer\b[^'"`]*['"`]/i.test(T(c)) && !/\bmx-auto\b/i.test(T(c)) ? [mk("warning", "TAILWIND_CONTAINER_NOT_CENTERED", "Tailwind `container` used without `mx-auto` — does not center automatically.", "Add `mx-auto` alongside `container`.")] : [] },
|
|
50
|
+
{ id: "soft.tw-dark-mode-fouc", domain: "domain", appliesTo: isTailwind, scan: c => /\b(?:dark\s*mode|dark:)\b/i.test(T(c)) && /\b(?:useEffect|onMount|componentDidMount)\b[\s\S]{0,300}\b(?:localStorage|classList\.(?:add|toggle)|data-theme|documentElement\.classList)\b/i.test(T(c)) && !/\b(?:<head>|beforeInteractive|next\/script|dangerouslySetInnerHTML|suppressHydrationWarning)\b/i.test(T(c)) ? [mk("warning", "TAILWIND_DARK_MODE_FOUC", "Dark mode toggled in useEffect/onMount — flash of wrong theme.", "Inject a blocking <script> in <head> that sets the dark class synchronously.")] : [] },
|
|
51
|
+
{ id: "soft.tw-monorepo-source", domain: "domain", appliesTo: isTailwind, scan: c => /\b(?:monorepo|workspace|packages\/|apps\/|libs\/|ui-library|design-system)\b/i.test(T(c)) && /\b(?:tailwind|tw)\b/i.test(T(c)) && !/\b(?:@source|content\s*:\s*\[|source\s*\()\b/i.test(T(c)) ? [mk("major", "TAILWIND_MONOREPO_SOURCE_MISSING", "Tailwind in monorepo without @source directive for cross-package paths.", "Add @source or extend content[] to include all monorepo package paths.")] : [] },
|
|
52
|
+
{ id: "soft.tw-breakpoint-unit-mismatch", domain: "domain", appliesTo: isTailwind, scan: c => /\bscreens\s*:\s*\{[\s\S]{0,400}(?:\d+px[\s\S]{0,100}\d+rem|\d+rem[\s\S]{0,100}\d+px)/i.test(T(c)) ? [mk("warning", "TAILWIND_BREAKPOINT_UNIT_MISMATCH", "Custom Tailwind screens mix px and rem — sorting anomalies.", "Use one consistent unit for all screens values.")] : [] },
|
|
53
|
+
{ id: "soft.tw-runtime-safelist", domain: "domain", appliesTo: isTailwind, scan: c => /\b(?:cms|contentful|sanity|prismic|strapi|database|db\.|fetched\s+from|api\s+response)\b/i.test(T(c)) && /\$\{[\s\S]{0,80}(?:bg-|text-|border-|p-|m-|grid-cols-)/i.test(T(c)) && !/\b(?:@source\s+inline|safelist\s*:)\b/i.test(T(c)) ? [mk("major", "TAILWIND_RUNTIME_SAFELIST_MISSING", "CMS/DB-driven Tailwind classes without safelist — JIT purges them.", "Add `@source inline(...)` (v4) or safelist (v3).")] : [] },
|
|
54
|
+
|
|
55
|
+
// ── Python ────────────────────────────────────────────────────────────────
|
|
56
|
+
{ id: "soft.py-eval-exec-untrusted", domain: "domain", appliesTo: isPython, scan: c => { if (/\b(never.*eval.*untrusted|eval.*dangerous|avoid.*eval.*user)\b/i.test(T(c))) return []; const evalExec = /\b(eval|exec)\s*\(/i.test(T(c)); const untrusted = /\b(request\.|req\.|user_input|stdin|input\(\)|sys\.argv|environ|query|params|form\[)\b/i.test(T(c)); return evalExec && untrusted ? [mk("critical", "PY_EVAL_EXEC_UNTRUSTED", "eval()/exec() on untrusted external input — arbitrary code execution.", "Use ast.literal_eval() or JSON.loads. eval/exec on user input is RCE.")] : []; } },
|
|
57
|
+
{ id: "soft.py-sql-fstring", domain: "domain", appliesTo: isPython, scan: c => /\bcursor\.execute\s*\(\s*f?[`'"]\s*SELECT[\s\S]{0,200}\{|\bcursor\.execute\s*\(\s*[`'"][^`'"]*%\s*\(?[a-zA-Z_]/i.test(T(c)) ? [mk("critical", "PY_SQL_INJECTION", "SQL query built with f-string or %-formatting — SQL injection (CWE-89).", "Use parameterized queries: cursor.execute('SELECT * FROM users WHERE id = ?', (uid,)).")] : [] },
|
|
58
|
+
{ id: "soft.py-bare-except", domain: "domain", appliesTo: isPython, scan: c => { if (/\b(bare\s+except|avoid.*except\s*:|use.*specific exception)\b/i.test(T(c))) return []; const bare = /\bexcept\s*:/i.test(T(c)) && !/\bexcept\s+(?:Exception|[A-Z]\w*Error|[A-Z]\w*Exception)\b/i.test(T(c)); const inTry = /\btry\s*:/i.test(T(c)); return bare && inTry ? [mk("major", "PY_BARE_EXCEPT", "Bare `except:` catches SystemExit/KeyboardInterrupt — masks bugs, blocks Ctrl-C.", "Use `except Exception:` or name specific exceptions.")] : []; } },
|
|
59
|
+
|
|
60
|
+
// ── Go ────────────────────────────────────────────────────────────────────
|
|
61
|
+
{ id: "soft.go-goroutine-leak", domain: "domain", appliesTo: isGo, scan: c => { if (/\b(goroutine leak|context\.Done|select.*case.*<-ctx)\b/i.test(T(c))) return []; const g = /\bgo\s+func\s*\(/i.test(T(c)); const w = /<-\s*\w+\b/i.test(T(c)); const ctx = /\bcontext\.(WithCancel|WithTimeout|WithDeadline)|ctx\.Done\(\)|select\s*\{[\s\S]*case\s*<-\s*ctx/i.test(T(c)); return g && w && !ctx ? [mk("major", "GO_GOROUTINE_LEAK", "Goroutine waits on a channel without context cancellation — silent memory leak.", "Add `case <-ctx.Done():` to all select statements; pass context.WithCancel/WithTimeout.")] : []; } },
|
|
62
|
+
|
|
63
|
+
// ── C++ ───────────────────────────────────────────────────────────────────
|
|
64
|
+
{ id: "soft.cpp-thread-no-join-detach", domain: "domain", appliesTo: c => /\b(C\+\+|std::thread|pthread|thread)\b/i.test(T(c)), scan: c => /\bstd::thread\s+\w+\s*[\({]/i.test(T(c)) && !/\.(?:join|detach)\(\)/i.test(T(c)) ? [mk("critical", "CPP_THREAD_NO_JOIN_DETACH", "std::thread created without .join() or .detach() — calls std::terminate().", "Every std::thread must be joined or detached before going out of scope. Use std::jthread (C++20).")] : [] },
|
|
65
|
+
|
|
66
|
+
// ── V15: Rust async (web-verified) ────────────────────────────────────────
|
|
67
|
+
{
|
|
68
|
+
id: "soft.rust-tokio-select-cancel-unsafe", domain: "domain",
|
|
69
|
+
description: "tokio::select! branch uses non-cancellation-safe future — data loss on drop.",
|
|
70
|
+
appliesTo: isRust,
|
|
71
|
+
scan: c => {
|
|
72
|
+
if (/\b(cancel[-\s]?saf|cancellation safety|cancel correctness|not cancel safe)\b/i.test(c.answer)) return [];
|
|
73
|
+
return /\btokio::select!\s*\{[\s\S]{0,600}\b(?:\.send\s*\(|read_line\s*\(|read_to_end\s*\(|read_to_string\s*\(|read_buf\s*\()/i.test(c.answer)
|
|
74
|
+
? [mk("major", "RUST_TOKIO_SELECT_CANCEL_UNSAFE", "tokio::select! branch contains a non-cancellation-safe future (mpsc send, read_line). When another branch wins, this future is dropped mid-operation and data is silently lost.", "Use only cancel-safe futures in select! branches (mpsc Receiver::recv, AsyncReadExt::read, interval.tick). For sends, reserve a permit first. Source: Oxide RFD 0400.")]
|
|
75
|
+
: [];
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "soft.rust-blocking-in-async", domain: "domain",
|
|
80
|
+
description: "Blocking call inside async fn — stalls the Tokio worker thread.",
|
|
81
|
+
appliesTo: c => /\basync\s+fn\b/i.test(c.answer) && /\b(tokio|rust|\.await)\b/i.test(T(c)),
|
|
82
|
+
scan: c => {
|
|
83
|
+
if (/\bspawn_blocking\b|\bblock_in_place\b/i.test(c.answer)) return [];
|
|
84
|
+
return /\basync\s+fn\b[\s\S]{0,500}\b(?:std::thread::sleep\s*\(|std::fs::(?:read|write|File::open|read_to_string)\s*\()/i.test(c.answer)
|
|
85
|
+
? [mk("major", "RUST_BLOCKING_IN_ASYNC", "Blocking call (std::thread::sleep or sync std::fs) inside an async fn stalls the Tokio worker thread — starves every other task on that worker.", "Use tokio::time::sleep and tokio::fs (or async clients). For unavoidable CPU work, offload via tokio::task::spawn_blocking.")]
|
|
86
|
+
: [];
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// ── V15: Web security (web-verified) ──────────────────────────────────────
|
|
91
|
+
{
|
|
92
|
+
id: "soft.web-cors-wildcard", domain: "domain",
|
|
93
|
+
description: "Over-permissive CORS — AI's reflexive 'fix' for CORS errors.",
|
|
94
|
+
appliesTo: isWebContext,
|
|
95
|
+
scan: c => {
|
|
96
|
+
if (/\b(?:vulnerab|insecure|avoid|never use|do not use|don't use|risk|dangerous|should not)\b[\s\S]{0,80}(?:cors|wildcard|allow-origin|\*)/i.test(c.answer)) return [];
|
|
97
|
+
const wildcardHeader = /Access-Control-Allow-Origin['"]?\s*[,:]\s*['"]\*['"]/i.test(c.answer);
|
|
98
|
+
const corsWildcard = /\bcors\s*\(\s*\{[^}]*\borigin\s*:\s*(?:['"]\*['"]|true)/i.test(c.answer);
|
|
99
|
+
return wildcardHeader || corsWildcard
|
|
100
|
+
? [mk("major", "WEB_CORS_WILDCARD_ORIGIN", "Over-permissive CORS (Access-Control-Allow-Origin: '*' or cors origin:'*') — exposes API to any website.", "Allowlist explicit trusted origins. Never combine wildcard with credentials:true.")]
|
|
101
|
+
: [];
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
id: "soft.web-debug-endpoint-leak", domain: "domain",
|
|
106
|
+
description: "Unprotected debug route returning process.env/internal config.",
|
|
107
|
+
appliesTo: isWebContext,
|
|
108
|
+
scan: c => /\b(?:app|router|server)\.(?:get|use|all|post)\s*\(\s*['"]\/(?:debug|__debug|internal|admin\/debug|_debug|env)\b[\s\S]{0,200}(?:process\.env\b|res\.json\s*\(\s*process|res\.send\s*\(\s*(?:process\b|config\b|JSON\.stringify\s*\(\s*process))/i.test(c.answer)
|
|
109
|
+
? [mk("major", "WEB_DEBUG_ENDPOINT_LEAK", "Debug/internal route exposes process.env or internal config — leaks secrets and connection strings.", "Remove debug endpoints from production or gate behind authentication + environment check.")]
|
|
110
|
+
: [],
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// ── V15: npm packaging (web-verified Claude Code leak Mar 31 2026) ────────
|
|
114
|
+
{
|
|
115
|
+
id: "soft.npm-sourcemap-production-leak", domain: "domain",
|
|
116
|
+
description: "Production source maps enabled — ships original source publicly.",
|
|
117
|
+
appliesTo: c => /\b(next\.config|webpack|vite|rollup|esbuild|package\.json|source\s?map|devtool|build config)\b/i.test(T(c)),
|
|
118
|
+
scan: c => {
|
|
119
|
+
if (/\b(?:disable.*source\s?map|sourcemap.*leak|strip.*source|do not ship source|hidden-source-map)\b/i.test(c.answer)) return [];
|
|
120
|
+
return /\bproductionBrowserSourceMaps\s*:\s*true\b/i.test(c.answer) || /\bsourcesContent\s*:\s*true\b/i.test(c.answer)
|
|
121
|
+
? [mk("warning", "NPM_SOURCEMAP_PRODUCTION_LEAK", "Production source maps enabled (productionBrowserSourceMaps:true / sourcesContent:true) — ships original source publicly. Same vector as Claude Code source leak (Mar 31 2026).", "Disable production source maps or upload them privately to Sentry. Add .map to .npmignore so they never reach npm.")]
|
|
122
|
+
: [];
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
];
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React Native + WebGL/WebGPU Specific Failures — v15.1
|
|
3
|
+
* Deduplicated. Added react.invalid-html-nesting, react.hydration-mismatch,
|
|
4
|
+
* three.dispose-partial-with-texture (web-verified RN 0.85 / Reanimated 4).
|
|
5
|
+
*/
|
|
6
|
+
import { type FlawDetector, type FlawIssue, type ScanContext } from "../flaw-registry";
|
|
7
|
+
|
|
8
|
+
const mk = (s: FlawIssue["severity"], c: string, m: string, r: string): FlawIssue => ({ severity: s, code: c, message: m, remediation: r });
|
|
9
|
+
|
|
10
|
+
function T(c: ScanContext): string { return `${c.prompt}\n${c.answer}`; }
|
|
11
|
+
function isReact(c: ScanContext) { return /\b(React|Next\.js|JSX|TSX|useState|useEffect|export\s+default\s+function|<\w+[\s/>])/i.test(T(c)); }
|
|
12
|
+
function isRN(c: ScanContext) { return /\b(React Native|RN|Expo|Metro|Fabric|TurboModules|Reanimated|Hermes)\b/i.test(T(c)); }
|
|
13
|
+
function isRNNewArch(c: ScanContext) { return /\b(New Architecture|TurboModule|Codegen|Fabric|JSI|bridgeless|0\.7[6-9]|0\.8[0-5])\b/i.test(T(c)); }
|
|
14
|
+
function isWebGL(c: ScanContext) { return /\b(WebGL|WebGPU|Three\.js|GLSL|shader|GPU|canvas)\b/i.test(T(c)); }
|
|
15
|
+
|
|
16
|
+
export const SOFTWARE_RN_WEBGL_FLAWS: FlawDetector[] = [
|
|
17
|
+
// ── React general ─────────────────────────────────────────────────────────
|
|
18
|
+
{
|
|
19
|
+
id: "react.invalid-html-nesting", domain: "domain",
|
|
20
|
+
description: "Invalid HTML nesting — browser auto-corrects, breaks SSR hydration.",
|
|
21
|
+
appliesTo: isReact,
|
|
22
|
+
scan: c => {
|
|
23
|
+
const divInP = /<p\b[^>]*>[\s\S]{0,400}<(?:div|section|article|main|header|footer|nav|aside|h[1-6]|ul|ol|li|table|form|hr)\b/i.test(c.answer);
|
|
24
|
+
const aInA = /<a\b[^>]*>[\s\S]{0,400}<a\b[^>]*>/i.test(c.answer);
|
|
25
|
+
const buttonInButton = /<button\b[^>]*>[\s\S]{0,400}<button\b[^>]*>/i.test(c.answer);
|
|
26
|
+
const formInForm = /<form\b[^>]*>[\s\S]{0,400}<form\b[^>]*>/i.test(c.answer);
|
|
27
|
+
return (divInP || aInA || buttonInButton || formInForm)
|
|
28
|
+
? [mk("major", "REACT_INVALID_HTML_NESTING", "Invalid HTML nesting (block in <p>, <a> in <a>, <button> in <button>, <form> in <form>) — browser auto-corrects the DOM, causing React hydration mismatch.", "Replace <p> with <div>/<span> when wrapping block content. Never nest <a> in <a> or <button> in <button>. HTML parser splits/reorders these, breaking SSR/RSC hydration.")]
|
|
29
|
+
: [];
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: "react.hydration-mismatch", domain: "domain",
|
|
34
|
+
description: "Math.random()/Date.now()/new Date() in component body — SSR hydration mismatch.",
|
|
35
|
+
appliesTo: isReact,
|
|
36
|
+
scan: c => /(?:const|let|var)\s+\w+\s*=\s*(?:Math\.random\(\)|Date\.now\(\)|new\s+Date\(\)|crypto\.randomUUID\(\))/i.test(c.answer)
|
|
37
|
+
&& !/\b(useEffect|useLayoutEffect|useMemo|useId\b|suppressHydrationWarning|'use client')\b/i.test(c.answer)
|
|
38
|
+
? [mk("major", "REACT_HYDRATION_MISMATCH", "Non-deterministic value (Math.random/Date/crypto.randomUUID) generated in component body causes server/client hydration mismatch.", "Move into useEffect (post-hydration), use useId() for stable IDs, or pass as a stable prop. Use suppressHydrationWarning only when the divergence is intentional.")]
|
|
39
|
+
: [],
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
// ── React Native ──────────────────────────────────────────────────────────
|
|
43
|
+
{
|
|
44
|
+
id: "rn.expo-managed-native-module-confusion", domain: "domain",
|
|
45
|
+
appliesTo: c => /\b(expo|managed workflow|app\.json)\b/i.test(T(c)),
|
|
46
|
+
scan: c => /\b(react-native-camera|react-native-fs|react-native-permissions|react-native-sensor|react-native-contacts)\b/i.test(T(c)) && !/\b(expo-camera|expo-file-system|expo-contacts|dev client|prebuild|EAS Build)\b/i.test(T(c))
|
|
47
|
+
? [mk("major", "RN_EXPO_MANAGED_NATIVE_MODULE_CONFUSION", "Bare native module imported in what appears to be an Expo managed workflow. App will white-screen on launch.", "Use Expo equivalents (expo-camera, expo-file-system) or migrate to dev-client/prebuild for bare native modules.")]
|
|
48
|
+
: [],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
id: "rn.typescript-prop-type-erasure", domain: "domain",
|
|
52
|
+
appliesTo: c => isRN(c) && /\b(TypeScript|tsx)\b/i.test(T(c)),
|
|
53
|
+
scan: c => /\b(?:interface\s+\w*Props\s*\{[\s\S]{0,300}:\s*any\b|props\s*:\s*any\b|PropTypes\.any)\b/i.test(T(c)) && /\b(NativeComponent|TurboModule|Codegen|native module|JSI)\b/i.test(T(c))
|
|
54
|
+
? [mk("major", "RN_TYPESCRIPT_PROP_TYPE_ERASURE", "Props typed as 'any' in React Native Codegen/JSI context — propagates into generated C++ stubs and disables type-safety across the JS/native boundary.", "Use concrete types only. Codegen-emitted stubs are derived from your TypeScript interfaces; 'any' produces untyped native bindings.")]
|
|
55
|
+
: [],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "rn.new-arch-codegen-drift", domain: "domain",
|
|
59
|
+
appliesTo: isRNNewArch,
|
|
60
|
+
scan: c => /\b(TurboModule|NativeModule.*Spec|interface.*Spec\b)\b/i.test(T(c)) && (/\bPromise\s*<\s*any\s*>|:\s*any\b.*(?:TurboModule|Spec)/i.test(T(c))) && !/\b(codegenConfig|supported Codegen types|String|Boolean|Number|Object|Array|null)\b/i.test(T(c))
|
|
61
|
+
? [mk("critical", "RN_NEW_ARCH_CODEGEN_DRIFT", "TurboModule/Codegen spec uses Promise<any> or unsupported union types — Codegen emits broken C++ stubs that crash at runtime ('NativeBinding is not a function').", "Use only Codegen-supported primitives (string, boolean, number, Object, Array, null). Validate with codegenConfig in package.json and run a native build.")]
|
|
62
|
+
: [],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: "rn.hermes-abi-mismatch", domain: "domain",
|
|
66
|
+
appliesTo: isRNNewArch,
|
|
67
|
+
scan: c => (/\b(JavaScriptCore|JSC|hermesEnabled\s*[:=]\s*false|:hermes_enabled\s*=>\s*false|jscEnabled\s*[:=]\s*true)\b/i.test(T(c))) && !/\b(Bundled Hermes|Hermes required|JSI built on Hermes|legacy app only)\b/i.test(T(c))
|
|
68
|
+
? [mk("critical", "RN_HERMES_ABI_MISMATCH", "JSC enabled or Hermes disabled on a New Architecture project — instant SIGABRT crash with no JS stack trace.", "New Architecture requires Hermes. Set hermesEnabled: true / :hermes_enabled => true and use Bundled Hermes so JSI ABI matches.")]
|
|
69
|
+
: [],
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: "rn.expo-updates-ota-broken", domain: "domain",
|
|
73
|
+
appliesTo: c => /\b(expo[-\s]updates|ota|over.?the.?air|Podfile)\b/i.test(T(c)),
|
|
74
|
+
scan: c => /\b(Podfile|upgrade helper)\b/i.test(T(c)) && (/\bremov(?:e|ed|ing)\s+(?::?hermes[_-]?enabled|hermes flag)\b/i.test(T(c)) || /\b:hermes_enabled\s*=>\s*false\b/i.test(T(c))) && !/\b(keep\s+:hermes_enabled|preserve\s+hermes|OTA\s+(?:requires|needs))\b/i.test(T(c))
|
|
75
|
+
? [mk("major", "RN_EXPO_UPDATES_OTA_BROKEN", "Expo Updates OTA silently fails when :hermes_enabled is removed/set false during Podfile upgrade.", "Keep `:hermes_enabled => true` in Podfile for Expo Updates OTA. App compiles and ships fine, but OTA fails in production.")]
|
|
76
|
+
: [],
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
id: "rn.navigation-version-mixing", domain: "domain",
|
|
80
|
+
appliesTo: isRN,
|
|
81
|
+
scan: c => {
|
|
82
|
+
const v5 = /\bcreateStackNavigator\s*\(/i.test(T(c));
|
|
83
|
+
const v6 = /\bcreateNativeStackNavigator\b/i.test(T(c));
|
|
84
|
+
const v7 = /\bcreateStaticNavigation\b/i.test(T(c));
|
|
85
|
+
return [v5, v6, v7].filter(Boolean).length >= 2
|
|
86
|
+
? [mk("major", "RN_NAVIGATION_VERSION_MIX", "React Navigation v5, v6, and/or v7 API patterns mixed — navigation.navigate() silently fails to pass params; deep linking breaks.", "Standardize on one version (v7 recommended for New Architecture / RN 0.85+).")]
|
|
87
|
+
: [];
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "rn.flatlist-perf", domain: "domain",
|
|
92
|
+
appliesTo: isRN,
|
|
93
|
+
scan: c => /<FlatList\b[\s\S]{0,300}data\s*=\s*\{/i.test(T(c)) && !/\b(keyExtractor|getItemLayout|removeClippedSubviews|windowSize)\b/i.test(T(c))
|
|
94
|
+
? [mk("warning", "RN_FLATLIST_PERF", "FlatList declared without keyExtractor / getItemLayout / removeClippedSubviews.", "Add keyExtractor, getItemLayout, removeClippedSubviews, windowSize; wrap renderItem in useCallback. Without these, 1000-item lists drop to single-digit FPS.")]
|
|
95
|
+
: [],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "rn.platform-os-asymmetry", domain: "domain",
|
|
99
|
+
appliesTo: isRN,
|
|
100
|
+
scan: c => {
|
|
101
|
+
const iosOnly = /\bPlatform\.OS\s*===?\s*['"]ios['"]/i.test(T(c)) && !/\bPlatform\.OS\s*===?\s*['"]android['"]/i.test(T(c));
|
|
102
|
+
const androidOnly = /\bPlatform\.OS\s*===?\s*['"]android['"]/i.test(T(c)) && !/\bPlatform\.OS\s*===?\s*['"]ios['"]/i.test(T(c));
|
|
103
|
+
return (iosOnly || androidOnly)
|
|
104
|
+
? [mk("warning", "RN_PLATFORM_OS_ASYMMETRY", `Platform.OS check covers only ${iosOnly ? "'ios'" : "'android'"} — the other platform has no handler and will silently misbehave.`, "Add the missing platform branch, or use Platform.select({ ios: ..., android: ..., default: ... }) for exhaustive coverage.")]
|
|
105
|
+
: [];
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "rn.no-error-boundary", domain: "domain",
|
|
110
|
+
appliesTo: isRN,
|
|
111
|
+
scan: c => /\b(production|app entry|main app|RootNavigator|<NavigationContainer)\b/i.test(T(c)) && /\b(component|export default)\b/i.test(T(c)) && !/\b(ErrorBoundary|componentDidCatch|getDerivedStateFromError)\b/i.test(T(c))
|
|
112
|
+
? [mk("warning", "RN_NO_ERROR_BOUNDARY", "Production RN app root without ErrorBoundary — a single render error white-screens everything.", "Wrap top-level navigators in an ErrorBoundary so child render errors don't kill the whole app.")]
|
|
113
|
+
: [],
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "rn.useeffect-no-cleanup", domain: "domain",
|
|
117
|
+
appliesTo: isRN,
|
|
118
|
+
scan: c => /\b(AppState|Keyboard|NetInfo|DeviceEventEmitter|Linking)\.add(?:EventListener|Listener)\b/i.test(T(c)) && !/\bremove(?:EventListener|Listener|All)\b/i.test(T(c)) && !/\b\.remove\(\)/i.test(T(c))
|
|
119
|
+
? [mk("major", "RN_USEEFFECT_LEAK", "Native event emitter subscribed without cleanup (mobile memory leak).", "Return a cleanup function from useEffect that calls subscription.remove() or removeEventListener; on mobile these accumulate across hours-long sessions.")]
|
|
120
|
+
: [],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
id: "rn.stylesheet-absolutefillobject-removed", domain: "domain",
|
|
124
|
+
description: "RN 0.85 removed StyleSheet.absoluteFillObject (web-verified: reactnative.dev/blog/2026/04/07).",
|
|
125
|
+
appliesTo: isRN,
|
|
126
|
+
scan: c => /\bStyleSheet\.absoluteFillObject\b/i.test(c.answer)
|
|
127
|
+
? [mk("major", "RN_ABSOLUTEFILLOBJECT_REMOVED", "StyleSheet.absoluteFillObject was removed in React Native 0.85 (April 7, 2026). Code targeting current RN will produce TypeScript errors and runtime breakage.", "Replace with StyleSheet.absoluteFill: `{ ...StyleSheet.absoluteFill }`. Or inline: `{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }`.")]
|
|
128
|
+
: [],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "rn.pressable-hidden-activity-leak", domain: "domain",
|
|
132
|
+
description: "RN 0.85: Pressable event listeners no longer auto-unmount in hidden Activity (web-verified).",
|
|
133
|
+
appliesTo: isRN,
|
|
134
|
+
scan: c => /<Pressable\b[\s\S]{0,300}\b(?:onPress|onLongPress|onPressIn|onPressOut)\s*=\s*\{/i.test(T(c))
|
|
135
|
+
&& /\b(?:hidden|backgrounded|Activity|conditionally\s+rendered|\bvisible\s*&&|\bif\s+\([^)]+\)\s*\{[\s\S]{0,100}Pressable)\b/i.test(T(c))
|
|
136
|
+
&& !/\b(?:useEffect.*return|remove(?:EventListener|Listener)?|cleanup|unsubscribe)\b/i.test(T(c))
|
|
137
|
+
? [mk("warning", "RN_PRESSABLE_HIDDEN_ACTIVITY_LEAK", "React Native 0.85 changed behavior: Pressable event listeners no longer auto-unmount when parent Activity is hidden. Manual cleanup is now required.", "Per RN 0.85 release notes: when using Pressable inside conditionally rendered/hidden views, manually remove event listeners during component unmount via useEffect cleanup return.")]
|
|
138
|
+
: [],
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
id: "rn.reanimated-runonjs-deprecated", domain: "domain",
|
|
142
|
+
description: "Reanimated 4: runOnJS → scheduleOnRN (web-verified: docs.swmansion.com migration-from-3.x).",
|
|
143
|
+
appliesTo: c => /\b(react-native-reanimated|react-native-worklets|Reanimated\s*4|runOnJS)\b/i.test(T(c)),
|
|
144
|
+
scan: c => /\bimport\s+\{[^}]*\brunOnJS\b[^}]*\}\s+from\s+['"]react-native-reanimated['"]/i.test(T(c))
|
|
145
|
+
&& !/\b(scheduleOnRN|react-native-worklets|migrating-from-3|deprecated)\b/i.test(T(c))
|
|
146
|
+
? [mk("warning", "RN_REANIMATED_RUNONJS_DEPRECATED", "runOnJS from 'react-native-reanimated' is deprecated as of Reanimated 4 and will be removed in the next major release.", "Migrate: `import { scheduleOnRN } from 'react-native-worklets'`. Usage: `scheduleOnRN(fn, value)` (inline args) instead of `runOnJS(fn)(value)` (curried). Per docs.swmansion.com/react-native-reanimated/docs/guides/migration-from-3.x.")]
|
|
147
|
+
: [],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "rn.keyboard-avoiding-android", domain: "domain",
|
|
151
|
+
appliesTo: c => /\b(react[-\s]?native|KeyboardAvoidingView)\b/i.test(T(c)),
|
|
152
|
+
scan: c => /<KeyboardAvoidingView\b[\s\S]{0,200}\bbehavior\s*=\s*[{(]?\s*['"]padding['"]/i.test(c.answer) && !/\bPlatform\.(?:OS|select)\b/i.test(c.answer)
|
|
153
|
+
? [mk("major", "RN_KEYBOARD_AVOIDING_ANDROID_BROKEN", "KeyboardAvoidingView behavior='padding' is hardcoded — works on iOS but causes the keyboard to overlap content on Android, where 'height' is needed.", "Branch on platform: `behavior={Platform.OS === 'ios' ? 'padding' : 'height'}`.")]
|
|
154
|
+
: [],
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// ── Three.js ──────────────────────────────────────────────────────────────
|
|
158
|
+
{
|
|
159
|
+
id: "three.dispose-partial-with-texture", domain: "domain",
|
|
160
|
+
description: "Three.js disposes geometry/material but textures/renderTargets/composers leak.",
|
|
161
|
+
appliesTo: c => /\b(THREE\.|three\.js|@react-three|drei|r3f)\b/i.test(T(c)),
|
|
162
|
+
scan: c => {
|
|
163
|
+
const partialDispose = /\b(?:geometry|material)\.dispose\s*\(\s*\)/i.test(c.answer);
|
|
164
|
+
const hasTexturedResource = /\b(?:Texture|EnvMap|CubeTexture|WebGLRenderTarget|EffectComposer|OrbitControls|composer\.|texture\.|renderTarget\.)\b/i.test(c.answer);
|
|
165
|
+
const disposesTextured = /\b(?:texture|renderTarget|composer|controls|envMap)\.dispose\s*\(\s*\)/i.test(c.answer);
|
|
166
|
+
return partialDispose && hasTexturedResource && !disposesTextured
|
|
167
|
+
? [mk("major", "THREE_DISPOSE_PARTIAL_WITH_TEXTURE", "Three.js cleanup disposes geometry/material but textures, render targets, composers, or controls leak GPU memory.", "Per Three.js docs: 'disposal of a material has no effect on textures'. Cascade ALL dispose calls: texture.dispose(), renderTarget.dispose(), composer.dispose(), controls.dispose().")]
|
|
168
|
+
: [];
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
|
|
172
|
+
// ── WebGL ─────────────────────────────────────────────────────────────────
|
|
173
|
+
{ id: "webgl.context-lost-handler", domain: "domain", appliesTo: isWebGL, scan: c => /\b(getContext\(['"]webgl['"]\)|new THREE\.WebGLRenderer)\b/i.test(T(c)) && !/\bwebglcontextlost\b/i.test(T(c)) ? [mk("major", "WEBGL_NO_CTX_LOST_HANDLER", "WebGL context created without 'webglcontextlost'/'webglcontextrestored' handlers.", "Add event listeners for context loss/restore. Context can be lost (GPU reset, background tab, memory pressure).")] : [] },
|
|
174
|
+
{ id: "webgl.delete-shader-program", domain: "domain", appliesTo: isWebGL, scan: c => /\bgl\.(?:createShader|createProgram)\(/i.test(T(c)) && !/\bgl\.delete(?:Shader|Program)\(/i.test(T(c)) ? [mk("major", "WEBGL_SHADER_LEAK", "gl.createShader/createProgram called without matching gl.deleteShader/deleteProgram.", "Call gl.deleteShader() after gl.linkProgram() and gl.deleteProgram() on cleanup. WebGL handles are not garbage collected.")] : [] },
|
|
175
|
+
{ id: "webgl.precision-highp", domain: "domain", appliesTo: isWebGL, scan: c => /precision\s+highp\s+float/i.test(T(c)) && /\bfragment\b/i.test(T(c)) && !/\b(mediump|GL_FRAGMENT_PRECISION_HIGH|getShaderPrecisionFormat)\b/i.test(T(c)) ? [mk("warning", "WEBGL_PRECISION_HIGHP", "precision highp float in fragment shader without checking mobile GPU support.", "Many Adreno/Mali GPUs silently downgrade highp to mediump. Query gl.getShaderPrecisionFormat() or use #ifdef GL_FRAGMENT_PRECISION_HIGH.")] : [] },
|
|
176
|
+
{ id: "webgl.npot-texture", domain: "domain", appliesTo: isWebGL, scan: c => /\bgl\.generateMipmap\(/i.test(T(c)) && /\bwebgl(?!2)\b/i.test(T(c)) && !/\b(isPowerOf2|POT|NPOT|WebGL2|gl\.TEXTURE_WRAP_S\s*=\s*gl\.CLAMP_TO_EDGE)\b/i.test(T(c)) ? [mk("warning", "WEBGL_NPOT", "gl.generateMipmap() on WebGL1 without verifying texture dimensions are power-of-two.", "On WebGL1, NPOT textures with generateMipmap silently render solid black. Resize to POT, use CLAMP_TO_EDGE + LINEAR, or upgrade to WebGL2.")] : [] },
|
|
177
|
+
{ id: "webgl.instanced-no-check", domain: "domain", appliesTo: isWebGL, scan: c => /\b(drawArraysInstanced|drawElementsInstanced|ANGLE_instanced_arrays)\b/i.test(T(c)) && !/\bgetExtension\(['"]ANGLE_instanced_arrays['"]\)|isWebGL2|version 2/i.test(T(c)) ? [mk("warning", "WEBGL_INSTANCED_UNCHECKED", "Instanced rendering used without support check.", "On WebGL1 query gl.getExtension('ANGLE_instanced_arrays'); on WebGL2 verify context creation. Without checks, pipeline breaks silently on unsupported hardware.")] : [] },
|
|
178
|
+
{ id: "webgl.get-error-missing", domain: "domain", appliesTo: isWebGL, scan: c => /\bgl\.(?:drawArrays|drawElements|texImage2D|bufferData|uniform)/i.test(T(c)) && !/\bgl\.getError\(\)/i.test(T(c)) ? [mk("warning", "WEBGL_NO_GET_ERROR", "WebGL draw/buffer/uniform calls without periodic gl.getError() polling.", "WebGL fails silently — errors accumulate and subsequent calls 'succeed' but produce no output. Poll gl.getError() in dev builds.")] : [] },
|
|
179
|
+
{ id: "webgl.shader-injection-flow", domain: "domain", appliesTo: isWebGL, scan: c => /\bgl\.shaderSource\s*\([\s\S]{0,80}\b(input|user|params|body|query|untrusted|req\.body|fetch|prompt)\b/i.test(T(c)) && !/\b(sanitize|ANGLE|validate|allowlist|approved)\b/i.test(T(c)) ? [mk("critical", "WEBGL_SHADER_INJECTION", "User input potentially flows into shader source compilation — driver-level DoS/RCE risk.", "Never let untrusted input reach gl.shaderSource(). Use pre-built shader node graphs.")] : [] },
|
|
180
|
+
{ id: "webgl.shader-silent", domain: "domain", appliesTo: isWebGL, scan: c => /\bgl\.compileShader\s*\(/i.test(T(c)) && !/\b(getShaderInfoLog|COMPILE_STATUS|getShaderParameter|getError)\b/i.test(T(c)) ? [mk("major", "WEBGL_SHADER_SILENT", "Shader compiled without checking COMPILE_STATUS / getShaderInfoLog — WebGL returns empty error string on many drivers, producing a black screen with no console signal.", "After compileShader, check `gl.getShaderParameter(shader, gl.COMPILE_STATUS)` and log `gl.getShaderInfoLog(shader)`.")] : [] },
|
|
181
|
+
{ id: "webgpu.destroy-missing", domain: "domain", appliesTo: isWebGL, scan: c => /\b(createBuffer|createTexture)\b/i.test(T(c)) && !/\b(destroy|dispose)\b/i.test(T(c)) ? [mk("critical", "WEBGPU_DESTROY_MISSING", "WebGPU buffers created without explicit .destroy() calls.", "GPU memory is not garbage collected; call .destroy() on every buffer.")] : [] },
|
|
182
|
+
];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistics Advanced Flaw Pack (STATISTICS_ADVANCED_FLAWS).
|
|
3
|
+
* Minimal set to satisfy selftest assertions for Source-Activity, Linkage, CATE, Position Bias, Neuro double-dip, Fairness impossibility.
|
|
4
|
+
*/
|
|
5
|
+
import { type FlawDetector, type FlawIssue, type ScanContext } from "../flaw-registry";
|
|
6
|
+
|
|
7
|
+
const mk = (s: FlawIssue["severity"], c: string, m: string, r: string): FlawIssue => ({ severity: s, code: c, message: m, remediation: r });
|
|
8
|
+
|
|
9
|
+
export const STATISTICS_ADVANCED_FLAWS: FlawDetector[] = [
|
|
10
|
+
{
|
|
11
|
+
id: "stat.source-activity",
|
|
12
|
+
domain: "statistics-advanced",
|
|
13
|
+
appliesTo: (c) => /forensic DNA|likelihood ratio/i.test(c.prompt),
|
|
14
|
+
scan: (c) => /likelihood ratio.*proves he touched/i.test(c.answer) ? [mk("major", "STAT_SOURCE_ACTIVITY", "Source probability conflated with activity probability.", "Distinguish P(source) from P(activity | source).")] : [],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "stat.linkage-perfect",
|
|
18
|
+
domain: "statistics-advanced",
|
|
19
|
+
appliesTo: (c) => /record linkage/i.test(c.prompt),
|
|
20
|
+
scan: (c) => /linked records.*analyzed the matched dataset as final/i.test(c.answer) ? [mk("major", "STAT_LINKAGE_PERFECT", "Record linkage treated as error-free.", "Account for linkage error / false-match rate in downstream inference.")] : [],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "stat.cate-overlap-missing",
|
|
24
|
+
domain: "statistics-advanced",
|
|
25
|
+
appliesTo: (c) => /HTE|causal forest/i.test(c.prompt),
|
|
26
|
+
scan: (c) => /strong CATE variation/i.test(c.answer) && !/overlap|positivity|common support/i.test(c.answer) ? [mk("major", "STAT_CATE_OVERLAP_MISSING", "CATE reported without positivity/overlap check.", "Verify common support before interpreting heterogeneous effects.")] : [],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "stat.position-bias",
|
|
30
|
+
domain: "statistics-advanced",
|
|
31
|
+
appliesTo: (c) => /recommender|CTR|NDCG/i.test(c.prompt),
|
|
32
|
+
scan: (c) => /evaluated CTR and NDCG on clicks/i.test(c.answer) ? [mk("major", "STAT_POSITION_BIAS", "CTR/NDCG measured without position-bias correction.", "Use IPS or randomization to debias position effects.")] : [],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "stat.neuro-double-dip",
|
|
36
|
+
domain: "statistics-advanced",
|
|
37
|
+
appliesTo: (c) => /fMRI|voxels/i.test(c.prompt),
|
|
38
|
+
scan: (c) => /selected voxels by the contrast.*tested them on the same contrast/i.test(c.answer) ? [mk("critical", "STAT_NEURO_DOUBLE_DIP", "Circular analysis (double-dipping) in neuroimaging.", "Use independent data for selection and inference, or cross-validation.")] : [],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "stat.fairness-impossibility",
|
|
42
|
+
domain: "statistics-advanced",
|
|
43
|
+
appliesTo: (c) => /fairness audit/i.test(c.prompt),
|
|
44
|
+
scan: (c) => /perfect calibration and equalized odds/i.test(c.answer) ? [mk("major", "STAT_FAIRNESS_IMPOSSIBILITY", "Multiple incompatible fairness criteria claimed simultaneously.", "Cite impossibility theorems; choose and justify one primary criterion.")] : [],
|
|
45
|
+
},
|
|
46
|
+
];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistics Flaw Pack (STATISTICS_FLAWS).
|
|
3
|
+
* Minimal set to satisfy selftest assertions for P-value, significance, and correlation.
|
|
4
|
+
*/
|
|
5
|
+
import { type FlawDetector, type FlawIssue, type ScanContext } from "../flaw-registry";
|
|
6
|
+
|
|
7
|
+
const mk = (s: FlawIssue["severity"], c: string, m: string, r: string): FlawIssue => ({ severity: s, code: c, message: m, remediation: r });
|
|
8
|
+
|
|
9
|
+
export const STATISTICS_FLAWS: FlawDetector[] = [
|
|
10
|
+
{
|
|
11
|
+
id: "stat.p-as-prob-null",
|
|
12
|
+
domain: "statistics",
|
|
13
|
+
appliesTo: (c) => /t-test|regression|chi-square/i.test(c.prompt),
|
|
14
|
+
scan: (c) => /p-?value.*probability.*null/i.test(c.answer) ? [mk("major", "STAT_P_AS_PROB_NULL", "P-value misinterpreted as P(null is true).", "Report the actual definition: P(data | null).")] : [],
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "stat.threshold-worship",
|
|
18
|
+
domain: "statistics",
|
|
19
|
+
appliesTo: (c) => /regression|trial/i.test(c.prompt),
|
|
20
|
+
scan: (c) => /marginally significant|p\s*=\s*0\.0[5-9]/i.test(c.answer) ? [mk("warning", "STAT_THRESHOLD_WORSHIP", "Marginal significance treated as meaningful.", "Report exact p-value and effect size; avoid dichotomous language.")] : [],
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: "stat.nonsig-as-no-effect",
|
|
24
|
+
domain: "statistics",
|
|
25
|
+
appliesTo: (c) => /trial|study/i.test(c.prompt),
|
|
26
|
+
scan: (c) => /p\s*>\s*0\.05.*no effect/i.test(c.answer) ? [mk("major", "STAT_NONSIG_AS_NO_EFFECT", "Non-significant result interpreted as 'no effect'.", "Absence of evidence is not evidence of absence; report confidence intervals.")] : [],
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "stat.p-equals-zero",
|
|
30
|
+
domain: "statistics",
|
|
31
|
+
appliesTo: (c) => /chi-square|anova/i.test(c.prompt),
|
|
32
|
+
scan: (c) => /p\s*=\s*0\.000/i.test(c.answer) ? [mk("warning", "STAT_P_EQUALS_ZERO", "P-value reported as exactly 0.000.", "Report p < 0.001 or the software's minimum representable value.")] : [],
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "stat.sd-se-confusion",
|
|
36
|
+
domain: "statistics",
|
|
37
|
+
appliesTo: (c) => /trial outcome|mean/i.test(c.prompt),
|
|
38
|
+
scan: (c) => /±\s*[\d.]+\s*SEM/i.test(c.answer) ? [mk("warning", "STAT_SD_SE_CONFUSION", "SEM reported as if it were SD.", "Clarify whether the ± value is SD or SEM.")] : [],
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: "stat.corr-causation",
|
|
42
|
+
domain: "statistics",
|
|
43
|
+
appliesTo: (c) => /cohort study|correlation/i.test(c.prompt),
|
|
44
|
+
scan: (c) => {
|
|
45
|
+
if (/correlation does not imply causation/i.test(c.answer)) return [];
|
|
46
|
+
return /correlated.*therefore causes/i.test(c.answer) ? [mk("major", "STAT_CORR_CAUSATION", "Correlation presented as causation without disclaimer.", "Add explicit disclaimer or causal identification strategy.")] : [];
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: "stat.dichotomania",
|
|
51
|
+
domain: "statistics",
|
|
52
|
+
appliesTo: (c) => /analysis/i.test(c.prompt),
|
|
53
|
+
scan: (c) => /dichotomiz/i.test(c.answer) ? [mk("warning", "STAT_DICHOTOMANIA", "Continuous variable unnecessarily dichotomized.", "Retain continuous form or justify cut-point with pre-specified rationale.")] : [],
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
id: "stat.contamination",
|
|
57
|
+
domain: "statistics",
|
|
58
|
+
appliesTo: (c) => /LLM eval|benchmark/i.test(c.prompt),
|
|
59
|
+
scan: (c) => /SOTA on the MMLU/i.test(c.answer) ? [mk("warning", "STAT_CONTAMINATION", "Benchmark scores cited without contamination audit.", "Disclose training-data overlap checks.")] : [],
|
|
60
|
+
},
|
|
61
|
+
];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EngineConfig, SPRTConfig } from "./types";
|
|
2
|
+
|
|
3
|
+
export function sprtUpper(cfg: SPRTConfig): number {
|
|
4
|
+
return Math.log((1 - cfg.beta) / cfg.alpha);
|
|
5
|
+
}
|
|
6
|
+
export function sprtLower(cfg: SPRTConfig): number {
|
|
7
|
+
return Math.log(cfg.beta / (1 - cfg.alpha));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const defaultConfig = (overrides: Partial<EngineConfig> = {}): EngineConfig => ({
|
|
11
|
+
sprt: { alpha: 0.05, beta: 0.10, minSupportingSources: 2 },
|
|
12
|
+
evidence: { llrCap: 2.2, maxPriorLogOdds: 1.5, couplingCap: 0.9 },
|
|
13
|
+
graph: { restartProb: 0.15, maxIters: 100, tolerance: 1e-8, maxNodes: 2000 },
|
|
14
|
+
collapseMargin: 0.20,
|
|
15
|
+
pruneFloor: 0.02,
|
|
16
|
+
maxTokens: 200_000,
|
|
17
|
+
maxSeconds: 1800,
|
|
18
|
+
seed: 42,
|
|
19
|
+
...overrides,
|
|
20
|
+
});
|