progmune-runtime 2.1.6 → 3.2.1
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/README.md +119 -461
- package/dist/ablation-study.js +144 -0
- package/dist/ablation-study.test.js +18 -0
- package/dist/action-runtime.js +3 -1
- package/dist/active-learning.js +211 -0
- package/dist/analytics.js +139 -0
- package/dist/asset-factory.js +309 -0
- package/dist/asset-growth.js +244 -0
- package/dist/asset-promotion.js +382 -0
- package/dist/asset-quality.js +550 -0
- package/dist/audit/business-translator.js +285 -0
- package/dist/audit/cli.js +66 -0
- package/dist/audit/formatters/html.js +379 -0
- package/dist/audit/formatters/json.js +11 -0
- package/dist/audit/formatters/markdown.js +192 -0
- package/dist/audit/formatters/terminal.js +189 -0
- package/dist/audit/index.js +25 -0
- package/dist/audit/report-builder.js +318 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit.js +3 -3
- package/dist/auto-benchmark-generator.js +137 -0
- package/dist/auto-benchmark-generator.test.js +45 -0
- package/dist/auto-protocol-synthesizer.js +362 -0
- package/dist/auto-protocol-synthesizer.test.js +82 -0
- package/dist/autonomous-patch.js +175 -0
- package/dist/autonomous-patch.test.js +128 -0
- package/dist/badge/badge-server.js +98 -0
- package/dist/behavior-miner.js +442 -0
- package/dist/belief-layer.js +475 -0
- package/dist/benchmark-count.js +5 -0
- package/dist/benchmark-generator.js +211 -0
- package/dist/benchmark-harness.js +201 -0
- package/dist/benchmark-pass-rate.js +7 -0
- package/dist/benchmark-report.js +8 -3
- package/dist/benchmark-save.js +14 -1
- package/dist/bootstrap-validation.js +197 -0
- package/dist/bootstrap-validation.test.js +51 -0
- package/dist/branch-ledger.js +1 -1
- package/dist/capability-gap.js +130 -0
- package/dist/certify-html.js +351 -0
- package/dist/certify.js +326 -0
- package/dist/check.js +4 -4
- package/dist/compliance-miner.js +447 -0
- package/dist/continuous-benchmark.js +194 -0
- package/dist/continuous-benchmark.test.js +116 -0
- package/dist/corpus-stats.js +173 -0
- package/dist/counterfactual-engine.js +288 -0
- package/dist/coverage-dashboard.js +109 -0
- package/dist/coverage-system.test.js +205 -0
- package/dist/cross-repo-precision.js +352 -0
- package/dist/cve-benchmark.js +180 -0
- package/dist/cve-benchmark.test.js +28 -0
- package/dist/cve-collector.js +73 -0
- package/dist/data-quality.js +141 -0
- package/dist/decision-engine.js +388 -0
- package/dist/derive-metadata.js +250 -0
- package/dist/difficulty-active.test.js +198 -0
- package/dist/difficulty-map.js +244 -0
- package/dist/discovery-analytics.js +125 -0
- package/dist/discovery-model.js +149 -0
- package/dist/discovery-optimize.test.js +199 -0
- package/dist/discovery-trace.js +276 -0
- package/dist/discovery-trace.test.js +97 -0
- package/dist/emitter.js +83 -1
- package/dist/enterprise-dashboard.js +405 -0
- package/dist/eval-hardening.js +297 -0
- package/dist/eval-hardening.test.js +85 -0
- package/dist/evaluation-campaign.js +359 -0
- package/dist/evaluation-campaign.test.js +181 -0
- package/dist/evidence-growth.js +143 -0
- package/dist/evidence-repository.js +209 -0
- package/dist/evidence-system.js +441 -0
- package/dist/execute.js +15 -7
- package/dist/experimental/software-physics.js +291 -0
- package/dist/experimental/state-inference.js +516 -0
- package/dist/experimental/unsupervised-physics.js +230 -0
- package/dist/extract-ir-python.js +54 -7
- package/dist/extract-ir.js +376 -12
- package/dist/failure-collector.js +2 -2
- package/dist/failure-corpus.js +322 -9
- package/dist/feedback.js +16 -5
- package/dist/feedback.test.js +49 -0
- package/dist/file-lock.js +1 -1
- package/dist/flywheel-batch.js +292 -0
- package/dist/frameworks/express-cli.js +237 -0
- package/dist/frameworks/express-detector.js +445 -0
- package/dist/frameworks/express-detector.test.js +206 -0
- package/dist/frameworks/index.js +30 -0
- package/dist/frameworks/nestjs-detector.js +302 -0
- package/dist/frameworks/trpc-detector.js +161 -0
- package/dist/frameworks/version-awareness.js +179 -0
- package/dist/function-synonyms.js +164 -0
- package/dist/function-synonyms.test.js +68 -0
- package/dist/generalization.test.js +352 -0
- package/dist/goal-annotator.js +113 -0
- package/dist/goal-planner.js +563 -0
- package/dist/gold-cve.js +164 -0
- package/dist/gold-cve.test.js +104 -0
- package/dist/gold-quality.js +206 -0
- package/dist/gold-tiers.js +241 -0
- package/dist/governance-dashboard.js +327 -0
- package/dist/graph-viz.js +240 -0
- package/dist/guided-frontier.js +195 -0
- package/dist/hierarchical-planner.js +148 -0
- package/dist/identifier-parser.js +260 -0
- package/dist/immune-metrics.js +93 -0
- package/dist/immune-receiver.js +158 -0
- package/dist/immune-reporter.js +1 -1
- package/dist/improvement-orchestrator.js +206 -0
- package/dist/inject-p0-vocabulary.js +300 -0
- package/dist/intent-parser.js +218 -0
- package/dist/invariant-algebra.js +476 -0
- package/dist/invariant-calculus.js +533 -0
- package/dist/ir-utils.js +70 -0
- package/dist/ir-utils.test.js +50 -0
- package/dist/knowledge-api.js +312 -0
- package/dist/knowledge-evolution.js +452 -0
- package/dist/knowledge-explorer.js +506 -0
- package/dist/knowledge-flywheel.js +274 -0
- package/dist/knowledge-governance.js +338 -0
- package/dist/knowledge-governance.test.js +150 -0
- package/dist/knowledge-graph.js +181 -0
- package/dist/knowledge-guided-synth.js +246 -0
- package/dist/knowledge-loop.test.js +77 -0
- package/dist/knowledge-object.js +316 -0
- package/dist/knowledge-package.js +98 -0
- package/dist/kpi-dashboard.js +561 -0
- package/dist/l3-cross-function.js +280 -0
- package/dist/learning-ranker.js +148 -0
- package/dist/learning-ranker.test.js +291 -0
- package/dist/ledger/accountability.js +322 -0
- package/dist/ledger/chain-builder.js +185 -0
- package/dist/ledger/cli.js +222 -0
- package/dist/ledger/index.js +13 -0
- package/dist/ledger/signatures.js +193 -0
- package/dist/ledger/types.js +9 -0
- package/dist/llm.js +74 -3
- package/dist/load-benchmarks.js +8 -3
- package/dist/logger.js +66 -0
- package/dist/logger.test.js +37 -0
- package/dist/logistic-reward.js +339 -0
- package/dist/logistic-reward.test.js +180 -0
- package/dist/macro-graph.js +193 -0
- package/dist/macro-repair.js +183 -0
- package/dist/mcp-server.mjs +1202 -483
- package/dist/memory-layer.js +42 -5
- package/dist/multi-repo-precision.js +422 -0
- package/dist/name-free-protocol.js +425 -0
- package/dist/name-free-protocol.test.js +170 -0
- package/dist/name-scrambling.js +138 -0
- package/dist/name-scrambling.test.js +16 -0
- package/dist/p3-observability.test.js +281 -0
- package/dist/p5-orchestrator.test.js +225 -0
- package/dist/pairwise-preference.js +294 -0
- package/dist/pairwise-preference.test.js +140 -0
- package/dist/planner-constraints.js +104 -0
- package/dist/planner-prompts.js +155 -0
- package/dist/planner-telemetry.js +415 -0
- package/dist/planner-trace.js +214 -0
- package/dist/planner.js +162 -167
- package/dist/plsb/artifact.js +116 -0
- package/dist/plsb/cli.js +71 -0
- package/dist/plsb/index.js +19 -0
- package/dist/plsb/leaderboard.js +249 -0
- package/dist/plsb/report-md.js +156 -0
- package/dist/plsb/schema.js +179 -0
- package/dist/plsb-benchmark.js +284 -0
- package/dist/plsb-benchmark.test.js +119 -0
- package/dist/policy/cli.js +134 -0
- package/dist/policy/engine.js +333 -0
- package/dist/policy/index.js +12 -0
- package/dist/policy/types.js +59 -0
- package/dist/policy-miner.js +505 -0
- package/dist/precision-analyze.js +229 -0
- package/dist/precision-benchmark.js +147 -0
- package/dist/precision-label-c.js +134 -0
- package/dist/precision-label.js +193 -0
- package/dist/precision-report-c.js +149 -0
- package/dist/precision-report.js +246 -0
- package/dist/progmune-status.js +108 -0
- package/dist/proof-engine.js +479 -0
- package/dist/proof-provenance.js +315 -0
- package/dist/protocol-coverage.js +294 -0
- package/dist/protocol-detector.js +1189 -0
- package/dist/protocol-embedding-expanded.js +297 -0
- package/dist/protocol-embedding-expanded.test.js +97 -0
- package/dist/protocol-embedding.js +195 -0
- package/dist/protocol-embedding.test.js +82 -0
- package/dist/protocol-extractor-v2.js +354 -0
- package/dist/protocol-extractor-v2.test.js +140 -0
- package/dist/protocol-extractor.js +310 -0
- package/dist/protocol-extractor.test.js +113 -0
- package/dist/protocol-foundation.js +322 -0
- package/dist/protocol-foundation.test.js +163 -0
- package/dist/protocol-frontier.js +243 -0
- package/dist/protocol-frontier.test.js +92 -0
- package/dist/protocol-gap-analyzer.js +228 -0
- package/dist/protocol-gap-analyzer.test.js +49 -0
- package/dist/protocol-invariants.js +276 -0
- package/dist/protocol-invariants.test.js +111 -0
- package/dist/protocol-knowledge.js +464 -0
- package/dist/protocol-miner.js +343 -0
- package/dist/protocol-mining.js +207 -0
- package/dist/protocol-mining.test.js +37 -0
- package/dist/protocol-registry.js +1 -1
- package/dist/protocol-security-benchmark.js +222 -0
- package/dist/protocol-vulnerability.js +257 -0
- package/dist/protocol-vulnerability.test.js +60 -0
- package/dist/python-benchmark.js +120 -0
- package/dist/python-emitter.js +163 -45
- package/dist/python-protocol-extractor.js +187 -0
- package/dist/python-protocol-extractor.test.js +116 -0
- package/dist/realworld-benchmark.js +646 -0
- package/dist/realworld-benchmark.test.js +36 -0
- package/dist/repair-arch.test.js +411 -0
- package/dist/repair-evolution.test.js +454 -0
- package/dist/repair-executor.js +719 -0
- package/dist/repair-proposal.js +4 -4
- package/dist/repair-ranker.js +141 -0
- package/dist/repair-strategies.js +419 -0
- package/dist/repair-taxonomy.js +234 -0
- package/dist/repair-types.js +12 -0
- package/dist/repo-evaluator.js +250 -0
- package/dist/repo-evaluator.test.js +128 -0
- package/dist/resource-abstraction.js +242 -0
- package/dist/resource-detector.js +211 -0
- package/dist/result.test.js +43 -0
- package/dist/reward-system.js +411 -0
- package/dist/reward-system.test.js +175 -0
- package/dist/risk-model.js +215 -0
- package/dist/rule-miner.js +234 -7
- package/dist/rule-specificity.js +254 -0
- package/dist/runtime-types.js +27 -0
- package/dist/scaffold.js +208 -0
- package/dist/scale-collector.test.js +101 -0
- package/dist/scale-trajectory-collector.js +128 -0
- package/dist/sdk.js +250 -0
- package/dist/search-planner.js +4 -41
- package/dist/semantic-snapshot.js +1 -1
- package/dist/semantic-topology.js +121 -0
- package/dist/semantic-trace.js +310 -317
- package/dist/sequence-extractor.js +343 -0
- package/dist/skill-library.js +245 -0
- package/dist/skill-planner.test.js +189 -0
- package/dist/software-physics.js +291 -0
- package/dist/software-physics.test.js +81 -0
- package/dist/ssg-precision.js +478 -0
- package/dist/ssg-validator.js +71 -21
- package/dist/state-inference-doubleblind.test.js +160 -0
- package/dist/state-inference.js +516 -0
- package/dist/state-inference.test.js +115 -0
- package/dist/state-machine-fingerprint.js +345 -0
- package/dist/state-machine-fingerprint.test.js +120 -0
- package/dist/state-miner.js +386 -0
- package/dist/state-name-inference.js +213 -0
- package/dist/state-name-inference.test.js +69 -0
- package/dist/strategy-planner.js +262 -96
- package/dist/strategy-planner.test.js +135 -0
- package/dist/telemetry-analytics.test.js +402 -0
- package/dist/terminal-format.js +68 -0
- package/dist/terminal-format.test.js +83 -0
- package/dist/topology-factory.js +196 -0
- package/dist/topology-representation.js +242 -0
- package/dist/topology-representation.test.js +27 -0
- package/dist/trajectory-augmentation.js +254 -0
- package/dist/trajectory-augmentation.test.js +63 -0
- package/dist/trajectory-corpus.js +440 -0
- package/dist/trajectory-corpus.test.js +32 -0
- package/dist/trajectory-feedback.test.js +116 -0
- package/dist/transition-synthesizer.js +286 -0
- package/dist/transition-synthesizer.test.js +123 -0
- package/dist/trust/api-semantic-mapper.js +809 -0
- package/dist/trust/call-graph-propagator.js +225 -0
- package/dist/trust/cli.js +122 -0
- package/dist/trust/compliance-scorer.js +283 -0
- package/dist/trust/confidence-calculator.js +261 -0
- package/dist/trust/engine.js +1145 -0
- package/dist/trust/explainability.js +85 -0
- package/dist/trust/formatters/ci.js +42 -0
- package/dist/trust/formatters/json.js +11 -0
- package/dist/trust/formatters/terminal.js +152 -0
- package/dist/trust/index.js +39 -0
- package/dist/trust/phase1-verify.js +171 -0
- package/dist/trust/protocol-domain-validator.js +697 -0
- package/dist/trust/score-calculator.js +282 -0
- package/dist/trust/ssg-bridge.js +641 -0
- package/dist/trust/ssg-bridge.test.js +269 -0
- package/dist/trust/types.js +67 -0
- package/dist/trust/violation-trace.js +335 -0
- package/dist/trust-api.js +179 -0
- package/dist/trust-calibration.js +279 -0
- package/dist/unknown-protocol-discovery.js +339 -0
- package/dist/unknown-protocol-discovery.test.js +102 -0
- package/dist/unsupervised-physics.js +230 -0
- package/dist/unsupervised-physics.test.js +95 -0
- package/dist/utils.test.js +37 -0
- package/dist/validator.js +187 -10
- package/dist/verification-intelligence.js +475 -0
- package/dist/verify-api.js +432 -0
- package/dist/vi-impact-report.js +293 -0
- package/dist/wl-fingerprint.js +162 -0
- package/dist/wl-fingerprint.test.js +130 -0
- package/dist/zeroshot-strategy.js +139 -0
- package/docs/Progmune_/346/212/225/350/265/204/344/272/272/347/231/275/347/232/256/344/271/246_v2.0.html +576 -0
- package/docs/Progmune_/351/241/271/347/233/256/345/205/250/350/247/243.html +721 -0
- package/package.json +73 -6
- package/protocols.json +1956 -50
- package/.dockerignore +0 -14
- package/.mcp.json +0 -11
- package/.progmune_allowlist +0 -50
- package/.test_report/test_report.md +0 -87
- package/Dockerfile +0 -9
- package/FAQ.md +0 -167
- package/WHITEPAPER.md +0 -540
- package/demo-project/auth.ts +0 -55
- package/demo-project/tsconfig.json +0 -8
- package/dist/acl-breakdown.js +0 -13
- package/dist/all-sessions.js +0 -11
- package/dist/antibody-stats.js +0 -11
- package/dist/branch-tree-count.js +0 -14
- package/dist/common-fixpath.js +0 -12
- package/dist/constraint-types.js +0 -12
- package/dist/exec-metrics.js +0 -11
- package/dist/failure-report.js +0 -11
- package/dist/fast-path-hits.js +0 -13
- package/dist/fingerprint-list.js +0 -15
- package/dist/gen-history-log.js +0 -13
- package/dist/heatmap-data.js +0 -11
- package/dist/recent-session.js +0 -12
- package/dist/svl-distribution.js +0 -11
- package/dist/terminal-status.js +0 -11
- package/dist/token-savings.js +0 -11
- package/dist/total-repairs.js +0 -12
- package/dist/unresolved-count.js +0 -12
- package/dist/valid-fingerprints.js +0 -13
- package/dist/verify-ledgers.js +0 -11
- package/docs/whitepaper-style.css +0 -77
- package/docs/whitepaper-v2.1.md +0 -609
- package/docs/whitepaper-v2.2.md +0 -1064
- package/docs/whitepaper-v2.2.pdf +0 -0
- package/fly.toml +0 -31
- package/public/dashboard.html +0 -119
- package/server/hub.js +0 -116
- package/test/replay-golden/sess_1780063202050_mgeld.json +0 -9
- package/test/replay-golden/sess_1780064032560_gocld.json +0 -354
- package/test/replay-golden/sess_1780064413331_s2709.json +0 -606
- package/test/replay-golden/sess_1780064792710_y3avo.json +0 -614
- package/test/replay-golden.ts +0 -84
- package/test_benchmark.js +0 -165
- package/test_comprehensive.mjs +0 -638
- package/test_concurrency.js +0 -129
- package/test_ir_robustness.js +0 -85
- package/test_semantic_contracts.js +0 -269
- package/test_ssg_stress.js +0 -156
- package/test_svl3.js +0 -58
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,1189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Protocol State Machine Detector v2 — Repo-Agnostic
|
|
4
|
+
*
|
|
5
|
+
* All protocol patterns use \w* prefix/suffix patterns to match
|
|
6
|
+
* any project's naming conventions (curl, nginx, redis, any C project).
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.identifierParse = identifierParse;
|
|
10
|
+
exports.detectSafeguardViolations = detectSafeguardViolations;
|
|
11
|
+
exports.buildCallerMap = buildCallerMap;
|
|
12
|
+
exports.detectSafeguardViolationsV7 = detectSafeguardViolationsV7;
|
|
13
|
+
exports.detectProtocolViolations = detectProtocolViolations;
|
|
14
|
+
exports.validateProtocolState = validateProtocolState;
|
|
15
|
+
exports.validateCombined = validateCombined;
|
|
16
|
+
const PROTOCOLS = [
|
|
17
|
+
{
|
|
18
|
+
name: "TLS Handshake", category: "ssl", minCompleteness: 0.5,
|
|
19
|
+
steps: [
|
|
20
|
+
{ pattern: /\b(\w*ssl\w*init|\w*SSL\w*new|\w*ssl\w*create|\w*ssl\w*setup|\w*tls\w*init|\w*TLS\w*new|\w*ssl_cf_get_primary|\w*OPENSSL_init)\b/i, label: "tls_init", required: true },
|
|
21
|
+
{ pattern: /\b(\w*ssl\w*connect|\w*ssl\w*handshake|\w*tls\w*connect|\w*tls\w*handshake|\w*SSL_do_handshake)\b/i, label: "tls_connect", required: true },
|
|
22
|
+
{ pattern: /\b(\w*ssl\w*free|\w*SSL\w*cleanup|\w*ssl\w*shutdown|\w*tls\w*free|\w*SSL_CTX_free)\b/i, label: "tls_free", required: true },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: "SSH Connection", category: "ssh", minCompleteness: 0.4,
|
|
27
|
+
steps: [
|
|
28
|
+
{ pattern: /\b(\w*ssh\w*init|ssh\w*setup|\w*ssh_state_init)\b/i, label: "ssh_init", required: true },
|
|
29
|
+
{ pattern: /\b(\w*ssh\w*auth|\w*ssh\w*login|\w*ssh\w*cred)\b/i, label: "ssh_auth", required: true },
|
|
30
|
+
{ pattern: /\b(\w*ssh\w*done|\w*ssh\w*close|\w*ssh\w*cleanup|\w*ssh\w*error|\w*ssh\w*finish|\w*ssh\w*free|\w*ssh\w*disconnect)\b/i, label: "ssh_done", required: true },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "HTTP Request", category: "http", minCompleteness: 0.5,
|
|
35
|
+
steps: [
|
|
36
|
+
// init: handler setup, hook registration (nginx + Apache + curl)
|
|
37
|
+
{ pattern: /\b(\w*http\w*init|\w*http\w*create|\w*http\w*setup|\w*http\w*handler|\w*hook_handler|\w*hook_pre_config|curl_easy_init)\b/i, label: "http_init", required: true },
|
|
38
|
+
// process: request handling (all naming conventions)
|
|
39
|
+
{ pattern: /\b(\w*http\w*perform|\w*http\w*send|\w*http\w*request|\w*http\w*process|\w*process_request|\w*run_method|curl_easy_perform|\w*http\w*response|ap_pass_brigade)\b/i, label: "http_send", required: true },
|
|
40
|
+
// cleanup: finalize (all naming conventions)
|
|
41
|
+
{ pattern: /\b(\w*http\w*cleanup|\w*http\w*free|\w*http\w*close|\w*http\w*done|\w*finalize_request|curl_easy_cleanup|\w*http\w*finalize|ap_remove_output_filter)\b/i, label: "http_cleanup", required: true },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "Connection Lifecycle", category: "connection", minCompleteness: 0.4,
|
|
46
|
+
steps: [
|
|
47
|
+
{ pattern: /\b(\w*connect\b|\w*do_connect|\w*start_connect|\w*conn\w*init|\w*conn\w*setup)\b/i, label: "conn_init", required: true },
|
|
48
|
+
{ pattern: /\b(\w*send\b|\w*recv\b|\w*readwrite|\w*transfer|\w*xfer)\b/i, label: "conn_transfer", required: true },
|
|
49
|
+
{ pattern: /\b(\w*disconnect|\w*done\b|\w*conn\w*cleanup|\w*close_connection|\w*conn\w*free)\b/i, label: "conn_done", required: true },
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "Authentication", category: "auth", minCompleteness: 0.5,
|
|
54
|
+
steps: [
|
|
55
|
+
{ pattern: /\b(\w*auth\w*init|\w*auth\w*create|\w*auth\w*ntlm|\w*auth\w*spnego|\w*auth\w*digest|\w*auth\w*plain|\w*auth\w*login)\b/i, label: "auth_init", required: true },
|
|
56
|
+
{ pattern: /\b(\w*auth\w*free|\w*auth\w*cleanup|\w*auth\w*delete|FreeContextBuffer|DeleteSecurityContext)\b/i, label: "auth_cleanup", required: true },
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "HTTP/2 Session", category: "http2", minCompleteness: 0.5,
|
|
61
|
+
steps: [
|
|
62
|
+
{ pattern: /\b(\w*h2\w*init|\w*http2\w*init|\w*nghttp2_session_new)\b/i, label: "h2_init", required: true },
|
|
63
|
+
{ pattern: /\b(\w*h2\w*send|\w*http2\w*send|\w*nghttp2_submit|\w*nghttp2_session_send)\b/i, label: "h2_send", required: true },
|
|
64
|
+
{ pattern: /\b(\w*h2\w*close|\w*http2\w*free|\w*nghttp2_session_del)\b/i, label: "h2_close", required: true },
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
name: "QUIC Connection", category: "connection", minCompleteness: 0.4,
|
|
69
|
+
steps: [
|
|
70
|
+
{ pattern: /\b(\w*quic\w*init|\w*quic\w*new|\w*quiche_config_new)\b/i, label: "quic_init", required: true },
|
|
71
|
+
{ pattern: /\b(\w*quic\w*send|\w*quic\w*recv|\w*quiche_conn)\b/i, label: "quic_transfer", required: true },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: "SSL Configuration", category: "ssl", minCompleteness: 0.4,
|
|
76
|
+
steps: [
|
|
77
|
+
{ pattern: /\b(\w*ssl\w*get_config|\w*ssl_cf_get|\w*ssl\w*setup|\w*ssl\w*seed)\b/i, label: "ssl_cfg_init", required: true },
|
|
78
|
+
{ pattern: /\b(\w*ssl\w*config|\w*ssl\w*set|\w*ssl\w*default|\w*ssl\w*init_method)\b/i, label: "ssl_cfg_apply", required: true },
|
|
79
|
+
{ pattern: /\b(\w*ssl\w*verify|\w*ssl\w*cert|\w*ssl\w*handshake|\w*ssl\w*connect)\b/i, label: "ssl_cfg_done", required: true },
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: "Auth Protocol", category: "auth", minCompleteness: 0.5,
|
|
84
|
+
steps: [
|
|
85
|
+
{ pattern: /\b(login|register|authenticate|signin)\b/i, label: "auth_login", required: true },
|
|
86
|
+
{ pattern: /\b(generate_token|generate_access_token|issue_token|create_session|jwt_sign)\b/i, label: "auth_token", required: true },
|
|
87
|
+
{ pattern: /\b(refresh_token|get_profile|access_resource|verify_token|authorize|access_token)\b/i, label: "auth_access", required: true },
|
|
88
|
+
{ pattern: /\b(logout|destroy_session|revoke_token|token_revoke|invalidate)\b/i, label: "auth_logout", required: true },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
// ═══════════════════════════════════════════════════════════════
|
|
92
|
+
// P0 Injection: Payment Processing + Session Management detectors
|
|
93
|
+
// ═══════════════════════════════════════════════════════════════
|
|
94
|
+
{
|
|
95
|
+
name: "Payment Processing", category: "payment", minCompleteness: 0.4,
|
|
96
|
+
steps: [
|
|
97
|
+
// init: payment intent creation (Stripe, PayPal, Braintree, generic)
|
|
98
|
+
{ pattern: /\b(\w*payment\w*intent|\w*payment\w*init|\w*pay\w*init|\w*pay\w*create|\w*create\w*payment|\w*create\w*order|\w*stripe\w*create|\w*paypal\w*create|\w*braintree\w*create|\w*checkout\w*create|\w*initiate\w*payment|\w*initiate\w*order)\b/i, label: "pay_init", required: true },
|
|
99
|
+
// process: callback/webhook handling
|
|
100
|
+
{ pattern: /\b(\w*payment\w*callback|\w*webhook|\w*payment\w*confirm|\w*handle\w*webhook|\w*verify\w*signature|\w*validate\w*webhook|\w*stripe\w*webhook|\w*paypal\w*ipn|\w*callback\w*handler|\w*payment\w*success)\b/i, label: "pay_callback", required: true },
|
|
101
|
+
// complete: confirm/capture
|
|
102
|
+
{ pattern: /\b(\w*payment\w*confirm|\w*capture\w*payment|\w*confirm\w*order|\w*payment\w*done|\w*payment\w*success|\w*order\w*complete|\w*payment\w*succeed)\b/i, label: "pay_done", required: true },
|
|
103
|
+
// protect: signature verification (not required for minCompleteness)
|
|
104
|
+
{ pattern: /\b(\w*verify\w*sign|\w*check\w*sign|\w*validate\w*sign|\w*hmac\b|\w*webhook\w*secret|\w*stripe\w*sign|\w*paypal\w*verify|\w*verify\w*webhook\w*sign)\b/i, label: "pay_sig_check", required: false },
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "Session Management", category: "session", minCompleteness: 0.4,
|
|
109
|
+
steps: [
|
|
110
|
+
// create: session establishment
|
|
111
|
+
{ pattern: /\b(\w*session\w*create|\w*create\w*session|\w*session\w*init|\w*session\w*new|\w*session\w*start|\w*login\w*session|\w*init\w*session)\b/i, label: "sess_create", required: true },
|
|
112
|
+
// validate: session check (middleware)
|
|
113
|
+
{ pattern: /\b(\w*session\w*check|\w*session\w*valid|\w*validate\w*session|\w*verify\w*session|\w*authenticate\w*session|\w*session\w*auth|\w*check\w*session|\w*get\w*session)\b/i, label: "sess_validate", required: true },
|
|
114
|
+
// destroy: session termination/cleanup
|
|
115
|
+
{ pattern: /\b(\w*session\w*destroy|\w*session\w*delete|\w*session\w*revoke|\w*session\w*invalidate|\w*session\w*end|\w*session\w*logout|\w*session\w*signout|\w*session\w*expire|\w*session\w*timeout|\w*destroy\w*session|\w*cleanup\w*session|\w*purge\w*session)\b/i, label: "sess_destroy", required: true },
|
|
116
|
+
// refresh: token/session refresh (not required for minCompleteness)
|
|
117
|
+
{ pattern: /\b(\w*session\w*refresh|\w*session\w*renew|\w*session\w*extend|\w*refresh\w*session|\w*rotate\w*token|\w*session\w*rotate|\w*renew\w*session)\b/i, label: "sess_refresh", required: false },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
// ── P0 Round 2: Registration + File Upload + Resource Validation ──
|
|
121
|
+
{
|
|
122
|
+
name: "User Registration", category: "registration", minCompleteness: 0.4,
|
|
123
|
+
steps: [
|
|
124
|
+
{ pattern: /\b(\w*register\w*user|\w*user\w*register|\w*signup|\w*sign\w*up|\w*create\w*account|\w*account\w*create|\w*registration\w*init|\w*registration\w*start)\b/i, label: "reg_init", required: true },
|
|
125
|
+
{ pattern: /\b(\w*send\w*(code|otp|token|verif|sms|email)|\w*(code|otp|token|verif|sms|email)\w*send|\w*verification\w*send|\w*send\w*verif)\b/i, label: "reg_send_code", required: true },
|
|
126
|
+
{ pattern: /\b(\w*verify\w*(code|otp|token|email)|\w*(code|otp|token)\w*verify|\w*confirm\w*(code|registration|account)|\w*validate\w*(code|token|verif))\b/i, label: "reg_verify", required: true },
|
|
127
|
+
{ pattern: /\b(\w*activ\w*account|\w*account\w*activ|\w*registration\w*complete|\w*registration\w*done|\w*registration\w*finish)\b/i, label: "reg_activate", required: false },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: "File Upload", category: "file_upload", minCompleteness: 0.4,
|
|
132
|
+
steps: [
|
|
133
|
+
{ pattern: /\b(upload\w*file|file\w*upload|handle\w*upload|receive\w*upload|process\w*upload|multipart|form\w*data|upload\w*handler|upload\w*endpoint)\b/i, label: "up_receive", required: true },
|
|
134
|
+
{ pattern: /\b(\w*validate\w*file|\w*file\w*valid|\w*check\w*file\w*type|\w*check\w*file\w*size|\w*mime\w*check|\w*max\w*file\w*size|\w*allow\w*ext|\w*file\w*ext\w*check)\b/i, label: "up_validate", required: true },
|
|
135
|
+
{ pattern: /\b(\w*file\w*store|\w*store\w*file|\w*file\w*save|\w*save\w*file|\w*upload\w*to\w*cloud|\w*S3\w*upload|\w*cloud\w*upload)\b/i, label: "up_store", required: true },
|
|
136
|
+
{ pattern: /\b(\w*file\w*clean|\w*file\w*remove|\w*remove\w*file|\w*file\w*delete|\w*cleanup\w*upload|\w*upload\w*clean)\b/i, label: "up_cleanup", required: false },
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "Input Validation Chain", category: "resource", minCompleteness: 0.4,
|
|
141
|
+
steps: [
|
|
142
|
+
{ pattern: /\b(\w*sanitize|\w*escape|\w*clean|\w*strip\w*tags|\w*html\w*escape|\w*xss\w*clean|\w*filter\w*input|\w*input\w*filter)\b/i, label: "res_sanitize", required: true },
|
|
143
|
+
{ pattern: /\b(\w*valid\w*type|\w*type\w*check|\w*check\w*type|\w*is_string|\w*is_number|\w*is_int|\w*is_bool|\w*type\w*of|\w*schema\w*valid|\w*valid\w*schema)\b/i, label: "res_validate", required: true },
|
|
144
|
+
{ pattern: /\b(\w*valid\w*range|\w*range\w*check|\w*min\w*length|\w*max\w*length|\w*min_value|\w*max_value|\w*length\w*check|\w*bound\w*check)\b/i, label: "res_range", required: false },
|
|
145
|
+
],
|
|
146
|
+
},
|
|
147
|
+
// ── P0 Round 3: api_gateway + notification + supplier + tls
|
|
148
|
+
// + data_integrity + dev_pipeline
|
|
149
|
+
// + printlab_order + printlab_print ──
|
|
150
|
+
{
|
|
151
|
+
name: "Rate Limiting", category: "api_gateway", minCompleteness: 0.4,
|
|
152
|
+
steps: [
|
|
153
|
+
{ pattern: /\b(\w*rate\w*limit|\w*rate\w*check|\w*throttle\b|\w*check\w*rate|\w*quota\w*check|\w*concurrency\w*limit|\w*rate\w*limiter)\b/i, label: "rate_check", required: true },
|
|
154
|
+
{ pattern: /\b(\w*rate\w*exceed|rate\w*block|rate\w*reject|conn\w*limit|circuit\w*break|circuit\w*open|too\w*many\w*request)\b/i, label: "rate_block", required: true },
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: "Notification Delivery", category: "notification", minCompleteness: 0.4,
|
|
159
|
+
steps: [
|
|
160
|
+
{ pattern: /\b(\w*notif\w*compos|\w*compos\w*notif|\w*email\w*compos|\w*sms\w*compos|\w*push\w*compos|\w*notif\w*create|\w*notif\w*build|\w*build\w*notif)\b/i, label: "notif_compose", required: true },
|
|
161
|
+
{ pattern: /\b(\w*send\w*notif|\w*notif\w*send|\w*email\w*send|\w*sms\w*send|\w*push\w*send|\w*dispatch\w*notif|\w*notif\w*dispatch)\b/i, label: "notif_send", required: true },
|
|
162
|
+
{ pattern: /\b(\w*confirm\w*deliv|\w*deliv\w*confirm|\w*notif\w*ack|\w*notif\w*confirm|\w*send\w*confirm|\w*ack\w*deliv)\b/i, label: "notif_confirm", required: false },
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "Supplier Lifecycle", category: "supplier", minCompleteness: 0.4,
|
|
167
|
+
steps: [
|
|
168
|
+
{ pattern: /\b(\w*supplier\w*regist|\w*regist\w*supplier|\w*vendor\w*regist|\w*partner\w*onboard|\w*onboard\w*partner|\w*supplier\w*create)\b/i, label: "sup_register", required: true },
|
|
169
|
+
{ pattern: /\b(\w*supplier\w*verif|\w*verif\w*supplier|\w*supplier\w*valid|\w*approve\w*supplier|\w*supplier\w*approve|\w*kyc\w*check)\b/i, label: "sup_verify", required: true },
|
|
170
|
+
{ pattern: /\b(\w*supplier\w*enabl|\w*enabl\w*supplier|\w*supplier\w*activ|\w*activ\w*supplier|\w*supplier\w*assign)\b/i, label: "sup_enable", required: true },
|
|
171
|
+
],
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
name: "TLS Server Setup", category: "tls", minCompleteness: 0.4,
|
|
175
|
+
steps: [
|
|
176
|
+
{ pattern: /\b(\w*tls\w*config|\w*ssl\w*config|\w*cert\w*load|\w*load\w*cert|\w*key\w*load|\w*tls\w*setup|\w*tls\w*init|\w*SSL_CTX\w*config)\b/i, label: "tls_config", required: true },
|
|
177
|
+
{ pattern: /\b(\w*create\w*server|\w*server\w*start|\w*listen\b|\w*bind\b|\w*server\w*init|\w*start\w*server|\w*http\w*listen)\b/i, label: "tls_server", required: false },
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
name: "Data Integrity Check", category: "data_integrity", minCompleteness: 0.4,
|
|
182
|
+
steps: [
|
|
183
|
+
{ pattern: /\b(\w*check\w*exist|\w*exist\w*check|\w*lookup|\w*find\w*by\w*id|\w*get\w*entity|\w*valid\w*refer|\w*refer\w*valid)\b/i, label: "di_check", required: true },
|
|
184
|
+
{ pattern: /\b(\w*create\w*refer|\w*refer\w*create|\w*link\w*entity|\w*assoc\w*entity|\w*set\w*foreign|\w*foreign\w*key)\b/i, label: "di_refer", required: true },
|
|
185
|
+
{ pattern: /\b(\w*audit|\w*log\w*change|\w*record\w*mutation|\w*track\w*change|\w*change\w*log|\w*audit\w*trail)\b/i, label: "di_audit", required: false },
|
|
186
|
+
],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: "Dev Pipeline", category: "dev_pipeline", minCompleteness: 0.4,
|
|
190
|
+
steps: [
|
|
191
|
+
{ pattern: /\b(\w*extract\w*ir|\w*ir\w*extract|\w*parse\w*source|\w*analyze\w*code|\w*ast\w*parse|\w*build\w*ast|\w*compile\w*ir)\b/i, label: "dev_extract", required: true },
|
|
192
|
+
{ pattern: /\b(\w*valid\w*action|\w*action\w*valid|\w*check\w*rule|\w*rule\w*check|\w*lint|\w*verify\w*rule|\w*enforce\w*rule)\b/i, label: "dev_validate", required: true },
|
|
193
|
+
{ pattern: /\b(\w*emit|\w*generate|\w*output|\w*codegen|\w*compile|\w*build\w*out|\w*write\w*out)\b/i, label: "dev_emit", required: false },
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
name: "3D Print Order", category: "printlab_order", minCompleteness: 0.3,
|
|
198
|
+
steps: [
|
|
199
|
+
{ pattern: /\b(\w*upload\w*stl|\w*stl\w*upload|\w*upload\w*model|\w*model\w*upload|\w*file\w*upload|\w*upload\w*file)\b/i, label: "po_upload", required: true },
|
|
200
|
+
{ pattern: /\b(\w*slice|\w*gcode|\w*generate\w*gcode|\w*estimate|\w*cost\w*calc|\w*calc\w*cost|\w*create\w*order)\b/i, label: "po_process", required: true },
|
|
201
|
+
{ pattern: /\b(\w*queue|\w*ship|\w*deliver|\w*complete\w*order|\w*order\w*done|\w*finish\w*order)\b/i, label: "po_complete", required: false },
|
|
202
|
+
],
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: "3D Print Execution", category: "printlab_print", minCompleteness: 0.4,
|
|
206
|
+
steps: [
|
|
207
|
+
{ pattern: /\b(\w*start\w*print|\w*print\w*start|\w*begin\w*print|\w*print\w*begin|\w*execute\w*print|\w*run\w*print)\b/i, label: "pp_start", required: true },
|
|
208
|
+
{ pattern: /\b(\w*complete\w*print|\w*print\w*complete|\w*finish\w*print|\w*print\w*done|\w*print\w*success|\w*print\w*finish)\b/i, label: "pp_complete", required: true },
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
];
|
|
212
|
+
// ═══════════════════════════════════════════════════════════════
|
|
213
|
+
// P0 Round 4: PLSB Coverage — Double Release, Use After Release,
|
|
214
|
+
// Session Fixation, Privilege Escalation, Double Commit,
|
|
215
|
+
// API Contract / Workflow Bypass
|
|
216
|
+
// ═══════════════════════════════════════════════════════════════
|
|
217
|
+
const PLSB_PROTOCOLS = [
|
|
218
|
+
{
|
|
219
|
+
// PLS-002: Double Release
|
|
220
|
+
name: "Double Release Detection", category: "resource_leak", minCompleteness: 0.3,
|
|
221
|
+
steps: [
|
|
222
|
+
{ pattern: /\b(\w*close|close\w*|\w*free|free\w*|\w*disconnect|disconnect\w*|\w*cleanup|cleanup\w*|\w*destroy|destroy\w*|\w*release|release\w*|\w*shutdown|shutdown\w*|\w*end|end\w*|\w*delete|delete\w*)\b/i, label: "release_first", required: true },
|
|
223
|
+
{ pattern: /\b(\w*close|close\w*|\w*free|free\w*|\w*disconnect|disconnect\w*|\w*cleanup|cleanup\w*|\w*destroy|destroy\w*|\w*release|release\w*|\w*shutdown|shutdown\w*|\w*end|end\w*|\w*delete|delete\w*)\b/i, label: "release_second", required: false },
|
|
224
|
+
],
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
// PLS-003: Use After Release
|
|
228
|
+
name: "Use After Release Detection", category: "use_after_free", minCompleteness: 0.3,
|
|
229
|
+
steps: [
|
|
230
|
+
{ pattern: /\b(\w*close|close\w*|\w*free|free\w*|\w*disconnect|disconnect\w*|\w*destroy|destroy\w*|\w*release|release\w*|\w*shutdown|shutdown\w*)\b/i, label: "release", required: true },
|
|
231
|
+
{ pattern: /\b(\w*read|read\w*|\w*write|write\w*|\w*send|send\w*|\w*recv|recv\w*|\w*query|query\w*|\w*access|access\w*|\w*use|use\w*|\w*get|get\w*|\w*execute|execute\w*)\b/i, label: "use_after", required: false },
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
// PLS-005: Session Fixation
|
|
236
|
+
name: "Session Fixation Detection", category: "session_fixation", minCompleteness: 0.5,
|
|
237
|
+
steps: [
|
|
238
|
+
{ pattern: /\b(\w*login|login\w*|\w*signin|signin\w*|\w*authenticate|authenticate\w*|\w*create\w*session|session\w*create|\w*session\w*init|init\w*session|\w*session\w*start|start\w*session)\b/i, label: "session_create", required: true },
|
|
239
|
+
{ pattern: /\b(\w*logout|logout\w*|\w*signout|signout\w*|\w*session\w*destroy|destroy\w*session|\w*session\w*invalidate|invalidate\w*session|\w*session\w*revoke|revoke\w*session|\w*session\w*end|end\w*session|\w*session\w*expire|expire\w*session|\w*session\w*clear|clear\w*session|\w*clear\w*session|session\w*clear)\b/i, label: "session_destroy", required: true },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
// PLS-006: Privilege Escalation
|
|
244
|
+
name: "Privilege Escalation Detection", category: "privilege_escalation", minCompleteness: 0.5,
|
|
245
|
+
steps: [
|
|
246
|
+
{ pattern: /\b(\w*delete\w*all|deleteAll\w*|\w*delete\w*user|deleteUser\w*|\w*delete\w*account|deleteAccount\w*|\w*admin|admin\w*|\w*manage\w*user|manageUser\w*|\w*manage\w*system|manageSystem\w*|\w*system\w*config|systemConfig\w*|\w*ban\w*user|banUser\w*|\w*modify\w*role|modifyRole\w*|\w*assign\w*role|assignRole\w*|\w*grant\w*permission|grantPermission\w*)\b/i, label: "admin_action", required: true },
|
|
247
|
+
{ pattern: /\b(\w*check\w*role|checkRole\w*|\w*verify\w*admin|verifyAdmin\w*|\w*require\w*admin|requireAdmin\w*|\w*is\w*admin|isAdmin\w*|\w*has\w*role|hasRole\w*|\w*check\w*permission|checkPermission\w*|\w*has\w*permission|hasPermission\w*|\w*require\w*role|requireRole\w*|\w*admin\w*only|adminOnly\w*|\w*authorize|authorize\w*|\w*check\w*access|checkAccess\w*)\b/i, label: "admin_auth", required: true },
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
// PLS-008: Double Commit
|
|
252
|
+
name: "Double Commit Detection", category: "transaction_violation", minCompleteness: 0.3,
|
|
253
|
+
steps: [
|
|
254
|
+
{ pattern: /\b(\w*begin\w*tx|beginTx\w*|\w*start\w*transaction|startTransaction\w*|\w*begin\w*transaction|beginTransaction\w*|\w*tx\w*start|txStart\w*|\w*db\w*transaction|dbTransaction\w*|\w*with\w*transaction|withTransaction\w*)\b/i, label: "tx_begin", required: true },
|
|
255
|
+
{ pattern: /\b(\w*commit|commit\w*|\w*commit\w*tx|commitTx\w*|\w*end\w*transaction|endTransaction\w*)\b/i, label: "tx_commit_first", required: true },
|
|
256
|
+
{ pattern: /\b(\w*commit|commit\w*|\w*commit\w*tx|commitTx\w*|\w*end\w*transaction|endTransaction\w*)\b/i, label: "tx_commit_second", required: false },
|
|
257
|
+
],
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
// PLS-013: Workflow Bypass / API Contract
|
|
261
|
+
name: "Workflow Bypass Detection", category: "missing_validation", minCompleteness: 0.5,
|
|
262
|
+
steps: [
|
|
263
|
+
{ pattern: /\b(\w*validate|validate\w*|\w*check|check\w*|\w*verify|verify\w*|\w*sanitize|sanitize\w*|\w*parse|parse\w*|\w*schema|schema\w*|\w*zod|zod\w*|\w*input\w*valid|valid\w*input)\b/i, label: "wf_validate", required: true },
|
|
264
|
+
{ pattern: /\b(\w*execute|execute\w*|\w*process|process\w*|\w*handle|handle\w*|\w*run|run\w*|\w*do\w*action|doAction\w*|\w*perform|perform\w*|\w*dispatch|dispatch\w*|\w*apply|apply\w*)\b/i, label: "wf_action", required: true },
|
|
265
|
+
{ pattern: /\b(\w*log|log\w*|\w*audit|audit\w*|\w*record|record\w*|\w*track|track\w*|\w*metric|metric\w*|\w*monitor|monitor\w*)\b/i, label: "wf_audit", required: false },
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
];
|
|
269
|
+
const ALL_PROTOCOLS = [...PROTOCOLS, ...PLSB_PROTOCOLS];
|
|
270
|
+
const SAFEGUARD_RULES = [
|
|
271
|
+
// ── Password Hashing ──
|
|
272
|
+
{
|
|
273
|
+
name: "Password Hashing",
|
|
274
|
+
category: "password_hashing",
|
|
275
|
+
trigger: /\b(register|signUp|createUser|createAccount|registerUser)\b/i,
|
|
276
|
+
safeguards: [
|
|
277
|
+
{ pattern: /\b(bcrypt|argon2|scrypt|pbkdf2|hash|hashPassword|createHash|hashSync|hash_password)\b/i, label: "secure_hash" },
|
|
278
|
+
],
|
|
279
|
+
violationMessage: "User registration function does not call a secure password hashing function (bcrypt/argon2/scrypt). Passwords may be stored in plaintext or with weak hashing.",
|
|
280
|
+
conceptMissing: ["PasswordHash", "KeyDerivation"],
|
|
281
|
+
conceptExpected: ["bcrypt", "argon2", "scrypt"],
|
|
282
|
+
},
|
|
283
|
+
// Catch SHA256/MD5 specifically
|
|
284
|
+
{
|
|
285
|
+
name: "Password Hashing (Weak)",
|
|
286
|
+
category: "password_hashing",
|
|
287
|
+
trigger: /\b(register|signUp|createUser|createAccount|registerUser)\b/i,
|
|
288
|
+
safeguards: [
|
|
289
|
+
{ pattern: /\b(bcrypt|argon2|scrypt|pbkdf2)\b/i, label: "strong_hash" },
|
|
290
|
+
],
|
|
291
|
+
violationMessage: "User registration uses weak or no password hashing. SHA256/MD5 detected — use bcrypt/argon2 instead.",
|
|
292
|
+
conceptMissing: ["StrongHash", "SaltGeneration"],
|
|
293
|
+
conceptExpected: ["bcrypt", "argon2"],
|
|
294
|
+
},
|
|
295
|
+
// ── Authorization / Ownership Check ──
|
|
296
|
+
// v2: narrowed triggers — removed "process" and "set" (too generic for C libraries)
|
|
297
|
+
{
|
|
298
|
+
name: "Authorization (Ownership Check)",
|
|
299
|
+
category: "authorization",
|
|
300
|
+
trigger: /\b(delete|remove|toggle|modify|edit|lock|ban|refund|assign|transfer|share|schedule|upload)(?:[A-Z]\w*|_\w+)|(?:[A-Z]\w*|_\w+)(Delete|Remove|Toggle|Modify|Edit|Lock|Ban|Refund|Assign|Transfer|Share|Schedule|Upload)\b/i,
|
|
301
|
+
safeguards: [
|
|
302
|
+
{ pattern: /\b(getUser|validateToken|verifySession|getSessionUser|getCurrentUser|checkOwner|authorId\s*[!=]==?|ownerId\s*[!=]==?|userId\s*[!=]==?|\.owner\s*[!=]==?|hasPermission|checkPermission|checkAccess|isAuthorized|checkRole|requireRole|adminCheck|isAdmin|canModify|canDelete|canEdit)\b/i, label: "auth_check" },
|
|
303
|
+
],
|
|
304
|
+
violationMessage: "Mutation operation does not verify user ownership or authorization before modifying data.",
|
|
305
|
+
conceptMissing: ["OwnershipCheck", "AuthorizationGuard"],
|
|
306
|
+
conceptExpected: ["getUser", "validateToken", "ownerId check"],
|
|
307
|
+
excludePatterns: [
|
|
308
|
+
/_hd_/, // HPACK header compression internals
|
|
309
|
+
/_frame_/, // protocol frame handlers
|
|
310
|
+
/_stream_/, // stream internals
|
|
311
|
+
/_buf_/, // buffer internals
|
|
312
|
+
/_pkt_/, // packet internals
|
|
313
|
+
/set_authn_id/, // internal auth setter
|
|
314
|
+
/add_auth_info/, // internal auth metadata
|
|
315
|
+
/process_echo_/, // echo protocol handler
|
|
316
|
+
/Command$/, // generic command processors
|
|
317
|
+
/InputBuffer/, // input buffer processors
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
// Functions that serve data without any auth
|
|
321
|
+
// v2: removed "read" (I/O op in C) and "find" (internal search); narrowed to API-oriented patterns
|
|
322
|
+
{
|
|
323
|
+
name: "Authorization (Unauthenticated Access)",
|
|
324
|
+
category: "authorization",
|
|
325
|
+
languages: ["typescript", "javascript", "python"],
|
|
326
|
+
trigger: /\b(list|download|view|fetch)(?:[A-Z]\w*|_\w+)|get(?:[A-Z]\w+|_\w+)/i,
|
|
327
|
+
safeguards: [
|
|
328
|
+
{ pattern: /\b(getUser|validateToken|verifySession|getSessionUser|getCurrentUser|token\w*(Check|Verify|Valid)|session\w*(Check|Verify|Valid)|auth\w*(Check|Verify|Valid|Guard|Middleware|Required)|requireAuth|withAuth|authenticate\w*(User|Request|Token)?|checkAuth|isAuth|hasAuth|checkAccess|hasAccess)\b/i, label: "auth_check" },
|
|
329
|
+
],
|
|
330
|
+
violationMessage: "Data access function does not check authentication. Anyone can access data without credentials.",
|
|
331
|
+
conceptMissing: ["AuthenticationCheck", "AccessControl"],
|
|
332
|
+
conceptExpected: ["token validation", "session check", "auth middleware"],
|
|
333
|
+
excludePatterns: [
|
|
334
|
+
/get_client_cert/, // TLS certificate retrieval
|
|
335
|
+
/get_ssl_/, // SSL config retrieval
|
|
336
|
+
/get_config/, // configuration retrieval
|
|
337
|
+
/get_option/, // option retrieval
|
|
338
|
+
/get_env/, // environment variable
|
|
339
|
+
/getpid|getuid|geteuid/, // OS-level getters
|
|
340
|
+
/listpack/, // Redis internal data structure
|
|
341
|
+
/readSync|readQuery/, // internal I/O
|
|
342
|
+
/findBig|findKey|findPk/, // internal search (not API)
|
|
343
|
+
],
|
|
344
|
+
},
|
|
345
|
+
// ── Data Integrity (Foreign Key Validation) ──
|
|
346
|
+
// v2: removed "process" and "send" (too generic for C)
|
|
347
|
+
{
|
|
348
|
+
name: "Data Integrity (Foreign Key)",
|
|
349
|
+
category: "data_integrity",
|
|
350
|
+
trigger: /\b(add|create|post|refund)(?:[A-Z]\w*|_\w+)|(?:[A-Z]\w*|_\w+)(Add|Create|Post|Refund)\b/i,
|
|
351
|
+
safeguards: [
|
|
352
|
+
{ pattern: /\b(get|find|check|exists|lookup|status|validate|verify)(?:[A-Z]\w*|_\w+)\b/i, label: "fk_check" },
|
|
353
|
+
],
|
|
354
|
+
violationMessage: "Creates a child entity without verifying the parent entity exists. Orphaned references possible.",
|
|
355
|
+
conceptMissing: ["ForeignKeyValidation", "ReferentialIntegrity"],
|
|
356
|
+
conceptExpected: ["checkExists", "getParent", "validateReference"],
|
|
357
|
+
excludePatterns: [
|
|
358
|
+
/_hd_/, // HPACK header compression
|
|
359
|
+
/add_auth_info/, // internal auth metadata
|
|
360
|
+
/add_header/, // HTTP header addition
|
|
361
|
+
/send_response/, // HTTP response (not entity creation)
|
|
362
|
+
/create_callback/, // callback creation
|
|
363
|
+
/create_filter/, // filter creation
|
|
364
|
+
],
|
|
365
|
+
},
|
|
366
|
+
// ── Input Validation ──
|
|
367
|
+
// v2: removed "send" (too generic for C); kept create/add/post/upload
|
|
368
|
+
{
|
|
369
|
+
name: "Input Validation",
|
|
370
|
+
category: "input_validation",
|
|
371
|
+
trigger: /\b(create|add|post|upload)(?:[A-Z]\w*|_\w+)|(?:[A-Z]\w*|_\w+)(Create|Add|Post|Upload)\b/i,
|
|
372
|
+
safeguards: [
|
|
373
|
+
{ pattern: /\b(validate|sanitize|check|verify)(?:[A-Z]\w*|_\w*)(Content|Input|Length|Title|Body|Type|Size|File|Data|Param|Arg|Field|Value)\b|\b(validateContent|sanitizeInput|checkLength|verifyType|checkSize)\b/i, label: "input_validation" },
|
|
374
|
+
],
|
|
375
|
+
violationMessage: "Content creation function does not validate or sanitize input. XSS, injection, and oversized content possible.",
|
|
376
|
+
conceptMissing: ["InputSanitization", "ContentValidation", "SizeLimit"],
|
|
377
|
+
conceptExpected: ["validateContent", "sanitizeInput", "checkLength"],
|
|
378
|
+
excludePatterns: [
|
|
379
|
+
/_hd_/, // HPACK header compression
|
|
380
|
+
/add_auth_info/, // internal auth metadata
|
|
381
|
+
/add_header/, // HTTP header addition
|
|
382
|
+
/send_response/, // HTTP response
|
|
383
|
+
/send_reply/, // protocol reply
|
|
384
|
+
/upload_blob/, // binary blob upload (not content)
|
|
385
|
+
],
|
|
386
|
+
},
|
|
387
|
+
// ── TLS Enforcement ──
|
|
388
|
+
{
|
|
389
|
+
name: "TLS Enforcement",
|
|
390
|
+
category: "tls_enforcement",
|
|
391
|
+
trigger: /\b(createServer|listen|handleRequest|app\.listen|express)\b/i,
|
|
392
|
+
safeguards: [
|
|
393
|
+
{ pattern: /\b(https|tls|ssl|cert|key|TLS|SSL|HTTPS|createSecureContext|credentials)\b/i, label: "tls_config" },
|
|
394
|
+
],
|
|
395
|
+
violationMessage: "Server created without TLS configuration. Connections will be unencrypted HTTP.",
|
|
396
|
+
conceptMissing: ["TLSConfiguration", "HTTPSEnforcement", "CertificateSetup"],
|
|
397
|
+
conceptExpected: ["https.createServer", "TLS cert", "SSL configuration"],
|
|
398
|
+
},
|
|
399
|
+
// ── Token Security ──
|
|
400
|
+
{
|
|
401
|
+
name: "Token Security (Weak Generation)",
|
|
402
|
+
category: "token_security",
|
|
403
|
+
languages: ["typescript", "javascript", "python"],
|
|
404
|
+
trigger: /\b(authenticate|login|signIn|logIn|createSession|generateToken)\b/i,
|
|
405
|
+
safeguards: [
|
|
406
|
+
{ pattern: /\b(crypto\.randomUUID|jwt\.sign|jsonwebtoken|nanoid|randomBytes|cryptoRandomString)\b/i, label: "secure_token" },
|
|
407
|
+
],
|
|
408
|
+
violationMessage: "Token/session generated without cryptographically secure random source. Tokens may be predictable or forgeable.",
|
|
409
|
+
conceptMissing: ["SecureRandom", "TokenEntropy", "CryptographicSignature"],
|
|
410
|
+
conceptExpected: ["crypto.randomUUID", "jwt.sign", "nanoid"],
|
|
411
|
+
},
|
|
412
|
+
// ── Stricter Ownership Check (for resource mutation) ──
|
|
413
|
+
{
|
|
414
|
+
name: "Authorization (Resource Ownership)",
|
|
415
|
+
category: "authorization",
|
|
416
|
+
trigger: /\b(toggle|remove)(?:[A-Z]\w*|_\w+)\b/i,
|
|
417
|
+
safeguards: [
|
|
418
|
+
{ pattern: /\b(ownerId\s*[!=]==?|authorId\s*[!=]==?|userId\s*[!=]==?|createdBy|\.owner\s*[!=]==?)/i, label: "ownership_comparison" },
|
|
419
|
+
],
|
|
420
|
+
violationMessage: "Resource mutation checks authentication but does NOT verify the resource belongs to the requesting user. Missing ownerId/authorId comparison.",
|
|
421
|
+
conceptMissing: ["ResourceOwnership", "HorizontalAuthorization"],
|
|
422
|
+
conceptExpected: ["ownerId comparison", "authorId check", "userId === resource.ownerId"],
|
|
423
|
+
},
|
|
424
|
+
// ── Payment Verification ──
|
|
425
|
+
{
|
|
426
|
+
name: "Payment Order Verification",
|
|
427
|
+
category: "data_integrity",
|
|
428
|
+
trigger: /\b(process|create|make|submit)\w*(Payment|Charge|Transaction)\b/i,
|
|
429
|
+
safeguards: [
|
|
430
|
+
{ pattern: /\b(getOrder|verifyOrder|checkOrder|findOrder|orderExists|order\b)\b/i, label: "order_verification" },
|
|
431
|
+
],
|
|
432
|
+
violationMessage: "Payment processed without verifying the associated order exists and belongs to the user.",
|
|
433
|
+
conceptMissing: ["OrderVerification", "PaymentAuthorization"],
|
|
434
|
+
conceptExpected: ["getOrder", "verifyOrder", "checkOrder"],
|
|
435
|
+
},
|
|
436
|
+
// ── Room Membership ──
|
|
437
|
+
{
|
|
438
|
+
name: "Room Membership Check",
|
|
439
|
+
category: "authorization",
|
|
440
|
+
trigger: /\b(send|post|publish)\w*(Message|Msg)\b/i,
|
|
441
|
+
safeguards: [
|
|
442
|
+
{ pattern: /\b(joinRoom|roomMember|checkMember|isMember|members\.includes|members\.find|memberOf|inRoom)\b/i, label: "room_membership" },
|
|
443
|
+
],
|
|
444
|
+
violationMessage: "Message sent to room without verifying the user is a room member.",
|
|
445
|
+
conceptMissing: ["RoomMembership", "ChannelAuthorization"],
|
|
446
|
+
conceptExpected: ["joinRoom", "isMember", "members.includes"],
|
|
447
|
+
},
|
|
448
|
+
// ── Refund Status Check ──
|
|
449
|
+
{
|
|
450
|
+
name: "Refund Status Verification",
|
|
451
|
+
category: "data_integrity",
|
|
452
|
+
trigger: /\b(refund|cancel|void|reverse)\w*(Payment|Order|Charge|Transaction)\b/i,
|
|
453
|
+
safeguards: [
|
|
454
|
+
{ pattern: /\b(status|\.status|getStatus|checkStatus|orderStatus|paymentStatus)\b/i, label: "status_check" },
|
|
455
|
+
],
|
|
456
|
+
violationMessage: "Refund/cancellation processed without verifying the current order/payment status.",
|
|
457
|
+
conceptMissing: ["StatusVerification", "IdempotencyCheck"],
|
|
458
|
+
conceptExpected: ["status check", "orderStatus", "paymentStatus"],
|
|
459
|
+
},
|
|
460
|
+
// ── Rate Limiting ──
|
|
461
|
+
{
|
|
462
|
+
name: "Rate Limiting",
|
|
463
|
+
category: "rate_limiting",
|
|
464
|
+
trigger: /\b(createServer|listen|handleRequest|app\.listen|express|router\.(post|get|put|delete|patch))\b/i,
|
|
465
|
+
safeguards: [
|
|
466
|
+
{ pattern: /\b(rateLimit|rate_limit|throttle|RateLimiter|expressRateLimit|rateLimiterMiddleware|limiter)\b/i, label: "rate_limit" },
|
|
467
|
+
],
|
|
468
|
+
violationMessage: "Server/API endpoint created without rate limiting. Vulnerable to brute force and abuse.",
|
|
469
|
+
conceptMissing: ["RateLimiting", "DoSProtection", "AbusePrevention"],
|
|
470
|
+
conceptExpected: ["rateLimit", "throttle", "express-rate-limit"],
|
|
471
|
+
},
|
|
472
|
+
// ── C-Specific Rules ──
|
|
473
|
+
{
|
|
474
|
+
name: "Key Derivation Safety",
|
|
475
|
+
category: "crypto",
|
|
476
|
+
languages: ["c"],
|
|
477
|
+
trigger: /\b(ecdh|curve25519|ssh_dh_|kex_|build_k|do_create_k|derive\w*secret|dh_set_param|ec_key|ecdh_)\b/i,
|
|
478
|
+
safeguards: [
|
|
479
|
+
{ pattern: /\b(EC_KEY_get0_group|EC_KEY_check_key|EVP_PKEY_check|DH_check|get0_group|check_key|verify_param|validate_curve|ssh_key_is_private|ssh_key_type)\b/i, label: "key_validation" },
|
|
480
|
+
{ pattern: /\b(EC_KEY_free|EVP_PKEY_free|DH_free|BN_free|BN_clear_free|gcry_sexp_release|mbedtls_ecp_group_free|mbedtls_ecp_point_free|mbedtls_mpi_free|ssh_string_free|ssh_string_burn|ssh_buffer_free|OSSL_PARAM_BLD_free|OSSL_PARAM_free|explicit_bzero)\b/i, label: "crypto_cleanup" },
|
|
481
|
+
],
|
|
482
|
+
violationMessage: "Key derivation function does not validate generated keys or properly clean up cryptographic material.",
|
|
483
|
+
conceptMissing: ["KeyValidation", "CryptoCleanup"],
|
|
484
|
+
conceptExpected: ["EC_KEY_check_key", "EVP_PKEY_check", "explicit_bzero"],
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
name: "Certificate Pinning Validation",
|
|
488
|
+
category: "certificate",
|
|
489
|
+
languages: ["c"],
|
|
490
|
+
trigger: /(pin_peer_pubkey|pin_cert|pin_pubkey|pubkey_pin|cert_pin|ssl_pin|pkpin)\b/i,
|
|
491
|
+
safeguards: [
|
|
492
|
+
{ pattern: /\b(X509_verify_cert|SSL_get_verify_result|X509_STORE_CTX_get_error|verify_certificate|verify_peer|cert_verify|ssl_verify|Curl_ssl_cf_get_config|ossl_verify|gtls_verify|mbedtls_ssl_conf_verify)\b/i, label: "cert_verify" },
|
|
493
|
+
{ pattern: /\b(Curl_ssl_cf_get_primary_config|Curl_ssl_cf_get_config|ssl_cf_get_config|ssl_config_get|peer_cert|get_peer_certificate)\b/i, label: "ssl_config" },
|
|
494
|
+
],
|
|
495
|
+
violationMessage: "Certificate/public key pinning used without proper X.509 certificate verification.",
|
|
496
|
+
conceptMissing: ["CertificateVerification", "TrustChainValidation"],
|
|
497
|
+
conceptExpected: ["X509_verify_cert", "SSL_get_verify_result"],
|
|
498
|
+
},
|
|
499
|
+
// ── Python-Specific Rules ──
|
|
500
|
+
{
|
|
501
|
+
name: "Unsafe Deserialization (Pickle)",
|
|
502
|
+
category: "input_validation",
|
|
503
|
+
languages: ["python"],
|
|
504
|
+
trigger: /\b(pickle\.loads|pickle\.load|cPickle\.loads|cPickle\.load|yaml\.load(?!_safe)|yaml_load(?!_safe))\b/i,
|
|
505
|
+
safeguards: [
|
|
506
|
+
{ pattern: /\b(pickle\.Unpickler|yaml\.safe_load|yaml_safe_load|json\.loads|ast\.literal_eval)\b/i, label: "safe_deserialize" },
|
|
507
|
+
],
|
|
508
|
+
violationMessage: "Unsafe deserialization of pickle/YAML can execute arbitrary code during deserialization.",
|
|
509
|
+
conceptMissing: ["SafeDeserialization", "InputSanitization"],
|
|
510
|
+
conceptExpected: ["yaml.safe_load", "json.loads"],
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
name: "Command Injection",
|
|
514
|
+
category: "input_validation",
|
|
515
|
+
languages: ["python"],
|
|
516
|
+
trigger: /\b(os\.system|os\.popen|subprocess\.(call|check_call|Popen|run)|commands\.getoutput|pty\.spawn)\b/i,
|
|
517
|
+
safeguards: [
|
|
518
|
+
{ pattern: /\b(shlex\.quote|shlex\.split|pipes\.quote|shell\s*=\s*False)\b/i, label: "safe_command" },
|
|
519
|
+
],
|
|
520
|
+
violationMessage: "Shell command execution without input quoting. Vulnerable to command injection.",
|
|
521
|
+
conceptMissing: ["CommandInjectionPrevention", "InputSanitization"],
|
|
522
|
+
conceptExpected: ["shlex.quote", "shell=False"],
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "Hardcoded Secrets",
|
|
526
|
+
category: "token_security",
|
|
527
|
+
languages: ["python"],
|
|
528
|
+
trigger: /\b(password|secret|api_key|API_KEY|token|\w*TOKEN\w*)\s*=\s*["'][^"']+["']/i,
|
|
529
|
+
safeguards: [
|
|
530
|
+
{ pattern: /\b(os\.environ|os\.getenv|config|\.env|python-dotenv|load_dotenv|SecretStr|Secret)\b/i, label: "env_secret" },
|
|
531
|
+
],
|
|
532
|
+
violationMessage: "Sensitive credentials hardcoded in source code. Use environment variables or a secrets manager.",
|
|
533
|
+
conceptMissing: ["SecretManagement", "ConfigurationSecurity"],
|
|
534
|
+
conceptExpected: ["os.environ", "os.getenv", "dotenv"],
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
name: "Dynamic Code Execution",
|
|
538
|
+
category: "input_validation",
|
|
539
|
+
languages: ["python"],
|
|
540
|
+
trigger: /\b(eval|exec|compile|__import__)\s*\(/i,
|
|
541
|
+
safeguards: [
|
|
542
|
+
{ pattern: /\b(ast\.literal_eval|json\.loads|safe_eval)\b/i, label: "safe_eval" },
|
|
543
|
+
],
|
|
544
|
+
violationMessage: "Dynamic code execution from string input. Attacker-controlled input can execute arbitrary code.",
|
|
545
|
+
conceptMissing: ["SafeCodeExecution", "InputValidation"],
|
|
546
|
+
conceptExpected: ["ast.literal_eval"],
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: "Context Manager Usage",
|
|
550
|
+
category: "resource_lifecycle",
|
|
551
|
+
languages: ["python"],
|
|
552
|
+
trigger: /\b(sqlite3\.connect|psycopg2\.connect|open|io\.open|socket\.socket|redis\.Redis)\b/i,
|
|
553
|
+
safeguards: [
|
|
554
|
+
{ pattern: /\b(with\s+\w|contextmanager|contextlib|closing|__enter__|\.close\(\))\b/i, label: "context_manager" },
|
|
555
|
+
],
|
|
556
|
+
violationMessage: "Resource opened outside a context manager — may leak on exception paths.",
|
|
557
|
+
conceptMissing: ["ResourceCleanup", "ExceptionSafety"],
|
|
558
|
+
conceptExpected: ["with statement", "contextmanager", ".close()"],
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
name: "SQL Injection (Python)",
|
|
562
|
+
category: "input_validation",
|
|
563
|
+
languages: ["python"],
|
|
564
|
+
trigger: /\b(execute|executemany)\b/i,
|
|
565
|
+
safeguards: [
|
|
566
|
+
{ pattern: /\b(%s|\?|:\w+|parameterize|\.execute\s*\(\s*\w+\s*,\s*[\[(])/i, label: "parameterized_query" },
|
|
567
|
+
],
|
|
568
|
+
violationMessage: "SQL executed with string formatting instead of parameterized queries. Vulnerable to SQL injection.",
|
|
569
|
+
conceptMissing: ["SQLInjectionPrevention", "ParameterizedQueries"],
|
|
570
|
+
conceptExpected: ["parameterized query", "%s placeholder"],
|
|
571
|
+
},
|
|
572
|
+
// ═══════════════════════════════════════════════════════════════
|
|
573
|
+
// P0 Injection: Payment + Session safeguard rules
|
|
574
|
+
// ═══════════════════════════════════════════════════════════════
|
|
575
|
+
// ── Payment: Webhook without signature verification ──
|
|
576
|
+
{
|
|
577
|
+
name: "Payment Webhook (No Signature Check)",
|
|
578
|
+
category: "payment",
|
|
579
|
+
trigger: /\b(\w*webhook|\w*payment\w*callback|\w*stripe\w*webhook|\w*paypal\w*ipn|\w*ipn\w*handler|\w*payment\w*notify)\b/i,
|
|
580
|
+
safeguards: [
|
|
581
|
+
{ pattern: /\b(\w*sign|\w*verify\w*sign|\w*check\w*sign|\w*validate\w*sign|\w*hmac\b|\w*webhook\w*secret|\w*signature\w*check|\w*signature\w*valid|\w*raw\w*body|\w*payload\w*raw)\b/i, label: "sig_check" },
|
|
582
|
+
],
|
|
583
|
+
violationMessage: "Payment webhook/callback handler does not verify the request signature. Accepts unauthenticated payment confirmations — trivial to forge.",
|
|
584
|
+
conceptMissing: ["WebhookSignatureVerification", "HMACValidation"],
|
|
585
|
+
conceptExpected: ["verifySignature", "stripe.webhooks.constructEvent", "hmac check"],
|
|
586
|
+
},
|
|
587
|
+
// ── Payment: Payment without order verification ──
|
|
588
|
+
{
|
|
589
|
+
name: "Payment Without Order Verification",
|
|
590
|
+
category: "payment",
|
|
591
|
+
trigger: /\b(process\w*payment|payment\w*process|create\w*payment|payment\w*create|processPayment|createPayment|handlePayment|payment\w*handle|complete\w*payment|payment\w*complete|charge\w*payment|payment\w*charge)\b/i,
|
|
592
|
+
safeguards: [
|
|
593
|
+
{ pattern: /\b(getOrder|checkOrder|verifyOrder|findOrder|orderExists|get_order|check_order|verify_order|find_order|order\w*find|order\w*get|order\w*check|order\w*verify|order\w*valid)\b/i, label: "order_check" },
|
|
594
|
+
],
|
|
595
|
+
violationMessage: "Payment processed without verifying the associated order exists and belongs to the user. Payments can be made against nonexistent or unauthorized orders.",
|
|
596
|
+
conceptMissing: ["OrderVerification", "PaymentAuthorization", "OrderOwnership"],
|
|
597
|
+
conceptExpected: ["getOrder", "checkOrder", "verifyOrder"],
|
|
598
|
+
},
|
|
599
|
+
// ── Payment: Refund without auth check ──
|
|
600
|
+
{
|
|
601
|
+
name: "Payment Refund (No Authorization)",
|
|
602
|
+
category: "payment",
|
|
603
|
+
trigger: /\b(refund|void|reverse|cancel\w*payment|\w*refund|\w*void\w*payment)\b/i,
|
|
604
|
+
safeguards: [
|
|
605
|
+
{ pattern: /\b(\w*auth|\w*admin|\w*role|\w*permission|\w*approve|\w*confirm|\w*verify\w*user|\w*check\w*owner|\w*validate\w*admin|\w*authorize\b)/i, label: "auth_check" },
|
|
606
|
+
],
|
|
607
|
+
violationMessage: "Refund/void function does not check authorization. Any user could initiate refunds without permission.",
|
|
608
|
+
conceptMissing: ["RefundAuthorization", "AdminCheck"],
|
|
609
|
+
conceptExpected: ["admin", "role check", "approve refund"],
|
|
610
|
+
},
|
|
611
|
+
// ── Session: Missing session timeout/expiry ──
|
|
612
|
+
{
|
|
613
|
+
name: "Session No Timeout",
|
|
614
|
+
category: "session",
|
|
615
|
+
trigger: /\b(\w*session\w*create|\w*create\w*session|\w*session\w*new|\w*session\w*start|\w*login\w*session|\w*session\w*init|signIn|signin|login\b|authenticate\b|createSession|create_session)\b/i,
|
|
616
|
+
safeguards: [
|
|
617
|
+
{ pattern: /\b(\w*expir|\w*ttl|\w*timeout|\w*max\w*age|\w*maxAge|\w*max_age|\w*lifetime|\w*duration|\w*expires|\w*deadline|\w*valid\w*for|\w*valid\w*until)/i, label: "timeout_set" },
|
|
618
|
+
],
|
|
619
|
+
violationMessage: "Session created without setting an expiry/TTL. Session tokens could be valid indefinitely.",
|
|
620
|
+
conceptMissing: ["SessionTimeout", "TTLConfiguration"],
|
|
621
|
+
conceptExpected: ["expiresIn", "maxAge", "session TTL"],
|
|
622
|
+
/** Library functions where timeout is configured via separate API (not in constructor) */
|
|
623
|
+
excludePatterns: [
|
|
624
|
+
/^session_new$/, // nghttp2 constructor (timeout via callbacks_set_send_timeout)
|
|
625
|
+
/_frame_/, // internal frame handlers
|
|
626
|
+
/_internal_/, // internal machinery
|
|
627
|
+
/CheckPAMAuth/, // PostgreSQL PAM — timeout via /etc/pam.d/
|
|
628
|
+
/pam_/, // PAM library functions (timeout external)
|
|
629
|
+
/CheckBSDAuth/, // BSD auth — timeout via login.conf
|
|
630
|
+
],
|
|
631
|
+
},
|
|
632
|
+
// ── Session: Missing token rotation on sensitive action ──
|
|
633
|
+
{
|
|
634
|
+
name: "No Token Rotation After Privilege Change",
|
|
635
|
+
category: "session",
|
|
636
|
+
trigger: /\b(\w*password\w*change|\w*password\w*reset|\w*change\w*password|\w*reset\w*password|\w*update\w*password|\w*privilege|\w*role\w*change|\w*escalat|\w*enable\w*2fa|\w*mfa\w*enable|\w*email\w*change)\b/i,
|
|
637
|
+
safeguards: [
|
|
638
|
+
{ pattern: /\b(\w*revoke|\w*rotate|\w*invalidate|\w*reissue|\w*regenerate|\w*new\w*token|\w*token\w*refresh|\w*session\w*refresh|\w*renew)/i, label: "token_rotate" },
|
|
639
|
+
],
|
|
640
|
+
violationMessage: "Privilege-changing operation detected without subsequent token rotation or session invalidation. Stolen pre-change tokens remain valid.",
|
|
641
|
+
conceptMissing: ["TokenRotation", "SessionInvalidation", "FixationPrevention"],
|
|
642
|
+
conceptExpected: ["revoke session", "rotate token", "invalidate old sessions"],
|
|
643
|
+
},
|
|
644
|
+
// ═══════════════════════════════════════════════════════════════
|
|
645
|
+
// P0 Round 2: Registration + File Upload + Resource safeguard rules
|
|
646
|
+
// ═══════════════════════════════════════════════════════════════
|
|
647
|
+
// ── Registration: No email verification ──
|
|
648
|
+
{
|
|
649
|
+
name: "Registration Without Email Verification",
|
|
650
|
+
category: "registration",
|
|
651
|
+
trigger: /\b(register|signup|signUp|registerUser|createUser|createAccount)\b/i,
|
|
652
|
+
safeguards: [
|
|
653
|
+
{ pattern: /\b(send\w*(Code|Otp|Token|Verif|Email|Sms|Link)|(code|otp|token|verif)\w*send|verification|confirmEmail|verifyEmail|sendVerification|verify_user_email)\b/i, label: "email_verify" },
|
|
654
|
+
],
|
|
655
|
+
violationMessage: "User registration does not send email/SMS verification. Fake accounts can be created without email ownership check.",
|
|
656
|
+
conceptMissing: ["EmailVerification", "OTPValidation", "AccountOwnership"],
|
|
657
|
+
conceptExpected: ["sendVerificationCode", "verifyEmail", "confirmAccount"],
|
|
658
|
+
},
|
|
659
|
+
// ── File Upload: No type/size validation ──
|
|
660
|
+
{
|
|
661
|
+
name: "File Upload Without Validation",
|
|
662
|
+
category: "file_upload",
|
|
663
|
+
trigger: /\b(upload|receive\w*file|handle\w*upload|file\w*upload|multipart|form\w*data|write\w*file|save\w*file|store\w*file)\b/i,
|
|
664
|
+
safeguards: [
|
|
665
|
+
{ pattern: /\b(valid\w*(type|size|ext|mime|file)|check\w*(type|size|ext|mime|file)|file\w*(type|size|ext|mime)|mime\w*check|max\w*size|max\w*file|allow\w*(ext|type)|file\w*filter|content\w*type|scan\w*file|virus\w*scan)\b/i, label: "file_validate" },
|
|
666
|
+
],
|
|
667
|
+
violationMessage: "File upload does not validate file type, size, or content. Vulnerable to malicious file uploads (webshells, oversized files, malware).",
|
|
668
|
+
conceptMissing: ["FileTypeValidation", "FileSizeLimit", "MalwareScanning"],
|
|
669
|
+
conceptExpected: ["validateFileType", "checkFileSize", "virus scan"],
|
|
670
|
+
excludePatterns: [
|
|
671
|
+
/file_upload/, // generic variable name, not upload handler
|
|
672
|
+
/ossl_do_file_type/, // OpenSSL file type detection
|
|
673
|
+
/ngx_file_(size|fs_size)/, // nginx file metadata
|
|
674
|
+
/nghttp2_max_size/, // nghttp2 size limit
|
|
675
|
+
/ngtcp2_conn_get_max_tx/, // ngtcp2 packet size
|
|
676
|
+
],
|
|
677
|
+
},
|
|
678
|
+
// ── Resource: Input sanitization missing ──
|
|
679
|
+
{
|
|
680
|
+
name: "No Input Sanitization",
|
|
681
|
+
category: "resource",
|
|
682
|
+
trigger: /\b(render|display|write|output|append|insert|innerHTML|dangerouslySetInnerHTML|document\.write|echo|printf|sprintf)\b/i,
|
|
683
|
+
safeguards: [
|
|
684
|
+
{ pattern: /\b(sanitize|escape|encode|html\w*entities|htmlspecialchars|encodeURI|stripTags|purify|DOMPurify|clean|filter\w*html|escape\w*html)\b/i, label: "sanitize" },
|
|
685
|
+
],
|
|
686
|
+
violationMessage: "User-controlled content rendered/displayed without sanitization. Vulnerable to XSS attacks.",
|
|
687
|
+
conceptMissing: ["XSSPrevention", "OutputEncoding", "ContentSanitization"],
|
|
688
|
+
conceptExpected: ["sanitize", "escapeHtml", "encodeURI"],
|
|
689
|
+
excludePatterns: [
|
|
690
|
+
/ssl_read|ssl_write|ssl_free/, // OpenSSL BIO I/O wrappers
|
|
691
|
+
/BIO_read|BIO_write|BIO_puts/, // OpenSSL BIO layer
|
|
692
|
+
/printf|sprintf|fprintf/, // C stdio — internal logging, not web rendering
|
|
693
|
+
/expect_quic_with_stream_lock/, // QUIC internal lock helper
|
|
694
|
+
/quic_write/, // QUIC internal I/O
|
|
695
|
+
/quic_read/, // QUIC internal I/O
|
|
696
|
+
/composite_end/, // JSON encoder internal
|
|
697
|
+
/qlog_event/, // QUIC qlog internal
|
|
698
|
+
/trace_frame/, // QUIC trace internal
|
|
699
|
+
/port_init|port_rx_pre/, // QUIC port internal
|
|
700
|
+
/echo$/, // shell echo, not web echo
|
|
701
|
+
/ldap_/, // LDAP connection/search — not web rendering
|
|
702
|
+
/FormatSearchFilter/, // LDAP search filter formatting
|
|
703
|
+
/InitializeLDAP/, // LDAP initialization
|
|
704
|
+
],
|
|
705
|
+
},
|
|
706
|
+
// ═══════════════════════════════════════════════════════════════
|
|
707
|
+
// P0 Round 3: Remaining namespace safeguard rules
|
|
708
|
+
// ═══════════════════════════════════════════════════════════════
|
|
709
|
+
// ── API Gateway: No rate limiting ──
|
|
710
|
+
{
|
|
711
|
+
name: "API Without Rate Limiting",
|
|
712
|
+
category: "api_gateway",
|
|
713
|
+
trigger: /\b(createServer|listen|handleRequest|router|endpoint|api\w*handler|request\w*handler|handle\w*request)\b/i,
|
|
714
|
+
safeguards: [
|
|
715
|
+
{ pattern: /\b(rate\w*limit|throttle|quota|concurrency|max\w*request|request\w*limit|token\w*bucket|leaky\w*bucket|circuit\w*break|redis\w*rate|limit\w*check)\b/i, label: "rate_limit" },
|
|
716
|
+
],
|
|
717
|
+
violationMessage: "API endpoint created without rate limiting. Vulnerable to DoS and brute-force attacks.",
|
|
718
|
+
conceptMissing: ["RateLimiting", "DoSProtection", "QuotaManagement"],
|
|
719
|
+
conceptExpected: ["rateLimit", "throttle", "maxRequests"],
|
|
720
|
+
excludePatterns: [
|
|
721
|
+
/ap_(get|setup)_client_block/, // Apache HTTP body reading
|
|
722
|
+
/connBlock/, // Redis connection blocking
|
|
723
|
+
/curlx_nonblock/, // curl non-blocking I/O
|
|
724
|
+
/inflate_header_block/, // HPACK header compression
|
|
725
|
+
/emit_\w*_block/, // HPACK emit
|
|
726
|
+
/DTLSv1_listen/, // DTLS protocol listener, not API endpoint
|
|
727
|
+
/BIO_dgram/, // BIO datagram
|
|
728
|
+
/recvfrom|sendto/, // socket I/O
|
|
729
|
+
],
|
|
730
|
+
},
|
|
731
|
+
// ── Notification: No retry on failure ──
|
|
732
|
+
{
|
|
733
|
+
name: "Notification Without Retry",
|
|
734
|
+
category: "notification",
|
|
735
|
+
trigger: /\b(send\w*email|send\w*sms|send\w*push|send\w*notif|dispatch|deliver\w*message|post\w*message)\b/i,
|
|
736
|
+
safeguards: [
|
|
737
|
+
{ pattern: /\b(retry|reconnect|fallback|resilience|circuit\w*break|exponential\w*backoff|dead\w*letter|dlq|queue\w*retry|backoff)\b/i, label: "retry" },
|
|
738
|
+
],
|
|
739
|
+
violationMessage: "Notification sent without retry/fallback mechanism. Transient network failures will cause silent message loss.",
|
|
740
|
+
conceptMissing: ["RetryLogic", "Resilience", "DeadLetterQueue"],
|
|
741
|
+
conceptExpected: ["retry", "exponential backoff", "dead letter queue"],
|
|
742
|
+
excludePatterns: [
|
|
743
|
+
/Curl_auth_create_\w+_message/, // auth message construction, not notification
|
|
744
|
+
/nghttp2_submit_\w+/, // HTTP/2 frame submission
|
|
745
|
+
],
|
|
746
|
+
},
|
|
747
|
+
// ── TLS: Certificate without renewal ──
|
|
748
|
+
{
|
|
749
|
+
name: "TLS Certificate Without Renewal",
|
|
750
|
+
category: "tls",
|
|
751
|
+
trigger: /\b(load\w*cert|load\w*ssl|ssl\w*config|tls\w*config|cert\w*load|ssl\w*ctx|certificate\w*load)\b/i,
|
|
752
|
+
safeguards: [
|
|
753
|
+
{ pattern: /\b(renew|rotate|expir|notAfter|notBefore|validity|check\w*expir|auto\w*renew|cert\w*watch|watch\w*cert)\b/i, label: "cert_renew" },
|
|
754
|
+
],
|
|
755
|
+
violationMessage: "TLS certificate loaded without expiry check or renewal mechanism. Will cause production outage when certificate expires.",
|
|
756
|
+
conceptMissing: ["CertificateRenewal", "ExpiryMonitoring", "AutoRotation"],
|
|
757
|
+
conceptExpected: ["checkExpiry", "autoRenew", "certificate watch"],
|
|
758
|
+
},
|
|
759
|
+
// ── Data Integrity: Mutation without audit ──
|
|
760
|
+
{
|
|
761
|
+
name: "Data Mutation Without Audit Trail",
|
|
762
|
+
category: "data_integrity",
|
|
763
|
+
trigger: /\b(update|delete|modify|change|mutate|set\w*field|edit|remove|drop)\b/i,
|
|
764
|
+
safeguards: [
|
|
765
|
+
{ pattern: /\b(audit|log\w*change|change\w*log|track\w*change|record\w*mutation|mutation\w*log|history|version|revision|snapshot|write\w*ahead|wal\b)/i, label: "audit_trail" },
|
|
766
|
+
],
|
|
767
|
+
violationMessage: "Data mutation without audit trail. Cannot track who changed what or recover from accidental data corruption.",
|
|
768
|
+
conceptMissing: ["AuditTrail", "ChangeTracking", "DataLineage"],
|
|
769
|
+
conceptExpected: ["auditLog", "changeHistory", "write-ahead log"],
|
|
770
|
+
},
|
|
771
|
+
// ═══════════════════════════════════════════════════════════════
|
|
772
|
+
// Governance meta-rule: Framework Version Convention Check
|
|
773
|
+
// Lesson from proxy.ts incident (2026-08-03)
|
|
774
|
+
// ═══════════════════════════════════════════════════════════════
|
|
775
|
+
{
|
|
776
|
+
name: "Framework Convention Override (Anti-Pattern)",
|
|
777
|
+
category: "governance",
|
|
778
|
+
languages: ["typescript", "javascript"],
|
|
779
|
+
trigger: /\b(middleware\.ts|middleware\.js|proxy\.ts|proxy\.js)\b/i,
|
|
780
|
+
safeguards: [
|
|
781
|
+
{ pattern: /\b(next@|next\s+16|next\s+17|next\s+18)\b/i, label: "next_version_check" },
|
|
782
|
+
],
|
|
783
|
+
violationMessage: "Framework convention file detected without version verification. " +
|
|
784
|
+
"If this project uses Next.js 16+, 'proxy.ts' is the correct convention (not 'middleware.ts'). " +
|
|
785
|
+
"Do not rename framework-generated files based on training data defaults. " +
|
|
786
|
+
"Check node_modules/<framework>/dist/docs/ for the current version's conventions. " +
|
|
787
|
+
"See: docs/proxy-ts-lesson.md",
|
|
788
|
+
conceptMissing: ["FrameworkVersionAwareness", "ConventionVerification"],
|
|
789
|
+
conceptExpected: ["version-specific convention", "framework docs check"],
|
|
790
|
+
},
|
|
791
|
+
// ═══════════════════════════════════════════════════════════════
|
|
792
|
+
// P0 Round 4: PLSB coverage safeguards — Session Fixation,
|
|
793
|
+
// Privilege Escalation, API Contract
|
|
794
|
+
// ═══════════════════════════════════════════════════════════════
|
|
795
|
+
// ── PLS-005: Session Fixation — session not invalidated on logout ──
|
|
796
|
+
{
|
|
797
|
+
name: "Session Fixation (Logout without Invalidation)",
|
|
798
|
+
category: "session_fixation",
|
|
799
|
+
languages: ["typescript", "javascript", "python"],
|
|
800
|
+
trigger: /\b(logout|signOut|logOut|signout|doLogout|handleLogout|endSession|clearSession)\b/i,
|
|
801
|
+
safeguards: [
|
|
802
|
+
{ pattern: /\b(session\w*destroy|destroy\w*session|session\w*invalidate|invalidate\w*session|session\w*revoke|revoke\w*session|session\w*clear|clear\w*session|session\w*end|end\w*session|session\w*expire|expire\w*session|token\w*revoke|revoke\w*token|token\w*blacklist|blacklist\w*token|invalidate\w*token)\b/i, label: "session_invalidate" },
|
|
803
|
+
],
|
|
804
|
+
violationMessage: "Logout function does not destroy/invalidate the session. Old session tokens remain valid, enabling session hijacking (session fixation).",
|
|
805
|
+
conceptMissing: ["SessionInvalidation", "SessionRevocation"],
|
|
806
|
+
conceptExpected: ["session.destroy", "session.invalidate", "session.revoke"],
|
|
807
|
+
},
|
|
808
|
+
// ── PLS-006: Privilege Escalation — admin action without role check ──
|
|
809
|
+
{
|
|
810
|
+
name: "Privilege Escalation (Admin without Role Check)",
|
|
811
|
+
category: "privilege_escalation",
|
|
812
|
+
trigger: /\b(deleteAll|deleteUser|deleteAccount|banUser|modifyRole|assignRole|grantPermission|manageSystem|systemConfig|adminDelete|adminRemove|adminBan|bulkDelete|massDelete)\b/i,
|
|
813
|
+
safeguards: [
|
|
814
|
+
{ pattern: /\b(checkRole|verifyAdmin|requireAdmin|isAdmin|hasRole|checkPermission|hasPermission|requireRole|adminOnly|authorize|checkAccess|verifyRole|isAuthorized|canDelete|canModify|canManage)\b/i, label: "admin_check" },
|
|
815
|
+
],
|
|
816
|
+
violationMessage: "Privileged/admin action does not verify the user's role or permissions before executing. An attacker with a lower-privilege account could perform this action.",
|
|
817
|
+
conceptMissing: ["RoleVerification", "PermissionCheck"],
|
|
818
|
+
conceptExpected: ["checkRole", "isAdmin", "hasPermission"],
|
|
819
|
+
},
|
|
820
|
+
// ── PLS-013: API Contract — Express/tRPC handler without input validation ──
|
|
821
|
+
{
|
|
822
|
+
name: "API Contract (Handler without Input Validation)",
|
|
823
|
+
category: "missing_validation",
|
|
824
|
+
languages: ["typescript", "javascript"],
|
|
825
|
+
trigger: /\b(router\.(get|post|put|delete|patch)|app\.(get|post|put|delete|patch)|publicProcedure|protectedProcedure|adminProcedure|\.query\(|\.mutation\()\b/i,
|
|
826
|
+
safeguards: [
|
|
827
|
+
{ pattern: /\b(z\.(string|number|object|array|boolean|enum|union|record)|zodSchema|validateRequest|validateBody|validateQuery|validateParams|parseAsync|\.parse\(|\.safeParse\(|checkSchema|body\(|param\(|query\(|expressValidator|celebrate)\b/i, label: "input_validation" },
|
|
828
|
+
],
|
|
829
|
+
violationMessage: "API endpoint handler does not validate input. Missing Zod schema, express-validator, or parameter validation. This allows malformed/injection payloads to reach business logic.",
|
|
830
|
+
conceptMissing: ["InputValidation", "SchemaValidation"],
|
|
831
|
+
conceptExpected: ["z.object", "zod", "express-validator", "validate"],
|
|
832
|
+
},
|
|
833
|
+
// ── API Contract — DB write without input sanitization ──
|
|
834
|
+
{
|
|
835
|
+
name: "API Contract (DB Write without Sanitization)",
|
|
836
|
+
category: "missing_validation",
|
|
837
|
+
languages: ["typescript", "javascript"],
|
|
838
|
+
trigger: /\b(db\.(insert|update|delete)|\w*\.(insert|update|delete)\(|database\w*\.(insert|update|delete)|db\.(execute|run|query)\(.*INSERT|UPDATE.*SET)\b/i,
|
|
839
|
+
safeguards: [
|
|
840
|
+
{ pattern: /\b(validate|sanitize|escape|strip|clean|filter|z\.|\.safeParse|\.parse\(|checkPermission|verifyAuth|hasRole|isAdmin|authorize)\b/i, label: "data_sanitize_or_auth" },
|
|
841
|
+
],
|
|
842
|
+
violationMessage: "Database write operation does not validate/sanitize input or check authorization. Risk of SQL injection, data corruption, or unauthorized data modification.",
|
|
843
|
+
conceptMissing: ["InputSanitization", "WriteAuthorization"],
|
|
844
|
+
conceptExpected: ["validate", "sanitize", "checkPermission", "zod"],
|
|
845
|
+
},
|
|
846
|
+
];
|
|
847
|
+
/**
|
|
848
|
+
* Identifier Parser: splits camelCase/PascalCase/snake_case into words.
|
|
849
|
+
* registerNewUser → ["register", "New", "User"]
|
|
850
|
+
* doLogin → ["do", "Login"]
|
|
851
|
+
* verifyToken → ["verify", "Token"]
|
|
852
|
+
* Not AST. Just smarter string splitting.
|
|
853
|
+
*/
|
|
854
|
+
function identifierParse(name) {
|
|
855
|
+
// Split on snake_case, kebab-case, dots
|
|
856
|
+
const parts = name.split(/[_\-\.]/);
|
|
857
|
+
const words = [];
|
|
858
|
+
for (const part of parts) {
|
|
859
|
+
// Split camelCase/PascalCase: "registerNewUser" → ["register", "New", "User"]
|
|
860
|
+
const camelWords = part.replace(/([a-z])([A-Z])/g, "$1$2").split("");
|
|
861
|
+
for (const w of camelWords) {
|
|
862
|
+
if (w.length > 0)
|
|
863
|
+
words.push(w);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return words;
|
|
867
|
+
}
|
|
868
|
+
/**
|
|
869
|
+
* Detect missing safeguards in function call sequences.
|
|
870
|
+
* Uses identifier parsing to match compound names (registerNewUser → register).
|
|
871
|
+
*/
|
|
872
|
+
function detectSafeguardViolations(calls, enclosingFuncName, language) {
|
|
873
|
+
const violations = [];
|
|
874
|
+
// Build effective calls: raw names + identifier-parsed words
|
|
875
|
+
const rawCalls = enclosingFuncName ? [enclosingFuncName, ...calls] : [...calls];
|
|
876
|
+
const parsedWords = [];
|
|
877
|
+
for (const c of rawCalls) {
|
|
878
|
+
parsedWords.push(...identifierParse(c));
|
|
879
|
+
}
|
|
880
|
+
const effectiveCalls = [...new Set([...rawCalls, ...parsedWords])];
|
|
881
|
+
// Skip authorization rules for auth functions — check both raw lowercased name and parsed words
|
|
882
|
+
const rawLower = enclosingFuncName?.toLowerCase() || "";
|
|
883
|
+
const AUTH_PATTERN = /\b(register|signup|signin|login|authenticate|createuser|createaccount|registeruser|registernewuser|dologin|verifytoken|validatesession|getuser|getsessionuser|getcurrentuser|endsession|logout|signout|dologout|destroysession|invalidatesession|invalidate|signout)\b/i;
|
|
884
|
+
const isAuthFunction = enclosingFuncName != null && (AUTH_PATTERN.test(rawLower) ||
|
|
885
|
+
identifierParse(enclosingFuncName).some(w => AUTH_PATTERN.test(w)));
|
|
886
|
+
// Filter rules by language
|
|
887
|
+
const activeRules = language
|
|
888
|
+
? SAFEGUARD_RULES.filter(r => !r.languages || r.languages.includes(language))
|
|
889
|
+
: SAFEGUARD_RULES;
|
|
890
|
+
for (const rule of activeRules) {
|
|
891
|
+
// Check if trigger matches
|
|
892
|
+
const triggerMatch = effectiveCalls.some(c => rule.trigger.test(c));
|
|
893
|
+
if (!triggerMatch)
|
|
894
|
+
continue;
|
|
895
|
+
// Skip authorization rules for auth functions — they ARE the auth
|
|
896
|
+
if (isAuthFunction && rule.category === "authorization")
|
|
897
|
+
continue;
|
|
898
|
+
// Check if at least one safeguard matches
|
|
899
|
+
const matchedSafeguard = rule.safeguards.find(s => effectiveCalls.some(c => s.pattern.test(c)));
|
|
900
|
+
if (matchedSafeguard)
|
|
901
|
+
continue;
|
|
902
|
+
// Check excludePatterns (library functions where safeguard is deferred to separate API)
|
|
903
|
+
if (rule.excludePatterns) {
|
|
904
|
+
const excluded = effectiveCalls.some(c => rule.excludePatterns.some(ep => ep.test(c)));
|
|
905
|
+
if (excluded)
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
// No safeguard matched → violation
|
|
909
|
+
violations.push({
|
|
910
|
+
rule: rule.name,
|
|
911
|
+
category: rule.category,
|
|
912
|
+
type: "missing_safeguard",
|
|
913
|
+
detail: rule.violationMessage,
|
|
914
|
+
conceptDetail: `Missing: ${rule.conceptMissing.join(", ")}. Expected at least one of: ${rule.conceptExpected.join(", ")}`,
|
|
915
|
+
missingConcepts: rule.conceptMissing,
|
|
916
|
+
expectedConcepts: rule.conceptExpected,
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
return violations;
|
|
920
|
+
}
|
|
921
|
+
// ═══════════════════════════════════════════════════════════════
|
|
922
|
+
// v7: Call Graph — Function-level Context
|
|
923
|
+
// ═══════════════════════════════════════════════════════════════
|
|
924
|
+
/**
|
|
925
|
+
* Build a reverse call graph: function name → list of caller function names.
|
|
926
|
+
* From IR FunctionInfo[].calls[], computes the inverse: who calls each function.
|
|
927
|
+
*/
|
|
928
|
+
function buildCallerMap(funcs) {
|
|
929
|
+
const callerMap = new Map();
|
|
930
|
+
for (const f of funcs) {
|
|
931
|
+
if (!f.calls)
|
|
932
|
+
continue;
|
|
933
|
+
for (const callee of f.calls) {
|
|
934
|
+
if (!callerMap.has(callee))
|
|
935
|
+
callerMap.set(callee, []);
|
|
936
|
+
callerMap.get(callee).push(f.name);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
return callerMap;
|
|
940
|
+
}
|
|
941
|
+
/**
|
|
942
|
+
* v7: Build safeguard context — own → same-file → caller chain.
|
|
943
|
+
*
|
|
944
|
+
* Hierarchy:
|
|
945
|
+
* 1. Function's own calls + identifier-parsed words
|
|
946
|
+
* 2. Same-file functions' calls (file-level, as v6)
|
|
947
|
+
* 3. Transitive caller chain calls (new in v7)
|
|
948
|
+
*/
|
|
949
|
+
function buildSafeguardContext(funcName, callerMap, funcCalls, fileCalls, funcFile, visited = new Set()) {
|
|
950
|
+
if (visited.has(funcName))
|
|
951
|
+
return new Set();
|
|
952
|
+
visited.add(funcName);
|
|
953
|
+
const context = new Set(funcCalls.get(funcName) || []);
|
|
954
|
+
// Parse identifier words from funcName itself
|
|
955
|
+
for (const w of identifierParse(funcName))
|
|
956
|
+
context.add(w);
|
|
957
|
+
// ▸ Same-file context (inherited from v6)
|
|
958
|
+
const file = funcFile.get(funcName);
|
|
959
|
+
if (file && fileCalls.has(file)) {
|
|
960
|
+
for (const c of fileCalls.get(file))
|
|
961
|
+
context.add(c);
|
|
962
|
+
}
|
|
963
|
+
// ▸ Caller chain context (new in v7)
|
|
964
|
+
const callers = callerMap.get(funcName) || [];
|
|
965
|
+
for (const caller of callers) {
|
|
966
|
+
const callerContext = buildSafeguardContext(caller, callerMap, funcCalls, fileCalls, funcFile, visited);
|
|
967
|
+
for (const c of callerContext)
|
|
968
|
+
context.add(c);
|
|
969
|
+
}
|
|
970
|
+
return context;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* v7: Detect missing safeguards — Function Context + Call Graph.
|
|
974
|
+
*
|
|
975
|
+
* Trigger check: function's own body + file-level calls (same scope as v6).
|
|
976
|
+
* Safeguard check: own → same-file → transitive caller chain (superset of v6).
|
|
977
|
+
*
|
|
978
|
+
* Rationale: narrow triggers (own-body only) break Recall on C projects where
|
|
979
|
+
* function names don't carry trigger keywords. Keeping v6's file-level trigger
|
|
980
|
+
* while expanding safeguard context to include caller chain preserves Recall
|
|
981
|
+
* and improves Precision.
|
|
982
|
+
*/
|
|
983
|
+
function detectSafeguardViolationsV7(calls, enclosingFuncName, callerMap, funcCalls, fileCalls, funcFile, language) {
|
|
984
|
+
const violations = [];
|
|
985
|
+
// Build effective calls for trigger scope: own body + file-level (same as v6)
|
|
986
|
+
const rawCalls = enclosingFuncName ? [enclosingFuncName, ...calls] : [...calls];
|
|
987
|
+
// Add file-level calls to trigger scope (preserves v6 Recall)
|
|
988
|
+
const file = enclosingFuncName ? funcFile.get(enclosingFuncName) : undefined;
|
|
989
|
+
const fileLevelCalls = file && fileCalls.has(file) ? [...fileCalls.get(file)] : [];
|
|
990
|
+
const triggerRawCalls = [...new Set([...rawCalls, ...fileLevelCalls])];
|
|
991
|
+
const triggerParsedWords = [];
|
|
992
|
+
for (const c of triggerRawCalls) {
|
|
993
|
+
triggerParsedWords.push(...identifierParse(c));
|
|
994
|
+
}
|
|
995
|
+
const triggerEffectiveCalls = [...new Set([...triggerRawCalls, ...triggerParsedWords])];
|
|
996
|
+
// Build safeguard context: own + file + all transitive callers
|
|
997
|
+
const safeContext = enclosingFuncName
|
|
998
|
+
? buildSafeguardContext(enclosingFuncName, callerMap, funcCalls, fileCalls, funcFile)
|
|
999
|
+
: new Set(triggerEffectiveCalls);
|
|
1000
|
+
for (const c of triggerEffectiveCalls)
|
|
1001
|
+
safeContext.add(c);
|
|
1002
|
+
// Skip authorization rules for auth functions
|
|
1003
|
+
const rawLower = enclosingFuncName?.toLowerCase() || "";
|
|
1004
|
+
const AUTH_PATTERN = /\b(register|signup|signin|login|authenticate|createuser|createaccount|registeruser|registernewuser|dologin|verifytoken|validatesession|getuser|getsessionuser|getcurrentuser|endsession|logout|signout|dologout|destroysession|invalidatesession|invalidate|signout)\b/i;
|
|
1005
|
+
const isAuthFunction = enclosingFuncName != null && (AUTH_PATTERN.test(rawLower) ||
|
|
1006
|
+
identifierParse(enclosingFuncName).some(w => AUTH_PATTERN.test(w)));
|
|
1007
|
+
// Filter rules by language
|
|
1008
|
+
const activeRulesV7 = language
|
|
1009
|
+
? SAFEGUARD_RULES.filter(r => !r.languages || r.languages.includes(language))
|
|
1010
|
+
: SAFEGUARD_RULES;
|
|
1011
|
+
for (const rule of activeRulesV7) {
|
|
1012
|
+
// v7b: trigger check against own body + file-level (same scope as v6)
|
|
1013
|
+
const triggerMatch = triggerEffectiveCalls.some(c => rule.trigger.test(c));
|
|
1014
|
+
if (!triggerMatch)
|
|
1015
|
+
continue;
|
|
1016
|
+
if (isAuthFunction && rule.category === "authorization")
|
|
1017
|
+
continue;
|
|
1018
|
+
// v7: safeguard check against FULL context (own + file + callers)
|
|
1019
|
+
const matchedSafeguard = rule.safeguards.find(s => [...safeContext].some(c => s.pattern.test(c)));
|
|
1020
|
+
if (matchedSafeguard)
|
|
1021
|
+
continue;
|
|
1022
|
+
// Check excludePatterns (library functions where safeguard is deferred to separate API)
|
|
1023
|
+
if (rule.excludePatterns) {
|
|
1024
|
+
const excluded = triggerEffectiveCalls.some(c => rule.excludePatterns.some(ep => ep.test(c)));
|
|
1025
|
+
if (excluded)
|
|
1026
|
+
continue;
|
|
1027
|
+
}
|
|
1028
|
+
violations.push({
|
|
1029
|
+
rule: rule.name,
|
|
1030
|
+
category: rule.category,
|
|
1031
|
+
type: "missing_safeguard",
|
|
1032
|
+
detail: rule.violationMessage,
|
|
1033
|
+
conceptDetail: `Missing: ${rule.conceptMissing.join(", ")}. Expected at least one of: ${rule.conceptExpected.join(", ")}`,
|
|
1034
|
+
missingConcepts: rule.conceptMissing,
|
|
1035
|
+
expectedConcepts: rule.conceptExpected,
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
return violations;
|
|
1039
|
+
}
|
|
1040
|
+
// ── Concept Mapping (Ontology → Detector) ──
|
|
1041
|
+
const CONCEPT_MAP = {
|
|
1042
|
+
"TLS Handshake": { "tls_init": ["ClientHello"], "tls_connect": ["ServerHello", "Certificate"], "tls_free": ["Finished"] },
|
|
1043
|
+
"SSH Connection": { "ssh_init": ["Connection"], "ssh_auth": ["Authentication"], "ssh_done": ["Channel"] },
|
|
1044
|
+
"HTTP Request": { "http_init": ["Request"], "http_send": ["Response"], "http_cleanup": ["Cleanup"] },
|
|
1045
|
+
"HTTP/2 Session": { "h2_init": ["Session Init"], "h2_send": ["Stream Submit"], "h2_close": ["Session Close"] },
|
|
1046
|
+
};
|
|
1047
|
+
function enrichWithConcepts(v, matchedLabels) {
|
|
1048
|
+
const m = CONCEPT_MAP[v.protocol];
|
|
1049
|
+
if (!m)
|
|
1050
|
+
return v;
|
|
1051
|
+
const mc = [], fc = [];
|
|
1052
|
+
for (const l of v.missing) {
|
|
1053
|
+
const c = m[l] || [l];
|
|
1054
|
+
mc.push(...c);
|
|
1055
|
+
}
|
|
1056
|
+
for (const l of matchedLabels) {
|
|
1057
|
+
const c = m[l] || [l];
|
|
1058
|
+
fc.push(...c);
|
|
1059
|
+
}
|
|
1060
|
+
v.missingConcepts = [...new Set(mc)];
|
|
1061
|
+
v.foundConcepts = [...new Set(fc)];
|
|
1062
|
+
v.conceptDetail = `Missing: ${v.missingConcepts.join(", ")}. Found: ${v.foundConcepts.join(", ")}`;
|
|
1063
|
+
return v;
|
|
1064
|
+
}
|
|
1065
|
+
function detectProtocolViolations(calls) {
|
|
1066
|
+
const violations = [];
|
|
1067
|
+
for (const proto of ALL_PROTOCOLS) {
|
|
1068
|
+
const matched = [];
|
|
1069
|
+
for (let ci = 0; ci < calls.length; ci++) {
|
|
1070
|
+
for (const step of proto.steps) {
|
|
1071
|
+
if (step.pattern.test(calls[ci])) {
|
|
1072
|
+
if (!matched.some(m => m.label === step.label))
|
|
1073
|
+
matched.push({ label: step.label, index: ci, required: step.required });
|
|
1074
|
+
break;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
if (matched.length < 2)
|
|
1079
|
+
continue;
|
|
1080
|
+
const requiredSteps = proto.steps.filter(s => s.required);
|
|
1081
|
+
const matchedRequired = matched.filter(m => m.required);
|
|
1082
|
+
const completeness = matchedRequired.length / requiredSteps.length;
|
|
1083
|
+
const missing = [];
|
|
1084
|
+
for (const rs of requiredSteps) {
|
|
1085
|
+
if (!matchedRequired.some(m => m.label === rs.label))
|
|
1086
|
+
missing.push(rs.label);
|
|
1087
|
+
}
|
|
1088
|
+
const labels = matched.map(m => m.label);
|
|
1089
|
+
if (missing.length > 0) {
|
|
1090
|
+
violations.push(enrichWithConcepts({ protocol: proto.name, category: proto.category, type: "missing_step", missing, detail: `${proto.name} missing: ${missing.join(", ")}. Found: ${labels.join(" → ")}` }, labels));
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
if (completeness < proto.minCompleteness) {
|
|
1094
|
+
violations.push(enrichWithConcepts({ protocol: proto.name, category: proto.category, type: "incomplete", missing: proto.steps.filter(s => s.required && !labels.includes(s.label)).map(s => s.label), detail: `${proto.name}: ${(completeness * 100).toFixed(0)}% complete. Found: ${labels.join(" → ")}` }, labels));
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
return violations;
|
|
1098
|
+
}
|
|
1099
|
+
function validateProtocolState(calls) {
|
|
1100
|
+
const violations = detectProtocolViolations(calls);
|
|
1101
|
+
const matchedProtocols = [...new Set(violations.map(v => v.protocol))];
|
|
1102
|
+
const detail = violations.length === 0 ? "Protocol state complete" : violations.map(v => v.conceptDetail || v.detail).join("; ");
|
|
1103
|
+
return { valid: violations.length === 0, violations, matchedProtocols, detail };
|
|
1104
|
+
}
|
|
1105
|
+
function validateCombined(calls, enclosingFuncName) {
|
|
1106
|
+
const { validateResourceLifecycle } = require("./resource-detector");
|
|
1107
|
+
const res = validateResourceLifecycle(calls, enclosingFuncName);
|
|
1108
|
+
const proto = validateProtocolState(calls);
|
|
1109
|
+
const safe = detectSafeguardViolations(calls, enclosingFuncName);
|
|
1110
|
+
const all = [...res.violations, ...proto.violations, ...safe];
|
|
1111
|
+
return { valid: all.length === 0, resourceViolations: res.violations, protocolViolations: proto.violations, safeguardViolations: safe, detail: all.map((v) => v.detail || "").join("; ") || "All checks passed" };
|
|
1112
|
+
}
|
|
1113
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1114
|
+
// CLI
|
|
1115
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1116
|
+
if (require.main === module) {
|
|
1117
|
+
const fs = require("fs");
|
|
1118
|
+
const path = require("path");
|
|
1119
|
+
const args = process.argv.slice(2);
|
|
1120
|
+
const repoPath = path.resolve(args.find((a) => !a.startsWith("--")) || ".");
|
|
1121
|
+
const baseDir = path.dirname(repoPath);
|
|
1122
|
+
const repoName = path.basename(repoPath);
|
|
1123
|
+
const seqFile = path.join(baseDir, `${repoName}-sequences.json`);
|
|
1124
|
+
const labelFile = path.join(baseDir, `${repoName}-labels.json`);
|
|
1125
|
+
const mode = args.includes("--protocol") ? "protocol" : args.includes("--resource") ? "resource" : "combined";
|
|
1126
|
+
if (!fs.existsSync(seqFile) || !fs.existsSync(labelFile)) {
|
|
1127
|
+
console.error("Files not found");
|
|
1128
|
+
process.exit(1);
|
|
1129
|
+
}
|
|
1130
|
+
const seqData = JSON.parse(fs.readFileSync(seqFile, "utf-8"));
|
|
1131
|
+
const labelData = JSON.parse(fs.readFileSync(labelFile, "utf-8"));
|
|
1132
|
+
const sequences = seqData.sequences || seqData;
|
|
1133
|
+
const labels = labelData.labels || labelData;
|
|
1134
|
+
let tp = 0, fp = 0, tn = 0, fn = 0;
|
|
1135
|
+
const mismatches = [];
|
|
1136
|
+
for (const seq of sequences) {
|
|
1137
|
+
const idx = sequences.indexOf(seq);
|
|
1138
|
+
const expected = labels[idx];
|
|
1139
|
+
if (!expected || expected === "s" || expected === "skip")
|
|
1140
|
+
continue;
|
|
1141
|
+
const funcName = seq.function || "";
|
|
1142
|
+
let detected;
|
|
1143
|
+
let detail = "";
|
|
1144
|
+
if (mode === "resource") {
|
|
1145
|
+
const r = require("./resource-detector").validateResourceLifecycle(seq.calls || [], funcName);
|
|
1146
|
+
detected = r.valid ? "clean" : "violation";
|
|
1147
|
+
detail = r.detail;
|
|
1148
|
+
}
|
|
1149
|
+
else if (mode === "protocol") {
|
|
1150
|
+
const r = validateProtocolState(seq.calls || []);
|
|
1151
|
+
detected = r.valid ? "clean" : "violation";
|
|
1152
|
+
detail = r.detail;
|
|
1153
|
+
}
|
|
1154
|
+
else {
|
|
1155
|
+
const r = validateCombined(seq.calls || [], funcName);
|
|
1156
|
+
detected = r.valid ? "clean" : "violation";
|
|
1157
|
+
detail = r.detail;
|
|
1158
|
+
}
|
|
1159
|
+
if (expected === "violation" && detected === "violation")
|
|
1160
|
+
tp++;
|
|
1161
|
+
else if (expected === "clean" && detected === "violation")
|
|
1162
|
+
fp++;
|
|
1163
|
+
else if (expected === "clean" && detected === "clean")
|
|
1164
|
+
tn++;
|
|
1165
|
+
else if (expected === "violation" && detected === "clean")
|
|
1166
|
+
fn++;
|
|
1167
|
+
if (expected !== detected)
|
|
1168
|
+
mismatches.push({ idx, fn: funcName, expected, detected, calls: (seq.calls || []).slice(0, 5), detail });
|
|
1169
|
+
}
|
|
1170
|
+
const total = tp + fp + tn + fn;
|
|
1171
|
+
const P = tp + fp > 0 ? tp / (tp + fp) : 0;
|
|
1172
|
+
const R = tp + fn > 0 ? tp / (tp + fn) : 0;
|
|
1173
|
+
const F1 = P + R > 0 ? 2 * P * R / (P + R) : 0;
|
|
1174
|
+
const pct = (v) => `${(v * 100).toFixed(0)}%`;
|
|
1175
|
+
const C = { r: "\x1b[0m", b: "\x1b[1m", d: "\x1b[2m", g: "\x1b[32m", r2: "\x1b[31m", y: "\x1b[33m", c: "\x1b[36m" };
|
|
1176
|
+
const clr = (v) => v >= 0.7 ? C.g : v >= 0.5 ? C.y : C.r2;
|
|
1177
|
+
const label = mode === "resource" ? "Resource Lifecycle" : mode === "protocol" ? "Protocol State Machine" : "Combined (Resource + Protocol)";
|
|
1178
|
+
console.log(`\n${C.b}${C.c}╔══════════════════════════════════════════════╗${C.r}`);
|
|
1179
|
+
console.log(`${C.b}${C.c}║${C.r} ${C.b}${label} — ${repoName}${C.r}${" ".repeat(Math.max(0, 30 - label.length - repoName.length))}${C.b}${C.c}║${C.r}`);
|
|
1180
|
+
console.log(`${C.b}${C.c}╚══════════════════════════════════════════════╝${C.r}\n`);
|
|
1181
|
+
console.log(` Samples: ${total}\n TP: ${C.g}${tp}${C.r} FP: ${C.r2}${fp}${C.r} TN: ${C.g}${tn}${C.r} FN: ${C.r2}${fn}${C.r}\n`);
|
|
1182
|
+
console.log(` Precision: ${clr(P)}${pct(P)}${C.r}\n Recall: ${clr(R)}${pct(R)}${C.r}\n F1: ${clr(F1)}${pct(F1)}${C.r}\n`);
|
|
1183
|
+
if (mismatches.length > 0) {
|
|
1184
|
+
console.log(` ${C.y}Details:${C.r}`);
|
|
1185
|
+
for (const m of mismatches.slice(0, 12))
|
|
1186
|
+
console.log(` ${m.expected === "violation" ? C.r2 + "FN" : C.y + "FP"}${C.r} [${m.idx}] ${m.expected}→${m.detected} ${(m.calls || []).join(" → ")}` + (m.detail ? `\n ${C.d}${m.detail.slice(0, 100)}${C.r}` : ""));
|
|
1187
|
+
}
|
|
1188
|
+
console.log(`\n Rating: ${F1 >= 0.7 ? C.g + "GOOD" : F1 >= 0.5 ? C.y + "FAIR" : C.r2 + "NEEDS IMPROVEMENT"}${C.r}\n`);
|
|
1189
|
+
}
|