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,697 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Phase 1: Protocol Domain Validator
|
|
4
|
+
*
|
|
5
|
+
* Validates semantic sequences against known protocol domains.
|
|
6
|
+
* REPLACES the old anomaly-detection approach ("deviation from norm = violation")
|
|
7
|
+
* with domain-aware validation ("does this sequence follow valid protocol patterns?").
|
|
8
|
+
*
|
|
9
|
+
* Core principle: if a call sequence can be semantically mapped to a known
|
|
10
|
+
* protocol domain, and the sequence follows sensible patterns for that domain,
|
|
11
|
+
* it is CLEAN — regardless of whether the SSG has seen this exact pattern before.
|
|
12
|
+
*
|
|
13
|
+
* Architecture:
|
|
14
|
+
* SemanticSequence → Domain Grouping → Pattern Matching → Violation[]
|
|
15
|
+
* (empty violations = CLEAN)
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.SPECIFIC_VIOLATION_CHECKS = void 0;
|
|
19
|
+
exports.validateSemanticSequence = validateSemanticSequence;
|
|
20
|
+
exports.checkSpecificViolations = checkSpecificViolations;
|
|
21
|
+
exports.batchValidateSequences = batchValidateSequences;
|
|
22
|
+
const DOMAIN_GROUPS = [
|
|
23
|
+
{
|
|
24
|
+
name: "TLS/SSL",
|
|
25
|
+
description: "TLS 连接建立、证书验证、会话管理",
|
|
26
|
+
domains: ["tls_config", "tls_handshake", "tls_cert", "tls_session", "tls_alpn"],
|
|
27
|
+
isWellKnown: true,
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "Authentication",
|
|
31
|
+
description: "用户认证、凭证管理、SASL/SSPI/GSSAPI 协商",
|
|
32
|
+
domains: ["auth_cred", "auth_mech", "auth_hash", "auth_spn", "auth_gssapi"],
|
|
33
|
+
isWellKnown: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "LDAP",
|
|
37
|
+
description: "LDAP 目录服务协议",
|
|
38
|
+
domains: ["ldap_ops"],
|
|
39
|
+
isWellKnown: true,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "FTP",
|
|
43
|
+
description: "FTP 文件传输协议",
|
|
44
|
+
domains: ["ftp_ops"],
|
|
45
|
+
isWellKnown: true,
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
name: "SMTP",
|
|
49
|
+
description: "SMTP 邮件传输协议",
|
|
50
|
+
domains: ["smtp_ops"],
|
|
51
|
+
isWellKnown: true,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "IMAP",
|
|
55
|
+
description: "IMAP 邮件访问协议",
|
|
56
|
+
domains: ["imap_ops"],
|
|
57
|
+
isWellKnown: true,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: "MQTT",
|
|
61
|
+
description: "MQTT 物联网消息协议",
|
|
62
|
+
domains: ["mqtt_ops"],
|
|
63
|
+
isWellKnown: true,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "SMB",
|
|
67
|
+
description: "SMB 文件共享协议",
|
|
68
|
+
domains: ["smb_ops"],
|
|
69
|
+
isWellKnown: true,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "SSH/SFTP",
|
|
73
|
+
description: "SSH 安全外壳 + SFTP 文件传输",
|
|
74
|
+
domains: ["ssh_ops"],
|
|
75
|
+
isWellKnown: true,
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "Telnet",
|
|
79
|
+
description: "Telnet 远程终端协议",
|
|
80
|
+
domains: ["telnet_ops"],
|
|
81
|
+
isWellKnown: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "DNS/DoH",
|
|
85
|
+
description: "DNS 解析 + DNS-over-HTTPS",
|
|
86
|
+
domains: ["dns_ops"],
|
|
87
|
+
isWellKnown: true,
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "HTTP",
|
|
91
|
+
description: "HTTP/1.x 请求/响应处理",
|
|
92
|
+
domains: ["http_ops"],
|
|
93
|
+
isWellKnown: true,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "HTTP/2",
|
|
97
|
+
description: "HTTP/2 会话管理",
|
|
98
|
+
domains: ["http2_ops"],
|
|
99
|
+
isWellKnown: true,
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "Connection Management",
|
|
103
|
+
description: "连接生命周期、轮询、事件循环",
|
|
104
|
+
domains: ["conn_mgmt", "conn_poll"],
|
|
105
|
+
isWellKnown: true,
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
// Noise domains that appear in all protocol implementations
|
|
109
|
+
const NOISE_DOMAINS = [
|
|
110
|
+
"util", "mem_alloc", "mem_free", "mem_util",
|
|
111
|
+
"str_util", "str_format", "buf_util", "net_util",
|
|
112
|
+
"platform_util", "debug_trace", "error_handle",
|
|
113
|
+
];
|
|
114
|
+
// ═══════════════════════════════════════════════════════════════
|
|
115
|
+
// Compatibility Matrix — which groups can coexist in a valid seq
|
|
116
|
+
// ═══════════════════════════════════════════════════════════════
|
|
117
|
+
/**
|
|
118
|
+
* Groups that commonly appear together in valid protocol implementations.
|
|
119
|
+
* e.g., TLS + Auth together is normal (mutual TLS auth),
|
|
120
|
+
* LDAP + Auth is normal (LDAP bind with SASL),
|
|
121
|
+
* HTTP + TLS is normal (HTTPS).
|
|
122
|
+
*/
|
|
123
|
+
const COMPATIBLE_GROUPS = {
|
|
124
|
+
"TLS/SSL": ["Authentication", "HTTP", "HTTP/2", "FTP", "SMTP", "IMAP", "LDAP", "SSH/SFTP", "Connection Management", "DNS/DoH"],
|
|
125
|
+
"Authentication": ["TLS/SSL", "LDAP", "SSH/SFTP", "SMTP", "IMAP", "HTTP", "Connection Management"],
|
|
126
|
+
"LDAP": ["TLS/SSL", "Authentication", "Connection Management"],
|
|
127
|
+
"FTP": ["TLS/SSL", "Connection Management"],
|
|
128
|
+
"SMTP": ["TLS/SSL", "Authentication", "Connection Management"],
|
|
129
|
+
"IMAP": ["TLS/SSL", "Authentication", "HTTP", "Connection Management"],
|
|
130
|
+
"MQTT": ["TLS/SSL", "Connection Management"],
|
|
131
|
+
"SMB": ["Connection Management"],
|
|
132
|
+
"SSH/SFTP": ["TLS/SSL", "Authentication", "Connection Management"],
|
|
133
|
+
"Telnet": ["Connection Management"],
|
|
134
|
+
"DNS/DoH": ["TLS/SSL", "HTTP", "Connection Management"],
|
|
135
|
+
"HTTP": ["TLS/SSL", "Authentication", "HTTP/2", "IMAP", "SMTP", "Connection Management"],
|
|
136
|
+
"HTTP/2": ["TLS/SSL", "HTTP", "Connection Management"],
|
|
137
|
+
"Connection Management": ["TLS/SSL", "Authentication", "LDAP", "FTP", "SMTP", "IMAP", "MQTT", "SMB", "SSH/SFTP", "Telnet", "DNS/DoH", "HTTP", "HTTP/2"],
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Classify a ProtocolDomain into its DomainGroup name.
|
|
141
|
+
*/
|
|
142
|
+
function classifyDomain(domain) {
|
|
143
|
+
for (const group of DOMAIN_GROUPS) {
|
|
144
|
+
if (group.domains.includes(domain)) {
|
|
145
|
+
return group.name;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Check if two domain groups are compatible (can appear together in a valid sequence).
|
|
152
|
+
*/
|
|
153
|
+
function areGroupsCompatible(group1, group2) {
|
|
154
|
+
if (group1 === group2)
|
|
155
|
+
return true;
|
|
156
|
+
const compat = COMPATIBLE_GROUPS[group1];
|
|
157
|
+
return compat ? compat.includes(group2) : false;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Validate a semantic sequence against known protocol domains.
|
|
161
|
+
*
|
|
162
|
+
* Algorithm:
|
|
163
|
+
* 1. Extract non-noise domains from the sequence
|
|
164
|
+
* 2. Group consecutive domains into domain groups
|
|
165
|
+
* 3. Check if all groups are mutually compatible
|
|
166
|
+
* 4. If yes → CLEAN (valid protocol operation)
|
|
167
|
+
* 5. If no → flag as potential cross-domain violation
|
|
168
|
+
*
|
|
169
|
+
* This is intentionally lenient — it only flags sequences that clearly
|
|
170
|
+
* mix incompatible protocol domains without proper transitions.
|
|
171
|
+
*/
|
|
172
|
+
function validateSemanticSequence(seq) {
|
|
173
|
+
// Extract non-noise, non-util domains
|
|
174
|
+
const significantDomains = seq.domains.filter(d => !NOISE_DOMAINS.includes(d) && d !== "util");
|
|
175
|
+
// If no significant domains → can't validate, assume clean
|
|
176
|
+
if (significantDomains.length === 0) {
|
|
177
|
+
return {
|
|
178
|
+
valid: true,
|
|
179
|
+
groups: [],
|
|
180
|
+
primaryGroup: null,
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
// Map to groups, deduplicate
|
|
184
|
+
const groupSet = new Set();
|
|
185
|
+
for (const domain of significantDomains) {
|
|
186
|
+
const group = classifyDomain(domain);
|
|
187
|
+
if (group)
|
|
188
|
+
groupSet.add(group);
|
|
189
|
+
}
|
|
190
|
+
const groups = [...groupSet];
|
|
191
|
+
// Single group → always valid
|
|
192
|
+
if (groups.length <= 1) {
|
|
193
|
+
return {
|
|
194
|
+
valid: true,
|
|
195
|
+
groups,
|
|
196
|
+
primaryGroup: groups[0] || null,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
// Multiple groups → check compatibility
|
|
200
|
+
for (let i = 0; i < groups.length; i++) {
|
|
201
|
+
for (let j = i + 1; j < groups.length; j++) {
|
|
202
|
+
if (!areGroupsCompatible(groups[i], groups[j])) {
|
|
203
|
+
return {
|
|
204
|
+
valid: false,
|
|
205
|
+
groups,
|
|
206
|
+
primaryGroup: groups[0],
|
|
207
|
+
reason: `Incompatible protocol domains: ${groups[i]} and ${groups[j]} cannot appear together without proper transitions`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// All groups compatible → valid protocol operation
|
|
213
|
+
return {
|
|
214
|
+
valid: true,
|
|
215
|
+
groups,
|
|
216
|
+
primaryGroup: groups[0],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function classifyTlsAction(step) {
|
|
220
|
+
const api = step.api.toLowerCase();
|
|
221
|
+
// Actual handshake execution
|
|
222
|
+
// Only unambiguously handshake-only operations.
|
|
223
|
+
// "_connect(" as a function call (not "connection"), or explicit handshake calls.
|
|
224
|
+
// cr_send/send excluded: can be post-handshake data transfer.
|
|
225
|
+
if ((api.includes("_connect") && !api.includes("connection")) ||
|
|
226
|
+
api.includes("_do_handshake") ||
|
|
227
|
+
(api.includes("_handshake") && !api.includes("_is_handshaking"))) {
|
|
228
|
+
return "handshake_exec";
|
|
229
|
+
}
|
|
230
|
+
// CTX management (setup, not handshake)
|
|
231
|
+
if (api.includes("_ctx_new") ||
|
|
232
|
+
api.includes("_ctx_free") ||
|
|
233
|
+
api.includes("_client_method") ||
|
|
234
|
+
api.includes("_server_method") ||
|
|
235
|
+
api.includes("_init_backend") ||
|
|
236
|
+
api.includes("acquire_credential_handle") // Schannel cred acquisition
|
|
237
|
+
) {
|
|
238
|
+
return "ctx_init";
|
|
239
|
+
}
|
|
240
|
+
// Status/info queries
|
|
241
|
+
if (api.includes("_is_handshaking") ||
|
|
242
|
+
api.includes("_get_version") ||
|
|
243
|
+
api.includes("_get_peer_cert") ||
|
|
244
|
+
api.includes("_get_ciphersuite") ||
|
|
245
|
+
api.includes("_get_negotiated_") ||
|
|
246
|
+
api.includes("_get_protocol_version")) {
|
|
247
|
+
return "status_query";
|
|
248
|
+
}
|
|
249
|
+
return "other";
|
|
250
|
+
}
|
|
251
|
+
exports.SPECIFIC_VIOLATION_CHECKS = [
|
|
252
|
+
// ═══════════════════════════════════════════════════════
|
|
253
|
+
// TLS-001: TLS handshake without certificate verification
|
|
254
|
+
//
|
|
255
|
+
// Phase 3: Only counts actual handshake execution steps
|
|
256
|
+
// (connect, handshake), NOT CTX creation or status queries.
|
|
257
|
+
// Excludes session reuse (cert was verified in original session).
|
|
258
|
+
// ═══════════════════════════════════════════════════════
|
|
259
|
+
{
|
|
260
|
+
ruleId: "TLS_NO_CERT_VERIFY",
|
|
261
|
+
description: "TLS handshake performed without certificate verification. " +
|
|
262
|
+
"Server identity must be cryptographically verified to prevent MITM attacks.",
|
|
263
|
+
check: (steps) => {
|
|
264
|
+
// Phase 3: only count actual handshake execution
|
|
265
|
+
const handshakeExecSteps = steps.filter((s) => s.domain === "tls_handshake" &&
|
|
266
|
+
classifyTlsAction(s) === "handshake_exec");
|
|
267
|
+
const hasActualHandshake = handshakeExecSteps.length >= 1;
|
|
268
|
+
const hasCertVerify = steps.some((s) => s.domain === "tls_cert");
|
|
269
|
+
const hasSessionReuse = steps.some((s) => s.domain === "tls_session" ||
|
|
270
|
+
s.api.toLowerCase().includes("scache") ||
|
|
271
|
+
s.api.toLowerCase().includes("session_reuse") ||
|
|
272
|
+
s.api.toLowerCase().includes("session_set_data") ||
|
|
273
|
+
s.api.toLowerCase().includes("session_resume"));
|
|
274
|
+
return hasActualHandshake && !hasCertVerify && !hasSessionReuse;
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
// ═══════════════════════════════════════════════════════
|
|
278
|
+
// AUTH-001: Plaintext auth credentials in network context
|
|
279
|
+
//
|
|
280
|
+
// Only triggers when auth data is being actively prepared AND
|
|
281
|
+
// network transmission is occurring without TLS protection.
|
|
282
|
+
// ═══════════════════════════════════════════════════════
|
|
283
|
+
{
|
|
284
|
+
ruleId: "PLAINTEXT_AUTH_WITHOUT_TLS",
|
|
285
|
+
description: "Authentication credentials transmitted without TLS protection. " +
|
|
286
|
+
"SASL PLAIN, LOGIN, or credential data must be sent over an encrypted channel.",
|
|
287
|
+
check: (steps) => {
|
|
288
|
+
// Auth data being prepared (credentials + mechanism)
|
|
289
|
+
const hasAuthCred = steps.some((s) => s.domain === "auth_cred");
|
|
290
|
+
const hasAuthCreate = steps.some((s) => s.domain === "auth_mech" &&
|
|
291
|
+
(s.api.toLowerCase().includes("create") ||
|
|
292
|
+
s.api.toLowerCase().includes("build") ||
|
|
293
|
+
s.api.toLowerCase().includes("encode")));
|
|
294
|
+
// Network activity — data being sent or connection being used
|
|
295
|
+
const hasNetworkTx = steps.some((s) => s.domain === "conn_mgmt" &&
|
|
296
|
+
(s.api.toLowerCase().includes("send") ||
|
|
297
|
+
s.api.toLowerCase().includes("flush") ||
|
|
298
|
+
s.api.toLowerCase().includes("connect") ||
|
|
299
|
+
s.api.toLowerCase().includes("write") ||
|
|
300
|
+
s.api.toLowerCase().includes("perform") ||
|
|
301
|
+
s.api.toLowerCase().includes("get_current_host")));
|
|
302
|
+
// TLS protection present
|
|
303
|
+
const hasTls = steps.some((s) => s.domain === "tls_handshake" ||
|
|
304
|
+
s.domain === "tls_cert" ||
|
|
305
|
+
s.domain === "tls_session" ||
|
|
306
|
+
(s.domain === "tls_config" &&
|
|
307
|
+
(s.api.toLowerCase().includes("is_ssl") ||
|
|
308
|
+
s.api.toLowerCase().includes("ssl_cf"))));
|
|
309
|
+
// Only flag if auth is being actively created for network transmission
|
|
310
|
+
return (hasAuthCred || hasAuthCreate) && hasNetworkTx && !hasTls;
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
// ═══════════════════════════════════════════════════════
|
|
314
|
+
// SSH-001: SSH auth without host key verification
|
|
315
|
+
//
|
|
316
|
+
// Looks for SSH auth operations that lack host key checking.
|
|
317
|
+
// The SSH domain itself contains auth state machines; we check
|
|
318
|
+
// that hostkey-related steps appear before auth steps.
|
|
319
|
+
// ═══════════════════════════════════════════════════════
|
|
320
|
+
{
|
|
321
|
+
ruleId: "SSH_NO_HOST_KEY_CHECK",
|
|
322
|
+
description: "SSH authentication proceeds without host key verification. " +
|
|
323
|
+
"The server's host key must be verified before sending credentials.",
|
|
324
|
+
check: (steps) => {
|
|
325
|
+
const sshSteps = steps.filter((s) => s.domain === "ssh_ops");
|
|
326
|
+
// Require substantial SSH activity (not just internal helpers)
|
|
327
|
+
if (sshSteps.length < 4)
|
|
328
|
+
return false;
|
|
329
|
+
const hasHostKey = sshSteps.some((s) => s.api.toLowerCase().includes("hostkey") ||
|
|
330
|
+
s.api.toLowerCase().includes("host_key") ||
|
|
331
|
+
s.api.toLowerCase().includes("known_host"));
|
|
332
|
+
// Only flag if explicit auth operations AND user credential usage
|
|
333
|
+
const hasAuth = sshSteps.some((s) => (s.api.toLowerCase().includes("userauth") ||
|
|
334
|
+
s.api.toLowerCase().includes("auth_pass") ||
|
|
335
|
+
s.api.toLowerCase().includes("auth_pkey") ||
|
|
336
|
+
s.api.toLowerCase().includes("auth_key")) &&
|
|
337
|
+
!s.api.toLowerCase().includes("authlist"));
|
|
338
|
+
// Also require credential operations (not just internal SSH protocol)
|
|
339
|
+
const hasCreds = steps.some((s) => s.domain === "auth_cred" ||
|
|
340
|
+
s.api.toLowerCase().includes("password") ||
|
|
341
|
+
s.api.toLowerCase().includes("credential"));
|
|
342
|
+
return hasAuth && hasCreds && !hasHostKey;
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
// ═══════════════════════════════════════════════════════
|
|
346
|
+
// TLS-002: OCSP response parsed without verification
|
|
347
|
+
// ═══════════════════════════════════════════════════════
|
|
348
|
+
{
|
|
349
|
+
ruleId: "OCSP_UNVERIFIED_RESPONSE",
|
|
350
|
+
description: "OCSP stapling response is parsed but the verification result is not checked. " +
|
|
351
|
+
"An unverified OCSP response provides no certificate revocation protection.",
|
|
352
|
+
check: (steps) => {
|
|
353
|
+
const hasOcspParse = steps.some((s) => s.api.toLowerCase().includes("ocsp_response_status") ||
|
|
354
|
+
s.api.toLowerCase().includes("ocsp_response_get"));
|
|
355
|
+
const hasOcspVerify = steps.some((s) => s.api.toLowerCase().includes("ocsp_basic_verify") ||
|
|
356
|
+
s.api.toLowerCase().includes("ocsp_check"));
|
|
357
|
+
return hasOcspParse && !hasOcspVerify;
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
// ═══════════════════════════════════════════════════════
|
|
361
|
+
// AUTH-002: SRP authentication (legacy PAKE)
|
|
362
|
+
// ═══════════════════════════════════════════════════════
|
|
363
|
+
{
|
|
364
|
+
ruleId: "AUTH_SRP_USAGE",
|
|
365
|
+
description: "SRP (Secure Remote Password) authentication detected. " +
|
|
366
|
+
"SRP is a legacy PAKE with known limitations. Prefer modern alternatives.",
|
|
367
|
+
check: (steps) => {
|
|
368
|
+
return steps.some((s) => s.api.toLowerCase().includes("srp_") &&
|
|
369
|
+
(s.domain === "auth_cred" || s.domain === "auth_mech"));
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
// ═══════════════════════════════════════════════════════
|
|
373
|
+
// TLS-003: Security verification result not consumed
|
|
374
|
+
//
|
|
375
|
+
// Detects when a security verification function is called
|
|
376
|
+
// (OCSP_basic_verify, SSL_get_verify_result, X509_verify_cert)
|
|
377
|
+
// but the result is not checked before proceeding.
|
|
378
|
+
// ═══════════════════════════════════════════════════════
|
|
379
|
+
{
|
|
380
|
+
ruleId: "SECURITY_RESULT_NOT_CHECKED",
|
|
381
|
+
description: "Security verification result may not be checked. " +
|
|
382
|
+
"A verification function was called but no subsequent " +
|
|
383
|
+
"status check or conditional guard was detected.",
|
|
384
|
+
check: (steps) => {
|
|
385
|
+
const verifyApis = [
|
|
386
|
+
"ocsp_basic_verify",
|
|
387
|
+
"ssl_get_verify_result",
|
|
388
|
+
"x509_verify_cert",
|
|
389
|
+
"cert_verify",
|
|
390
|
+
];
|
|
391
|
+
// Find verification calls
|
|
392
|
+
const verifyIndices = [];
|
|
393
|
+
steps.forEach((s, i) => {
|
|
394
|
+
if (s.domain === "tls_cert" &&
|
|
395
|
+
verifyApis.some((v) => s.api.toLowerCase().includes(v))) {
|
|
396
|
+
verifyIndices.push(i);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
if (verifyIndices.length === 0)
|
|
400
|
+
return false;
|
|
401
|
+
// For the last verify call, check if subsequent steps
|
|
402
|
+
// include error handling or status checks
|
|
403
|
+
const lastVerifyIdx = verifyIndices[verifyIndices.length - 1];
|
|
404
|
+
const subsequentSteps = steps.slice(lastVerifyIdx + 1);
|
|
405
|
+
// Look for: error string functions, status checks, conditional guards
|
|
406
|
+
const hasResultCheck = subsequentSteps.some((s) => s.domain === "error_handle" ||
|
|
407
|
+
s.api.toLowerCase().includes("status") ||
|
|
408
|
+
s.api.toLowerCase().includes("strerror") ||
|
|
409
|
+
s.api.toLowerCase().includes("get_error") ||
|
|
410
|
+
s.api.toLowerCase().includes("_ok") ||
|
|
411
|
+
s.api.toLowerCase().includes("_err"));
|
|
412
|
+
// If verify is called and the NEXT step immediately uses the
|
|
413
|
+
// result of what was verified (e.g., gets peer cert after OCSP verify
|
|
414
|
+
// without checking verify result), it's suspicious
|
|
415
|
+
const immediateUse = subsequentSteps.length > 0 &&
|
|
416
|
+
subsequentSteps[0].domain === "tls_cert" &&
|
|
417
|
+
!hasResultCheck;
|
|
418
|
+
return immediateUse;
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
// ═══════════════════════════════════════════════════════
|
|
422
|
+
// SSH-002: SSH host key retrieved but not verified
|
|
423
|
+
//
|
|
424
|
+
// Detects when an SSH host key is obtained (ssh_state_hostkey,
|
|
425
|
+
// ssh_get_publickey_hash) but no known_hosts comparison follows.
|
|
426
|
+
// ═══════════════════════════════════════════════════════
|
|
427
|
+
{
|
|
428
|
+
ruleId: "SSH_HOST_KEY_NOT_VERIFIED",
|
|
429
|
+
description: "SSH host key retrieved but verification against known_hosts " +
|
|
430
|
+
"may be insufficient. Host key must be cryptographically verified.",
|
|
431
|
+
check: (steps) => {
|
|
432
|
+
// Check if hostkey-related step exists
|
|
433
|
+
const hostKeyIdx = steps.findIndex((s) => s.domain === "ssh_ops" &&
|
|
434
|
+
(s.api.toLowerCase().includes("hostkey") ||
|
|
435
|
+
s.api.toLowerCase().includes("host_key")));
|
|
436
|
+
if (hostKeyIdx < 0)
|
|
437
|
+
return false;
|
|
438
|
+
// Check if a verification/comparison step follows
|
|
439
|
+
const afterHostKey = steps.slice(hostKeyIdx + 1);
|
|
440
|
+
const hasVerify = afterHostKey.some((s) => s.api.toLowerCase().includes("verify") ||
|
|
441
|
+
s.api.toLowerCase().includes("check") ||
|
|
442
|
+
s.api.toLowerCase().includes("match") ||
|
|
443
|
+
s.api.toLowerCase().includes("compare") ||
|
|
444
|
+
s.api.toLowerCase().includes("known_host") ||
|
|
445
|
+
s.api.toLowerCase().includes("fingerprint"));
|
|
446
|
+
// Host key present but no verification detected
|
|
447
|
+
return !hasVerify;
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
// ═══════════════════════════════════════════════════════
|
|
451
|
+
// TLS-004: SSL connection established but no cert verify visible
|
|
452
|
+
//
|
|
453
|
+
// Phase 4: Cross-function inference. When an SSL connection is
|
|
454
|
+
// clearly being made (conn_is_ssl + do_connect/perform) but no
|
|
455
|
+
// certificate verification is visible in the same function window,
|
|
456
|
+
// this is suspicious. The verification might be in a callback,
|
|
457
|
+
// but we can't confirm from this window alone.
|
|
458
|
+
// Severity: LOW (cross-function uncertainty).
|
|
459
|
+
// ═══════════════════════════════════════════════════════
|
|
460
|
+
{
|
|
461
|
+
ruleId: "TLS_INFERRED_NO_CERT_VERIFY",
|
|
462
|
+
description: "SSL connection established but certificate verification " +
|
|
463
|
+
"not visible in this function window. Verify that cert validation " +
|
|
464
|
+
"is configured in the TLS backend callback or calling function.",
|
|
465
|
+
check: (steps) => {
|
|
466
|
+
// SSL is explicitly confirmed (conn_is_ssl or SSL filter/layer operations)
|
|
467
|
+
const hasSslConfirmed = steps.some((s) => s.api.toLowerCase().includes("conn_is_ssl") ||
|
|
468
|
+
s.api === "do_connect" ||
|
|
469
|
+
(s.domain === "tls_config" &&
|
|
470
|
+
/ssl|tls/i.test(s.api) &&
|
|
471
|
+
!s.api.toLowerCase().includes("close_all")) // closing all SSL is cleanup, not active use
|
|
472
|
+
);
|
|
473
|
+
// Connection is being established (but NOT "connection" which is a noun)
|
|
474
|
+
const hasConnect = steps.some((s) => (s.domain === "conn_mgmt" || s.api === "do_connect") &&
|
|
475
|
+
(/(?<!n)connect(?!i)/i.test(s.api) || // "connect" not in "connection"
|
|
476
|
+
s.api.toLowerCase().includes("perform") ||
|
|
477
|
+
s.api === "do_connect"));
|
|
478
|
+
// No cert verification visible in this window
|
|
479
|
+
const hasCertVerify = steps.some((s) => s.domain === "tls_cert");
|
|
480
|
+
// No session reuse
|
|
481
|
+
const hasSessionReuse = steps.some((s) => s.domain === "tls_session" ||
|
|
482
|
+
s.api.toLowerCase().includes("scache") ||
|
|
483
|
+
s.api.toLowerCase().includes("session_"));
|
|
484
|
+
// Phase 5: SSL filter explicitly added — cert verification is
|
|
485
|
+
// handled inside the filter during connect. Don't flag.
|
|
486
|
+
const hasSslFilterSetup = steps.some((s) => s.api.toLowerCase().includes("ssl_cfilter") ||
|
|
487
|
+
s.api.toLowerCase().includes("ssl_filter_add") ||
|
|
488
|
+
s.api.toLowerCase().includes("ssl_setup"));
|
|
489
|
+
return hasSslConfirmed && hasConnect &&
|
|
490
|
+
!hasCertVerify && !hasSessionReuse && !hasSslFilterSetup;
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
// ═══════════════════════════════════════════════════════
|
|
494
|
+
// TLS-005: TLS handshake without hostname verification
|
|
495
|
+
//
|
|
496
|
+
// CVE-2024-2466 pattern: mbedtls_ssl_set_hostname() only called
|
|
497
|
+
// when SNI is set → connecting by IP address skips hostname check.
|
|
498
|
+
// Also covers similar patterns in OpenSSL (SSL_set_tlsext_host_name),
|
|
499
|
+
// GnuTLS (gnutls_server_name_set), wolfSSL (wolfSSL_CTX_set_verify),
|
|
500
|
+
// and Rustls (cr_set_negotiated_alpn with hostname).
|
|
501
|
+
// ═══════════════════════════════════════════════════════
|
|
502
|
+
{
|
|
503
|
+
ruleId: "TLS_NO_HOSTNAME_VERIFY",
|
|
504
|
+
description: "TLS handshake performed without hostname verification. " +
|
|
505
|
+
"Without hostname verification, an attacker can present a valid " +
|
|
506
|
+
"certificate for a different hostname (CVE-2024-2466 pattern).",
|
|
507
|
+
check: (steps) => {
|
|
508
|
+
const hasTlsHandshake = steps.some((s) => s.domain === "tls_handshake" &&
|
|
509
|
+
classifyTlsAction(s) === "handshake_exec");
|
|
510
|
+
// Hostname/SNI verification functions across TLS backends
|
|
511
|
+
const hasHostnameSet = steps.some((s) => s.api.toLowerCase().includes("ssl_set_hostname") || // mbedTLS
|
|
512
|
+
s.api.toLowerCase().includes("set_tlsext_host_name") || // OpenSSL
|
|
513
|
+
s.api.toLowerCase().includes("server_name_set") || // GnuTLS
|
|
514
|
+
s.api.toLowerCase().includes("ctx_set_verify") || // wolfSSL
|
|
515
|
+
s.api.toLowerCase().includes("set_hostname") || // Generic
|
|
516
|
+
s.api.toLowerCase().includes("sni_") // SNI
|
|
517
|
+
);
|
|
518
|
+
// Exclude session reuse / early data / proxy context
|
|
519
|
+
// (hostname was verified when session was established or by proxy)
|
|
520
|
+
const hasPriorContext = steps.some((s) => s.domain === "tls_session" ||
|
|
521
|
+
s.api.toLowerCase().includes("scache") ||
|
|
522
|
+
s.api.toLowerCase().includes("session_") ||
|
|
523
|
+
s.api.toLowerCase().includes("earlydata") || // TLS 1.3 early data
|
|
524
|
+
s.api.toLowerCase().includes("ssl_cf_is_proxy") // proxy handles hostname
|
|
525
|
+
);
|
|
526
|
+
return hasTlsHandshake && !hasHostnameSet && !hasPriorContext;
|
|
527
|
+
},
|
|
528
|
+
},
|
|
529
|
+
// ═══════════════════════════════════════════════════════
|
|
530
|
+
// CRYPTO-001: DH parameters/key not validated
|
|
531
|
+
//
|
|
532
|
+
// libssh FN #36/#41 pattern: Diffie-Hellman parameters generated
|
|
533
|
+
// or key exchanged without validation. Small subgroup attacks
|
|
534
|
+
// (CVE-2016-0701, CVE-2015-4000 LOGJAM) can downgrade or break DH.
|
|
535
|
+
// Must call DH_check/DH_check_pub_key after parameter setup,
|
|
536
|
+
// and check DH_compute_key result for 0 or 1.
|
|
537
|
+
// ═══════════════════════════════════════════════════════
|
|
538
|
+
{
|
|
539
|
+
ruleId: "DH_UNVALIDATED_PARAMETERS",
|
|
540
|
+
description: "DH key exchange performed without parameter validation. " +
|
|
541
|
+
"Unvalidated DH parameters are vulnerable to small subgroup " +
|
|
542
|
+
"attacks (LOGJAM CVE-2015-4000, CVE-2016-0701).",
|
|
543
|
+
check: (steps) => {
|
|
544
|
+
// SSH wrappers handle DH internally — but only suppress if
|
|
545
|
+
// no raw OpenSSL DH operations are also present (mixed = suspicious)
|
|
546
|
+
const hasRawDh = steps.some(s => s.api.toLowerCase().includes("dh_set0") ||
|
|
547
|
+
s.api.toLowerCase().includes("dh_new") ||
|
|
548
|
+
s.api.toLowerCase().includes("dh_compute") ||
|
|
549
|
+
s.api.toLowerCase().includes("derive_init"));
|
|
550
|
+
const wrappedBySsh = steps.some(s => s.api.toLowerCase().includes("dh_init") ||
|
|
551
|
+
s.api.toLowerCase().includes("ssh_dh_keypair"));
|
|
552
|
+
// Mixed: SSH wrapper + raw DH ops → still need to check
|
|
553
|
+
if (wrappedBySsh && !hasRawDh)
|
|
554
|
+
return false;
|
|
555
|
+
const hasDhParams = steps.some(s => s.api.toLowerCase().includes("dh_set0") ||
|
|
556
|
+
s.api.toLowerCase().includes("dh_new") ||
|
|
557
|
+
(s.domain === "auth_hash" && s.api.toLowerCase().includes("dh_")));
|
|
558
|
+
const hasDhCompute = steps.some(s => s.api.toLowerCase().includes("dh_compute") ||
|
|
559
|
+
s.api.toLowerCase().includes("derive_init"));
|
|
560
|
+
const hasDhCheck = steps.some(s => s.api.toLowerCase().includes("dh_check") ||
|
|
561
|
+
s.api.toLowerCase().includes("bn_is_zero") ||
|
|
562
|
+
s.api.toLowerCase().includes("bn_is_one") ||
|
|
563
|
+
s.api.toLowerCase().includes("bn_cmp") ||
|
|
564
|
+
s.api.toLowerCase().includes("dh_param") ||
|
|
565
|
+
s.api.toLowerCase().includes("dh_debug"));
|
|
566
|
+
return (hasDhParams || hasDhCompute) && !hasDhCheck;
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
// ═══════════════════════════════════════════════════════
|
|
570
|
+
// AUTH-003: JWT verification without explicit algorithm
|
|
571
|
+
//
|
|
572
|
+
// CVE-2022-23540 pattern: jwt.verify() called without algorithms
|
|
573
|
+
// option → library defaults to "none" → accepts unsigned tokens.
|
|
574
|
+
// Also catches jwt.decode() without verify=true (PyJWT pattern).
|
|
575
|
+
// Severity: HIGH — can lead to complete authentication bypass.
|
|
576
|
+
// ═══════════════════════════════════════════════════════
|
|
577
|
+
{
|
|
578
|
+
ruleId: "JWT_UNSAFE_ALGORITHM",
|
|
579
|
+
description: "JWT verification may not specify required algorithms. " +
|
|
580
|
+
"Without explicit algorithm whitelist, the 'none' algorithm " +
|
|
581
|
+
"can be exploited to accept unsigned tokens (CVE-2022-23540).",
|
|
582
|
+
check: (steps, filePath) => {
|
|
583
|
+
// JWT verification functions being used
|
|
584
|
+
const hasJwtVerify = steps.some((s) => (s.api === "jwt.verify" ||
|
|
585
|
+
s.api === "verify" ||
|
|
586
|
+
s.api.includes("jwt_verify") ||
|
|
587
|
+
s.api.includes("jwtVerify") ||
|
|
588
|
+
s.api.includes("verifyToken")) &&
|
|
589
|
+
(s.domain === "auth_mech" || s.domain === "auth_cred"));
|
|
590
|
+
// Unsafe: jwt.decode without verify (PyJWT pattern)
|
|
591
|
+
const hasJwtDecode = steps.some((s) => (s.api === "jwt.decode" || s.api.includes("jwt_decode")) &&
|
|
592
|
+
!steps.some((t) => t.api.includes("verify")));
|
|
593
|
+
if (!hasJwtVerify && !hasJwtDecode)
|
|
594
|
+
return false;
|
|
595
|
+
// Algorithm explicitly specified as a call name (rare, but possible)
|
|
596
|
+
const hasAlgorithmSpec = steps.some((s) => s.api.toLowerCase().includes("algorithm") ||
|
|
597
|
+
s.api.toLowerCase().includes("alg") ||
|
|
598
|
+
s.api.toLowerCase().includes("hs256") ||
|
|
599
|
+
s.api.toLowerCase().includes("rs256") ||
|
|
600
|
+
s.api.toLowerCase().includes("es256") ||
|
|
601
|
+
s.api.toLowerCase().includes("eddsa"));
|
|
602
|
+
if (hasAlgorithmSpec)
|
|
603
|
+
return false;
|
|
604
|
+
// File-aware check: the `algorithms: ["HS256"]` option is an object
|
|
605
|
+
// literal argument, invisible to call-name analysis. Read the source
|
|
606
|
+
// and verify every jwtVerify/jwt.verify call site has the option nearby.
|
|
607
|
+
if (filePath) {
|
|
608
|
+
try {
|
|
609
|
+
const fs = require("fs");
|
|
610
|
+
const code = fs.readFileSync(filePath, "utf-8");
|
|
611
|
+
const verifyRe = /\b(?:jwtVerify|jwt\.verify|verifyToken|jwt_decode|jwt\.decode)\s*\(/g;
|
|
612
|
+
let found = false;
|
|
613
|
+
let allSafe = true;
|
|
614
|
+
let m;
|
|
615
|
+
while ((m = verifyRe.exec(code)) !== null) {
|
|
616
|
+
found = true;
|
|
617
|
+
// Look at the call's argument window for an algorithms whitelist
|
|
618
|
+
const window = code.slice(m.index, Math.min(m.index + 400, code.length));
|
|
619
|
+
if (!/algorithms\s*:/.test(window)) {
|
|
620
|
+
allSafe = false;
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (found && allSafe)
|
|
625
|
+
return false; // every call site whitelists algorithms
|
|
626
|
+
if (found && !allSafe)
|
|
627
|
+
return true; // at least one site lacks the whitelist
|
|
628
|
+
// No verify call sites found in file text → fall through to name-based
|
|
629
|
+
}
|
|
630
|
+
catch {
|
|
631
|
+
// File unreadable → fall through to name-based heuristic
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
return true;
|
|
635
|
+
},
|
|
636
|
+
},
|
|
637
|
+
// ═══════════════════════════════════════════════════════
|
|
638
|
+
// QUIC-001: Active migration disabled
|
|
639
|
+
// ═══════════════════════════════════════════════════════
|
|
640
|
+
{
|
|
641
|
+
ruleId: "QUIC_DISABLE_ACTIVE_MIGRATION",
|
|
642
|
+
description: "QUIC connection migration is disabled. This may impact privacy " +
|
|
643
|
+
"and connection resilience. Review if this is intentional.",
|
|
644
|
+
check: (steps) => {
|
|
645
|
+
return steps.some((s) => s.api.toLowerCase().includes("disable_active_migration"));
|
|
646
|
+
},
|
|
647
|
+
},
|
|
648
|
+
];
|
|
649
|
+
/**
|
|
650
|
+
* Run specific violation checks against a semantic sequence.
|
|
651
|
+
* Returns violations found (empty for Phase 1 MVP).
|
|
652
|
+
*/
|
|
653
|
+
function checkSpecificViolations(seq, _filePath) {
|
|
654
|
+
const violations = [];
|
|
655
|
+
for (const check of exports.SPECIFIC_VIOLATION_CHECKS) {
|
|
656
|
+
if (check.check(seq.steps, _filePath)) {
|
|
657
|
+
violations.push({
|
|
658
|
+
ruleId: check.ruleId,
|
|
659
|
+
description: check.description,
|
|
660
|
+
evidence: seq.steps.map(s => `${s.api}[${s.domain}]`).join(" → "),
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
return violations;
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Batch validate multiple call sequences.
|
|
668
|
+
* This is the main entry point for the Trust Engine integration.
|
|
669
|
+
*/
|
|
670
|
+
function batchValidateSequences(sequences, mapSequence) {
|
|
671
|
+
const result = {
|
|
672
|
+
total: sequences.length,
|
|
673
|
+
clean: 0,
|
|
674
|
+
unknown: 0,
|
|
675
|
+
flagged: 0,
|
|
676
|
+
details: [],
|
|
677
|
+
};
|
|
678
|
+
for (const seq of sequences) {
|
|
679
|
+
const semantic = mapSequence(seq.calls);
|
|
680
|
+
const validation = validateSemanticSequence(semantic);
|
|
681
|
+
result.details.push({
|
|
682
|
+
sequence: seq.calls,
|
|
683
|
+
semanticSteps: semantic.steps,
|
|
684
|
+
validation,
|
|
685
|
+
});
|
|
686
|
+
if (validation.valid && validation.primaryGroup !== null) {
|
|
687
|
+
result.clean++;
|
|
688
|
+
}
|
|
689
|
+
else if (validation.primaryGroup === null) {
|
|
690
|
+
result.unknown++;
|
|
691
|
+
}
|
|
692
|
+
else {
|
|
693
|
+
result.flagged++;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return result;
|
|
697
|
+
}
|