@trynullsec/s1-zk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LIMITATIONS.md +17 -0
- package/README.md +149 -0
- package/ROADMAP.md +21 -0
- package/RULES.md +229 -0
- package/benchmarks/historical/orchard-inspired/partial-ec-mul-halo2.rs +53 -0
- package/benchmarks/historical/orchard-inspired/safe-ec-mul-halo2.rs +69 -0
- package/dist/ai/prompt-builder.d.ts +2 -0
- package/dist/ai/prompt-builder.js +33 -0
- package/dist/ai/prompt-builder.js.map +1 -0
- package/dist/ai/reasoning-interface.d.ts +15 -0
- package/dist/ai/reasoning-interface.js +2 -0
- package/dist/ai/reasoning-interface.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +71 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +41 -0
- package/dist/config.js.map +1 -0
- package/dist/core/audit-engine.d.ts +3 -0
- package/dist/core/audit-engine.js +31 -0
- package/dist/core/audit-engine.js.map +1 -0
- package/dist/core/file-loader.d.ts +6 -0
- package/dist/core/file-loader.js +28 -0
- package/dist/core/file-loader.js.map +1 -0
- package/dist/core/issue-builder.d.ts +16 -0
- package/dist/core/issue-builder.js +27 -0
- package/dist/core/issue-builder.js.map +1 -0
- package/dist/core/range-check-classifier.d.ts +16 -0
- package/dist/core/range-check-classifier.js +194 -0
- package/dist/core/range-check-classifier.js.map +1 -0
- package/dist/core/rule-engine.d.ts +10 -0
- package/dist/core/rule-engine.js +41 -0
- package/dist/core/rule-engine.js.map +1 -0
- package/dist/core/severity.d.ts +6 -0
- package/dist/core/severity.js +21 -0
- package/dist/core/severity.js.map +1 -0
- package/dist/core/source-map.d.ts +3 -0
- package/dist/core/source-map.js +16 -0
- package/dist/core/source-map.js.map +1 -0
- package/dist/frontends/circom/circom-comments.d.ts +8 -0
- package/dist/frontends/circom/circom-comments.js +52 -0
- package/dist/frontends/circom/circom-comments.js.map +1 -0
- package/dist/frontends/circom/circom-ir-builder.d.ts +2 -0
- package/dist/frontends/circom/circom-ir-builder.js +12 -0
- package/dist/frontends/circom/circom-ir-builder.js.map +1 -0
- package/dist/frontends/circom/circom-parser.d.ts +2 -0
- package/dist/frontends/circom/circom-parser.js +218 -0
- package/dist/frontends/circom/circom-parser.js.map +1 -0
- package/dist/frontends/circom/circom-tokenizer.d.ts +7 -0
- package/dist/frontends/circom/circom-tokenizer.js +79 -0
- package/dist/frontends/circom/circom-tokenizer.js.map +1 -0
- package/dist/frontends/circom/circom-utils.d.ts +9 -0
- package/dist/frontends/circom/circom-utils.js +88 -0
- package/dist/frontends/circom/circom-utils.js.map +1 -0
- package/dist/frontends/gnark/gnark-adapter.d.ts +5 -0
- package/dist/frontends/gnark/gnark-adapter.js +7 -0
- package/dist/frontends/gnark/gnark-adapter.js.map +1 -0
- package/dist/frontends/halo2/halo2-adapter.d.ts +8 -0
- package/dist/frontends/halo2/halo2-adapter.js +12 -0
- package/dist/frontends/halo2/halo2-adapter.js.map +1 -0
- package/dist/frontends/halo2/halo2-constraint-extractor.d.ts +29 -0
- package/dist/frontends/halo2/halo2-constraint-extractor.js +38 -0
- package/dist/frontends/halo2/halo2-constraint-extractor.js.map +1 -0
- package/dist/frontends/halo2/halo2-constraint-graph.d.ts +16 -0
- package/dist/frontends/halo2/halo2-constraint-graph.js +96 -0
- package/dist/frontends/halo2/halo2-constraint-graph.js.map +1 -0
- package/dist/frontends/halo2/halo2-dataflow.d.ts +11 -0
- package/dist/frontends/halo2/halo2-dataflow.js +19 -0
- package/dist/frontends/halo2/halo2-dataflow.js.map +1 -0
- package/dist/frontends/halo2/halo2-expression-parser.d.ts +14 -0
- package/dist/frontends/halo2/halo2-expression-parser.js +62 -0
- package/dist/frontends/halo2/halo2-expression-parser.js.map +1 -0
- package/dist/frontends/halo2/halo2-ir-builder.d.ts +2 -0
- package/dist/frontends/halo2/halo2-ir-builder.js +17 -0
- package/dist/frontends/halo2/halo2-ir-builder.js.map +1 -0
- package/dist/frontends/halo2/halo2-parser.d.ts +2 -0
- package/dist/frontends/halo2/halo2-parser.js +316 -0
- package/dist/frontends/halo2/halo2-parser.js.map +1 -0
- package/dist/frontends/halo2/halo2-patterns.d.ts +6 -0
- package/dist/frontends/halo2/halo2-patterns.js +54 -0
- package/dist/frontends/halo2/halo2-patterns.js.map +1 -0
- package/dist/frontends/halo2/halo2-types.d.ts +100 -0
- package/dist/frontends/halo2/halo2-types.js +2 -0
- package/dist/frontends/halo2/halo2-types.js.map +1 -0
- package/dist/frontends/noir/noir-adapter.d.ts +5 -0
- package/dist/frontends/noir/noir-adapter.js +7 -0
- package/dist/frontends/noir/noir-adapter.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/ir/circuit-ir.d.ts +1 -0
- package/dist/ir/circuit-ir.js +2 -0
- package/dist/ir/circuit-ir.js.map +1 -0
- package/dist/ir/component-table.d.ts +6 -0
- package/dist/ir/component-table.js +11 -0
- package/dist/ir/component-table.js.map +1 -0
- package/dist/ir/constraint-graph.d.ts +26 -0
- package/dist/ir/constraint-graph.js +130 -0
- package/dist/ir/constraint-graph.js.map +1 -0
- package/dist/ir/dataflow.d.ts +3 -0
- package/dist/ir/dataflow.js +10 -0
- package/dist/ir/dataflow.js.map +1 -0
- package/dist/ir/reference-index.d.ts +6 -0
- package/dist/ir/reference-index.js +18 -0
- package/dist/ir/reference-index.js.map +1 -0
- package/dist/ir/signal-table.d.ts +6 -0
- package/dist/ir/signal-table.js +16 -0
- package/dist/ir/signal-table.js.map +1 -0
- package/dist/report/json.d.ts +2 -0
- package/dist/report/json.js +4 -0
- package/dist/report/json.js.map +1 -0
- package/dist/report/markdown.d.ts +2 -0
- package/dist/report/markdown.js +45 -0
- package/dist/report/markdown.js.map +1 -0
- package/dist/report/sarif.d.ts +2 -0
- package/dist/report/sarif.js +52 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/report/summary.d.ts +2 -0
- package/dist/report/summary.js +7 -0
- package/dist/report/summary.js.map +1 -0
- package/dist/report/terminal.d.ts +2 -0
- package/dist/report/terminal.js +50 -0
- package/dist/report/terminal.js.map +1 -0
- package/dist/rules/NS-ZK-001-dangerous-hint-assignment.d.ts +2 -0
- package/dist/rules/NS-ZK-001-dangerous-hint-assignment.js +27 -0
- package/dist/rules/NS-ZK-001-dangerous-hint-assignment.js.map +1 -0
- package/dist/rules/NS-ZK-002-assigned-but-unconstrained.d.ts +2 -0
- package/dist/rules/NS-ZK-002-assigned-but-unconstrained.js +22 -0
- package/dist/rules/NS-ZK-002-assigned-but-unconstrained.js.map +1 -0
- package/dist/rules/NS-ZK-003-unbound-public-input.d.ts +2 -0
- package/dist/rules/NS-ZK-003-unbound-public-input.js +22 -0
- package/dist/rules/NS-ZK-003-unbound-public-input.js.map +1 -0
- package/dist/rules/NS-ZK-004-unconstrained-output.d.ts +2 -0
- package/dist/rules/NS-ZK-004-unconstrained-output.js +27 -0
- package/dist/rules/NS-ZK-004-unconstrained-output.js.map +1 -0
- package/dist/rules/NS-ZK-005-missing-booleanity.d.ts +2 -0
- package/dist/rules/NS-ZK-005-missing-booleanity.js +26 -0
- package/dist/rules/NS-ZK-005-missing-booleanity.js.map +1 -0
- package/dist/rules/NS-ZK-006-missing-range-check.d.ts +2 -0
- package/dist/rules/NS-ZK-006-missing-range-check.js +32 -0
- package/dist/rules/NS-ZK-006-missing-range-check.js.map +1 -0
- package/dist/rules/NS-ZK-007-unsafe-assertion.d.ts +2 -0
- package/dist/rules/NS-ZK-007-unsafe-assertion.js +24 -0
- package/dist/rules/NS-ZK-007-unsafe-assertion.js.map +1 -0
- package/dist/rules/NS-ZK-008-unsafe-division-or-inverse.d.ts +2 -0
- package/dist/rules/NS-ZK-008-unsafe-division-or-inverse.js +39 -0
- package/dist/rules/NS-ZK-008-unsafe-division-or-inverse.js.map +1 -0
- package/dist/rules/NS-ZK-009-unconstrained-component-output.d.ts +2 -0
- package/dist/rules/NS-ZK-009-unconstrained-component-output.js +25 -0
- package/dist/rules/NS-ZK-009-unconstrained-component-output.js.map +1 -0
- package/dist/rules/NS-ZK-010-alias-overflow-risk.d.ts +2 -0
- package/dist/rules/NS-ZK-010-alias-overflow-risk.js +48 -0
- package/dist/rules/NS-ZK-010-alias-overflow-risk.js.map +1 -0
- package/dist/rules/NS-ZK-011-unused-signal.d.ts +2 -0
- package/dist/rules/NS-ZK-011-unused-signal.js +26 -0
- package/dist/rules/NS-ZK-011-unused-signal.js.map +1 -0
- package/dist/rules/NS-ZK-012-suspicious-selector.d.ts +2 -0
- package/dist/rules/NS-ZK-012-suspicious-selector.js +33 -0
- package/dist/rules/NS-ZK-012-suspicious-selector.js.map +1 -0
- package/dist/rules/halo2/NS-H2-001-assigned-advice-not-constrained.d.ts +2 -0
- package/dist/rules/halo2/NS-H2-001-assigned-advice-not-constrained.js +40 -0
- package/dist/rules/halo2/NS-H2-001-assigned-advice-not-constrained.js.map +1 -0
- package/dist/rules/halo2/NS-H2-002-instance-not-bound.d.ts +2 -0
- package/dist/rules/halo2/NS-H2-002-instance-not-bound.js +32 -0
- package/dist/rules/halo2/NS-H2-002-instance-not-bound.js.map +1 -0
- package/dist/rules/halo2/NS-H2-003-selector-risk.d.ts +2 -0
- package/dist/rules/halo2/NS-H2-003-selector-risk.js +37 -0
- package/dist/rules/halo2/NS-H2-003-selector-risk.js.map +1 -0
- package/dist/rules/halo2/NS-H2-004-unsafe-inverse.d.ts +2 -0
- package/dist/rules/halo2/NS-H2-004-unsafe-inverse.js +43 -0
- package/dist/rules/halo2/NS-H2-004-unsafe-inverse.js.map +1 -0
- package/dist/rules/halo2/NS-H2-005-partial-ec-operation.d.ts +2 -0
- package/dist/rules/halo2/NS-H2-005-partial-ec-operation.js +43 -0
- package/dist/rules/halo2/NS-H2-005-partial-ec-operation.js.map +1 -0
- package/dist/rules/halo2/NS-H2-006-missing-enable-equality.d.ts +2 -0
- package/dist/rules/halo2/NS-H2-006-missing-enable-equality.js +31 -0
- package/dist/rules/halo2/NS-H2-006-missing-enable-equality.js.map +1 -0
- package/dist/rules/halo2/halo2-rule-utils.d.ts +8 -0
- package/dist/rules/halo2/halo2-rule-utils.js +24 -0
- package/dist/rules/halo2/halo2-rule-utils.js.map +1 -0
- package/dist/rules/index.d.ts +2 -0
- package/dist/rules/index.js +39 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/scanner.d.ts +8 -0
- package/dist/scanner.js +52 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types.d.ts +171 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/examples/halo2/safe/bound-instance.rs +23 -0
- package/examples/halo2/safe/constrained-advice.rs +27 -0
- package/examples/halo2/safe/safe-inverse.rs +30 -0
- package/examples/halo2/safe/safe-selector.rs +27 -0
- package/examples/halo2/vulnerable/missing-selector-booleanity.rs +20 -0
- package/examples/halo2/vulnerable/partial-ec-mul.rs +23 -0
- package/examples/halo2/vulnerable/unbound-instance.rs +20 -0
- package/examples/halo2/vulnerable/unconstrained-advice.rs +24 -0
- package/examples/halo2/vulnerable/unsafe-inverse.rs +20 -0
- package/examples/safe/safe-boolean.circom +15 -0
- package/examples/safe/safe-component-output.circom +18 -0
- package/examples/safe/safe-division.circom +14 -0
- package/examples/safe/safe-public-input.circom +15 -0
- package/examples/safe/safe-range-check.circom +16 -0
- package/examples/safe/safe-transfer.circom +19 -0
- package/examples/vulnerable/alias-overflow-risk.circom +12 -0
- package/examples/vulnerable/merkle-selector-bug.circom +12 -0
- package/examples/vulnerable/missing-boolean.circom +11 -0
- package/examples/vulnerable/missing-range-check.circom +11 -0
- package/examples/vulnerable/public-input-not-bound.circom +12 -0
- package/examples/vulnerable/unconstrained-component-output.circom +15 -0
- package/examples/vulnerable/unconstrained-transfer.circom +17 -0
- package/examples/vulnerable/unsafe-assertion.circom +12 -0
- package/examples/vulnerable/unsafe-division.circom +11 -0
- package/package.json +46 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"summary.js","sourceRoot":"","sources":["../../src/report/summary.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,MAAe;IAC7C,OAAO,MAAM,CAAC,MAAM,CAClB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE;QACjB,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,OAAO,OAAO,CAAC;IACjB,CAAC,EACD,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CACrD,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
const colorBySeverity = {
|
|
3
|
+
CRITICAL: chalk.redBright.bold,
|
|
4
|
+
HIGH: chalk.red,
|
|
5
|
+
MEDIUM: chalk.yellow,
|
|
6
|
+
LOW: chalk.blue,
|
|
7
|
+
INFO: chalk.gray
|
|
8
|
+
};
|
|
9
|
+
function renderIssue(issue) {
|
|
10
|
+
const color = colorBySeverity[issue.severity];
|
|
11
|
+
return `${color(`[${issue.severity}]`)} ${issue.ruleId} ${issue.title}
|
|
12
|
+
File: ${issue.file}:${issue.line}
|
|
13
|
+
Template: ${issue.templateName ?? "unknown"}
|
|
14
|
+
|
|
15
|
+
${issue.snippet ?? ""}
|
|
16
|
+
|
|
17
|
+
Why this matters:
|
|
18
|
+
${issue.explanation}
|
|
19
|
+
|
|
20
|
+
Impact:
|
|
21
|
+
${issue.impact}
|
|
22
|
+
|
|
23
|
+
Suggested fix:
|
|
24
|
+
${issue.suggestedFix}
|
|
25
|
+
|
|
26
|
+
Confidence:
|
|
27
|
+
${issue.confidence}
|
|
28
|
+
`;
|
|
29
|
+
}
|
|
30
|
+
export function renderTerminalReport(result) {
|
|
31
|
+
return `${chalk.bold("Nullsec S1-ZK")}
|
|
32
|
+
AI-native auditing for zero-knowledge circuits
|
|
33
|
+
|
|
34
|
+
Target: ${result.target}
|
|
35
|
+
Frontend: ${result.frontend}
|
|
36
|
+
Files scanned: ${result.filesScanned}
|
|
37
|
+
Rules executed: ${result.rulesExecuted}
|
|
38
|
+
Issues found: ${result.issues.length}
|
|
39
|
+
|
|
40
|
+
Severity summary:
|
|
41
|
+
CRITICAL ${result.summary.CRITICAL}
|
|
42
|
+
HIGH ${result.summary.HIGH}
|
|
43
|
+
MEDIUM ${result.summary.MEDIUM}
|
|
44
|
+
LOW ${result.summary.LOW}
|
|
45
|
+
INFO ${result.summary.INFO}
|
|
46
|
+
|
|
47
|
+
${result.issues.map(renderIssue).join("\n")}
|
|
48
|
+
${result.parserWarnings.length > 0 ? `Parser warnings: ${result.parserWarnings.length}\n` : ""}`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/report/terminal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,eAAe,GAAgD;IACnE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,IAAI;IAC9B,IAAI,EAAE,KAAK,CAAC,GAAG;IACf,MAAM,EAAE,KAAK,CAAC,MAAM;IACpB,GAAG,EAAE,KAAK,CAAC,IAAI;IACf,IAAI,EAAE,KAAK,CAAC,IAAI;CACjB,CAAC;AAEF,SAAS,WAAW,CAAC,KAAY;IAC/B,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9C,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK;QAC/D,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI;YACpB,KAAK,CAAC,YAAY,IAAI,SAAS;;IAEvC,KAAK,CAAC,OAAO,IAAI,EAAE;;;EAGrB,KAAK,CAAC,WAAW;;;EAGjB,KAAK,CAAC,MAAM;;;EAGZ,KAAK,CAAC,YAAY;;;EAGlB,KAAK,CAAC,UAAU;CACjB,CAAC;AACF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAmB;IACtD,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,eAAe,CAAC;;;UAG7B,MAAM,CAAC,MAAM;YACX,MAAM,CAAC,QAAQ;iBACV,MAAM,CAAC,YAAY;kBAClB,MAAM,CAAC,aAAa;gBACtB,MAAM,CAAC,MAAM,CAAC,MAAM;;;YAGxB,MAAM,CAAC,OAAO,CAAC,QAAQ;YACvB,MAAM,CAAC,OAAO,CAAC,IAAI;YACnB,MAAM,CAAC,OAAO,CAAC,MAAM;YACrB,MAAM,CAAC,OAAO,CAAC,GAAG;YAClB,MAAM,CAAC,OAAO,CAAC,IAAI;;EAE7B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;EACzC,MAAM,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,MAAM,CAAC,cAAc,CAAC,MAAM,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACjG,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
export const dangerousHintAssignmentRule = {
|
|
3
|
+
id: "NS-ZK-001",
|
|
4
|
+
title: "Dangerous hint assignment",
|
|
5
|
+
description: "Detects Circom <-- assignments, which assign witness values without creating constraints.",
|
|
6
|
+
defaultSeverity: "CRITICAL",
|
|
7
|
+
tags: ["circom", "hints", "underconstraint"],
|
|
8
|
+
analyze(context) {
|
|
9
|
+
return context.graph.assignedByHint().map((assignment) => {
|
|
10
|
+
const constrained = context.graph.appearsInAnyConstraint(assignment.lhs);
|
|
11
|
+
return buildIssue({
|
|
12
|
+
ruleId: "NS-ZK-001",
|
|
13
|
+
title: "Dangerous hint assignment",
|
|
14
|
+
severity: constrained ? "MEDIUM" : "CRITICAL",
|
|
15
|
+
confidence: constrained ? "MEDIUM" : "HIGH",
|
|
16
|
+
location: assignment,
|
|
17
|
+
signalName: assignment.lhs,
|
|
18
|
+
explanation: `The signal \`${assignment.lhs}\` is assigned with <-- from \`${assignment.rhs}\`. In Circom, <-- computes a witness value but does not create a constraint.${constrained ? " The signal appears in a later constraint, so the immediate risk is lower but should still be reviewed." : " The signal was not later constrained with === or <==."}`,
|
|
19
|
+
impact: "A malicious prover may choose arbitrary witness values unless the assignment is independently constrained.",
|
|
20
|
+
suggestedFix: `Prefer <== when possible, or add an explicit equality constraint such as \`${assignment.lhs} === ${assignment.rhs}\`.`,
|
|
21
|
+
tags: dangerousHintAssignmentRule.tags,
|
|
22
|
+
metadata: { rhs: assignment.rhs, constrained }
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=NS-ZK-001-dangerous-hint-assignment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-001-dangerous-hint-assignment.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-001-dangerous-hint-assignment.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,CAAC,MAAM,2BAA2B,GAAS;IAC/C,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,2BAA2B;IAClC,WAAW,EAAE,2FAA2F;IACxG,eAAe,EAAE,UAAU;IAC3B,IAAI,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,iBAAiB,CAAC;IAC5C,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;YACvD,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACzE,OAAO,UAAU,CAAC;gBAChB,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,2BAA2B;gBAClC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU;gBAC7C,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;gBAC3C,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE,UAAU,CAAC,GAAG;gBAC1B,WAAW,EAAE,gBAAgB,UAAU,CAAC,GAAG,kCAAkC,UAAU,CAAC,GAAG,gFAAgF,WAAW,CAAC,CAAC,CAAC,yGAAyG,CAAC,CAAC,CAAC,wDAAwD,EAAE;gBAC/V,MAAM,EAAE,4GAA4G;gBACpH,YAAY,EAAE,8EAA8E,UAAU,CAAC,GAAG,QAAQ,UAAU,CAAC,GAAG,KAAK;gBACrI,IAAI,EAAE,2BAA2B,CAAC,IAAI;gBACtC,QAAQ,EAAE,EAAE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,WAAW,EAAE;aAC/C,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
export const assignedButUnconstrainedRule = {
|
|
3
|
+
id: "NS-ZK-002",
|
|
4
|
+
title: "Assigned but unconstrained signal",
|
|
5
|
+
description: "Detects signals assigned with <-- that are not constrained with === or <==.",
|
|
6
|
+
defaultSeverity: "CRITICAL",
|
|
7
|
+
tags: ["circom", "underconstraint", "soundness"],
|
|
8
|
+
analyze(context) {
|
|
9
|
+
return context.graph.onlyAssignedButNeverConstrained().map((assignment) => buildIssue({
|
|
10
|
+
ruleId: "NS-ZK-002",
|
|
11
|
+
title: "Assigned but unconstrained signal",
|
|
12
|
+
severity: "CRITICAL",
|
|
13
|
+
location: assignment,
|
|
14
|
+
signalName: assignment.lhs,
|
|
15
|
+
explanation: `The signal \`${assignment.lhs}\` is assigned with <--, which does not create a constraint. The signal was not later constrained with === or <==.`,
|
|
16
|
+
impact: "A malicious prover may choose arbitrary witness values while still satisfying the circuit.",
|
|
17
|
+
suggestedFix: `Use <== when possible, or add an explicit constraint such as \`${assignment.lhs} === ${assignment.rhs}\`.`,
|
|
18
|
+
tags: assignedButUnconstrainedRule.tags
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=NS-ZK-002-assigned-but-unconstrained.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-002-assigned-but-unconstrained.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-002-assigned-but-unconstrained.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,CAAC,MAAM,4BAA4B,GAAS;IAChD,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,mCAAmC;IAC1C,WAAW,EAAE,6EAA6E;IAC1F,eAAe,EAAE,UAAU;IAC3B,IAAI,EAAE,CAAC,QAAQ,EAAE,iBAAiB,EAAE,WAAW,CAAC;IAChD,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CACxE,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,mCAAmC;YAC1C,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,UAAU;YACpB,UAAU,EAAE,UAAU,CAAC,GAAG;YAC1B,WAAW,EAAE,gBAAgB,UAAU,CAAC,GAAG,oHAAoH;YAC/J,MAAM,EAAE,4FAA4F;YACpG,YAAY,EAAE,kEAAkE,UAAU,CAAC,GAAG,QAAQ,UAAU,CAAC,GAAG,KAAK;YACzH,IAAI,EAAE,4BAA4B,CAAC,IAAI;SACxC,CAAC,CACH,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
export const unboundPublicInputRule = {
|
|
3
|
+
id: "NS-ZK-003",
|
|
4
|
+
title: "Unbound input signal",
|
|
5
|
+
description: "Detects input signals that never appear in constraints.",
|
|
6
|
+
defaultSeverity: "HIGH",
|
|
7
|
+
tags: ["inputs", "public-inputs", "binding"],
|
|
8
|
+
analyze(context) {
|
|
9
|
+
return context.graph.unboundInputs().map((signal) => buildIssue({
|
|
10
|
+
ruleId: "NS-ZK-003",
|
|
11
|
+
title: "Unbound input signal",
|
|
12
|
+
severity: "HIGH",
|
|
13
|
+
location: signal,
|
|
14
|
+
signalName: signal.name,
|
|
15
|
+
explanation: `The input signal \`${signal.name}\` does not appear in any parsed constraint.`,
|
|
16
|
+
impact: "The proof may not bind the claimed input to the statement being proven.",
|
|
17
|
+
suggestedFix: "Reference the input inside a constraint that enforces the intended relation.",
|
|
18
|
+
tags: unboundPublicInputRule.tags
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=NS-ZK-003-unbound-public-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-003-unbound-public-input.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-003-unbound-public-input.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,CAAC,MAAM,sBAAsB,GAAS;IAC1C,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EAAE,yDAAyD;IACtE,eAAe,EAAE,MAAM;IACvB,IAAI,EAAE,CAAC,QAAQ,EAAE,eAAe,EAAE,SAAS,CAAC;IAC5C,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAClD,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,sBAAsB;YAC7B,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,WAAW,EAAE,sBAAsB,MAAM,CAAC,IAAI,8CAA8C;YAC5F,MAAM,EAAE,yEAAyE;YACjF,YAAY,EAAE,8EAA8E;YAC5F,IAAI,EAAE,sBAAsB,CAAC,IAAI;SAClC,CAAC,CACH,CAAC;IACJ,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
export const unconstrainedOutputRule = {
|
|
3
|
+
id: "NS-ZK-004",
|
|
4
|
+
title: "Unconstrained output",
|
|
5
|
+
description: "Detects output signals that are assigned unsafely or do not participate in constraints.",
|
|
6
|
+
defaultSeverity: "HIGH",
|
|
7
|
+
tags: ["outputs", "underconstraint"],
|
|
8
|
+
analyze(context) {
|
|
9
|
+
return context.graph.unconstrainedOutputs().map((signal) => {
|
|
10
|
+
const hintAssignment = context.graph.assignmentsForSignal(signal.name).find((assignment) => assignment.operator === "<--");
|
|
11
|
+
return buildIssue({
|
|
12
|
+
ruleId: "NS-ZK-004",
|
|
13
|
+
title: "Unconstrained output",
|
|
14
|
+
severity: hintAssignment ? "CRITICAL" : "HIGH",
|
|
15
|
+
location: hintAssignment ?? signal,
|
|
16
|
+
signalName: signal.name,
|
|
17
|
+
explanation: hintAssignment
|
|
18
|
+
? `The output \`${signal.name}\` is assigned with <-- and is not safely bound as an output constraint.`
|
|
19
|
+
: `The output \`${signal.name}\` does not appear in any parsed constraint.`,
|
|
20
|
+
impact: "The circuit may output a value that was not proven to be correctly derived.",
|
|
21
|
+
suggestedFix: "Constrain outputs with <== or an explicit === relation to the intended expression.",
|
|
22
|
+
tags: unconstrainedOutputRule.tags
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=NS-ZK-004-unconstrained-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-004-unconstrained-output.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-004-unconstrained-output.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,CAAC,MAAM,uBAAuB,GAAS;IAC3C,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,sBAAsB;IAC7B,WAAW,EAAE,yFAAyF;IACtG,eAAe,EAAE,MAAM;IACvB,IAAI,EAAE,CAAC,SAAS,EAAE,iBAAiB,CAAC;IACpC,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YACzD,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,KAAK,CAAC,CAAC;YAC3H,OAAO,UAAU,CAAC;gBAChB,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,sBAAsB;gBAC7B,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;gBAC9C,QAAQ,EAAE,cAAc,IAAI,MAAM;gBAClC,UAAU,EAAE,MAAM,CAAC,IAAI;gBACvB,WAAW,EAAE,cAAc;oBACzB,CAAC,CAAC,gBAAgB,MAAM,CAAC,IAAI,0EAA0E;oBACvG,CAAC,CAAC,gBAAgB,MAAM,CAAC,IAAI,8CAA8C;gBAC7E,MAAM,EAAE,6EAA6E;gBACrF,YAAY,EAAE,oFAAoF;gBAClG,IAAI,EAAE,uBAAuB,CAAC,IAAI;aACnC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
import { isBooleanLikeName } from "../frontends/circom/circom-utils.js";
|
|
3
|
+
export const missingBooleanityRule = {
|
|
4
|
+
id: "NS-ZK-005",
|
|
5
|
+
title: "Missing booleanity constraint",
|
|
6
|
+
description: "Detects boolean-like signals that are not constrained to 0 or 1.",
|
|
7
|
+
defaultSeverity: "HIGH",
|
|
8
|
+
tags: ["booleanity", "selectors", "authorization"],
|
|
9
|
+
analyze(context) {
|
|
10
|
+
return context.graph
|
|
11
|
+
.declaredSignals()
|
|
12
|
+
.filter((signal) => isBooleanLikeName(signal.baseName) && context.graph.signalReferences(signal.name).length > 0 && !context.graph.hasBooleanityConstraint(signal.name))
|
|
13
|
+
.map((signal) => buildIssue({
|
|
14
|
+
ruleId: "NS-ZK-005",
|
|
15
|
+
title: "Missing booleanity constraint",
|
|
16
|
+
severity: "HIGH",
|
|
17
|
+
location: signal,
|
|
18
|
+
signalName: signal.name,
|
|
19
|
+
explanation: `The signal \`${signal.name}\` looks boolean or selector-like, but no x * (x - 1) === 0 style constraint was found.`,
|
|
20
|
+
impact: "A prover may use arbitrary field values instead of 0 or 1, breaking selector logic, path selection, authorization checks, or branching constraints.",
|
|
21
|
+
suggestedFix: `Add a booleanity constraint: \`${signal.name} * (${signal.name} - 1) === 0;\`.`,
|
|
22
|
+
tags: missingBooleanityRule.tags
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=NS-ZK-005-missing-booleanity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-005-missing-booleanity.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-005-missing-booleanity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qCAAqC,CAAC;AAExE,MAAM,CAAC,MAAM,qBAAqB,GAAS;IACzC,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,+BAA+B;IACtC,WAAW,EAAE,kEAAkE;IAC/E,eAAe,EAAE,MAAM;IACvB,IAAI,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,eAAe,CAAC;IAClD,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,KAAK;aACjB,eAAe,EAAE;aACjB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;aACvK,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACd,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,+BAA+B;YACtC,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,WAAW,EAAE,gBAAgB,MAAM,CAAC,IAAI,yFAAyF;YACjI,MAAM,EAAE,qJAAqJ;YAC7J,YAAY,EAAE,kCAAkC,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,IAAI,iBAAiB;YAC9F,IAAI,EAAE,qBAAqB,CAAC,IAAI;SACjC,CAAC,CACH,CAAC;IACN,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
import { assessRangeCheckRisk } from "../core/range-check-classifier.js";
|
|
3
|
+
export const missingRangeCheckRule = {
|
|
4
|
+
id: "NS-ZK-006",
|
|
5
|
+
title: "Missing range check",
|
|
6
|
+
description: "Detects bounded-integer signals without obvious range-checking mechanisms.",
|
|
7
|
+
defaultSeverity: "MEDIUM",
|
|
8
|
+
tags: ["range", "overflow", "aliasing"],
|
|
9
|
+
analyze(context) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
for (const signal of context.graph.declaredSignals()) {
|
|
12
|
+
const assessment = assessRangeCheckRisk(signal, context);
|
|
13
|
+
if (!assessment.shouldReport)
|
|
14
|
+
continue;
|
|
15
|
+
issues.push(buildIssue({
|
|
16
|
+
ruleId: "NS-ZK-006",
|
|
17
|
+
title: "Missing range check",
|
|
18
|
+
severity: assessment.severity,
|
|
19
|
+
confidence: assessment.confidence,
|
|
20
|
+
location: signal,
|
|
21
|
+
signalName: signal.name,
|
|
22
|
+
explanation: assessment.explanation,
|
|
23
|
+
impact: "Field elements are not naturally bounded integers. Missing range checks can cause overflows, aliasing, invalid comparisons, or inconsistent protocol semantics.",
|
|
24
|
+
suggestedFix: "Constrain the value with an explicit bit decomposition, range-check gadget, comparator, or protocol-specific bounded representation.",
|
|
25
|
+
tags: missingRangeCheckRule.tags,
|
|
26
|
+
metadata: { category: assessment.category }
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
return issues;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=NS-ZK-006-missing-range-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-006-missing-range-check.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-006-missing-range-check.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,MAAM,CAAC,MAAM,qBAAqB,GAAS;IACzC,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,qBAAqB;IAC5B,WAAW,EAAE,4EAA4E;IACzF,eAAe,EAAE,QAAQ;IACzB,IAAI,EAAE,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,CAAC;IACvC,OAAO,CAAC,OAAO;QACb,MAAM,MAAM,GAAG,EAAE,CAAC;QAElB,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;YACrD,MAAM,UAAU,GAAG,oBAAoB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACzD,IAAI,CAAC,UAAU,CAAC,YAAY;gBAAE,SAAS;YAEvC,MAAM,CAAC,IAAI,CACT,UAAU,CAAC;gBACT,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,qBAAqB;gBAC5B,QAAQ,EAAE,UAAU,CAAC,QAAQ;gBAC7B,UAAU,EAAE,UAAU,CAAC,UAAU;gBACjC,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,MAAM,CAAC,IAAI;gBACvB,WAAW,EAAE,UAAU,CAAC,WAAW;gBACnC,MAAM,EACJ,iKAAiK;gBACnK,YAAY,EACV,sIAAsI;gBACxI,IAAI,EAAE,qBAAqB,CAAC,IAAI;gBAChC,QAAQ,EAAE,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE;aAC5C,CAAC,CACH,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
export const unsafeAssertionRule = {
|
|
3
|
+
id: "NS-ZK-007",
|
|
4
|
+
title: "Unsafe assertion over signals",
|
|
5
|
+
description: "Detects assert(...) expressions involving circuit signals.",
|
|
6
|
+
defaultSeverity: "HIGH",
|
|
7
|
+
tags: ["assert", "circom", "constraints"],
|
|
8
|
+
analyze(context) {
|
|
9
|
+
return context.ir.assertions
|
|
10
|
+
.filter((assertion) => assertion.referencedSignals.length > 0)
|
|
11
|
+
.map((assertion) => buildIssue({
|
|
12
|
+
ruleId: "NS-ZK-007",
|
|
13
|
+
title: "Unsafe assertion over signals",
|
|
14
|
+
severity: "HIGH",
|
|
15
|
+
location: assertion,
|
|
16
|
+
signalName: assertion.referencedSignals[0],
|
|
17
|
+
explanation: `The assertion \`assert(${assertion.expression})\` references circuit signals. Circom assertions are not a substitute for proof constraints.`,
|
|
18
|
+
impact: "A circuit may rely on a condition that is not actually enforced in the constraint system.",
|
|
19
|
+
suggestedFix: "Replace signal assertions with constraints using === or equivalent constraint gadgets.",
|
|
20
|
+
tags: unsafeAssertionRule.tags
|
|
21
|
+
}));
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=NS-ZK-007-unsafe-assertion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-007-unsafe-assertion.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-007-unsafe-assertion.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,CAAC,MAAM,mBAAmB,GAAS;IACvC,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,+BAA+B;IACtC,WAAW,EAAE,4DAA4D;IACzE,eAAe,EAAE,MAAM;IACvB,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,aAAa,CAAC;IACzC,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,EAAE,CAAC,UAAU;aACzB,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC;aAC7D,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CACjB,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,+BAA+B;YACtC,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,SAAS;YACnB,UAAU,EAAE,SAAS,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAC1C,WAAW,EAAE,0BAA0B,SAAS,CAAC,UAAU,+FAA+F;YAC1J,MAAM,EAAE,2FAA2F;YACnG,YAAY,EAAE,wFAAwF;YACtG,IAAI,EAAE,mBAAmB,CAAC,IAAI;SAC/B,CAAC,CACH,CAAC;IACN,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
import { expressionHasDivisionOrInverse } from "../frontends/circom/circom-utils.js";
|
|
3
|
+
function denominatorOf(expression) {
|
|
4
|
+
const match = expression.match(/\/\s*([A-Za-z_][A-Za-z0-9_.\[\]]*)/);
|
|
5
|
+
return match?.[1];
|
|
6
|
+
}
|
|
7
|
+
function hasNonzeroGuard(assignment, denominator, context) {
|
|
8
|
+
if (!denominator)
|
|
9
|
+
return /Safe|NonZero|AssertNonZero/i.test(assignment.rhs);
|
|
10
|
+
return context.ir.constraints.some((constraint) => {
|
|
11
|
+
const compact = constraint.expression.replace(/\s+/g, "");
|
|
12
|
+
return compact.includes(`${denominator}*`) && compact.includes("===1");
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export const unsafeDivisionOrInverseRule = {
|
|
16
|
+
id: "NS-ZK-008",
|
|
17
|
+
title: "Unsafe division or inverse",
|
|
18
|
+
description: "Detects division, inverse, and IsZero-like patterns without clear nonzero guards.",
|
|
19
|
+
defaultSeverity: "HIGH",
|
|
20
|
+
tags: ["division", "inverse", "nonzero"],
|
|
21
|
+
analyze(context) {
|
|
22
|
+
return context.ir.assignments
|
|
23
|
+
.filter((assignment) => expressionHasDivisionOrInverse(assignment.rhs))
|
|
24
|
+
.filter((assignment) => !hasNonzeroGuard(assignment, denominatorOf(assignment.rhs), context))
|
|
25
|
+
.map((assignment) => buildIssue({
|
|
26
|
+
ruleId: "NS-ZK-008",
|
|
27
|
+
title: "Unsafe division or inverse",
|
|
28
|
+
severity: assignment.rhs.includes("/") ? "HIGH" : "MEDIUM",
|
|
29
|
+
confidence: assignment.rhs.includes("/") ? "HIGH" : "MEDIUM",
|
|
30
|
+
location: assignment,
|
|
31
|
+
signalName: assignment.lhs,
|
|
32
|
+
explanation: `The expression \`${assignment.rhs}\` uses division or inversion without an obvious nonzero guard.`,
|
|
33
|
+
impact: "Division and inversion require careful nonzero constraints. Missing guards can create unsound edge cases.",
|
|
34
|
+
suggestedFix: "Add explicit nonzero constraints or use a vetted safe inversion/IsZero gadget.",
|
|
35
|
+
tags: unsafeDivisionOrInverseRule.tags
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=NS-ZK-008-unsafe-division-or-inverse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-008-unsafe-division-or-inverse.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-008-unsafe-division-or-inverse.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,8BAA8B,EAAE,MAAM,qCAAqC,CAAC;AAErF,SAAS,aAAa,CAAC,UAAkB;IACvC,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACrE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,SAAS,eAAe,CAAC,UAAsB,EAAE,WAA+B,EAAE,OAAuC;IACvH,IAAI,CAAC,WAAW;QAAE,OAAO,6BAA6B,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;IAC5E,OAAO,OAAO,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;QAChD,MAAM,OAAO,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC1D,OAAO,OAAO,CAAC,QAAQ,CAAC,GAAG,WAAW,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,MAAM,2BAA2B,GAAS;IAC/C,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,4BAA4B;IACnC,WAAW,EAAE,mFAAmF;IAChG,eAAe,EAAE,MAAM;IACvB,IAAI,EAAE,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,CAAC;IACxC,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,EAAE,CAAC,WAAW;aAC1B,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,8BAA8B,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;aACtE,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,eAAe,CAAC,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;aAC5F,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAClB,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,4BAA4B;YACnC,QAAQ,EAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;YAC1D,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;YAC5D,QAAQ,EAAE,UAAU;YACpB,UAAU,EAAE,UAAU,CAAC,GAAG;YAC1B,WAAW,EAAE,oBAAoB,UAAU,CAAC,GAAG,iEAAiE;YAChH,MAAM,EAAE,2GAA2G;YACnH,YAAY,EAAE,gFAAgF;YAC9F,IAAI,EAAE,2BAA2B,CAAC,IAAI;SACvC,CAAC,CACH,CAAC;IACN,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
export const unconstrainedComponentOutputRule = {
|
|
3
|
+
id: "NS-ZK-009",
|
|
4
|
+
title: "Unconstrained component output",
|
|
5
|
+
description: "Detects component outputs copied through unsafe hint assignments.",
|
|
6
|
+
defaultSeverity: "CRITICAL",
|
|
7
|
+
tags: ["components", "outputs", "underconstraint"],
|
|
8
|
+
analyze(context) {
|
|
9
|
+
return context.graph
|
|
10
|
+
.assignedByHint()
|
|
11
|
+
.filter((assignment) => assignment.referencedSignals.some((ref) => context.graph.isComponentOutputReference(ref)) && !context.graph.appearsInAnyConstraint(assignment.lhs))
|
|
12
|
+
.map((assignment) => buildIssue({
|
|
13
|
+
ruleId: "NS-ZK-009",
|
|
14
|
+
title: "Unconstrained component output",
|
|
15
|
+
severity: "CRITICAL",
|
|
16
|
+
location: assignment,
|
|
17
|
+
signalName: assignment.lhs,
|
|
18
|
+
explanation: `The signal \`${assignment.lhs}\` is assigned from component output \`${assignment.rhs}\` using <-- and is not constrained afterward.`,
|
|
19
|
+
impact: "Component outputs must be bound into the constraint system. Otherwise the witness can choose arbitrary downstream values.",
|
|
20
|
+
suggestedFix: `Use \`${assignment.lhs} <== ${assignment.rhs}\` or add \`${assignment.lhs} === ${assignment.rhs}\`.`,
|
|
21
|
+
tags: unconstrainedComponentOutputRule.tags
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=NS-ZK-009-unconstrained-component-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-009-unconstrained-component-output.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-009-unconstrained-component-output.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,CAAC,MAAM,gCAAgC,GAAS;IACpD,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,gCAAgC;IACvC,WAAW,EAAE,mEAAmE;IAChF,eAAe,EAAE,UAAU;IAC3B,IAAI,EAAE,CAAC,YAAY,EAAE,SAAS,EAAE,iBAAiB,CAAC;IAClD,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,KAAK;aACjB,cAAc,EAAE;aAChB,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;aAC1K,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAClB,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,gCAAgC;YACvC,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,UAAU;YACpB,UAAU,EAAE,UAAU,CAAC,GAAG;YAC1B,WAAW,EAAE,gBAAgB,UAAU,CAAC,GAAG,0CAA0C,UAAU,CAAC,GAAG,gDAAgD;YACnJ,MAAM,EAAE,2HAA2H;YACnI,YAAY,EAAE,SAAS,UAAU,CAAC,GAAG,QAAQ,UAAU,CAAC,GAAG,eAAe,UAAU,CAAC,GAAG,QAAQ,UAAU,CAAC,GAAG,KAAK;YACnH,IAAI,EAAE,gCAAgC,CAAC,IAAI;SAC5C,CAAC,CACH,CAAC;IACN,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
const BN254_SAFE_BITS = 253;
|
|
3
|
+
export const aliasOverflowRiskRule = {
|
|
4
|
+
id: "NS-ZK-010",
|
|
5
|
+
title: "Alias or overflow risk",
|
|
6
|
+
description: "Detects suspicious bit decompositions and limb arrays that may allow aliasing.",
|
|
7
|
+
defaultSeverity: "MEDIUM",
|
|
8
|
+
tags: ["aliasing", "overflow", "range"],
|
|
9
|
+
analyze(context) {
|
|
10
|
+
const issues = [];
|
|
11
|
+
for (const component of context.ir.components) {
|
|
12
|
+
const width = Number(component.params?.match(/\d+/)?.[0]);
|
|
13
|
+
if (/Num2Bits|Bits2Num|Decompose/.test(component.templateType ?? "") && width >= BN254_SAFE_BITS) {
|
|
14
|
+
const hasAliasCheck = context.ir.components.some((candidate) => /AliasCheck/.test(candidate.templateType ?? ""));
|
|
15
|
+
if (!hasAliasCheck) {
|
|
16
|
+
issues.push(buildIssue({
|
|
17
|
+
ruleId: "NS-ZK-010",
|
|
18
|
+
title: "Alias or overflow risk",
|
|
19
|
+
severity: width >= 254 ? "HIGH" : "MEDIUM",
|
|
20
|
+
confidence: "MEDIUM",
|
|
21
|
+
location: component,
|
|
22
|
+
signalName: component.name,
|
|
23
|
+
explanation: `The component \`${component.name}\` uses ${component.templateType}(${width}) near or above the BN254 scalar field bit length without an obvious AliasCheck.`,
|
|
24
|
+
impact: "Values may have multiple field representations or exceed intended integer ranges.",
|
|
25
|
+
suggestedFix: "Use a safe bit width for the field, add AliasCheck for large decompositions, and document the intended integer domain.",
|
|
26
|
+
tags: aliasOverflowRiskRule.tags
|
|
27
|
+
}));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const signal of context.ir.signals.filter((signal) => signal.arrayDimensions.length > 0 && /limb/i.test(signal.name) && !context.graph.hasRangeCheck(signal.name))) {
|
|
32
|
+
issues.push(buildIssue({
|
|
33
|
+
ruleId: "NS-ZK-010",
|
|
34
|
+
title: "Alias or overflow risk",
|
|
35
|
+
severity: "MEDIUM",
|
|
36
|
+
confidence: "LOW",
|
|
37
|
+
location: signal,
|
|
38
|
+
signalName: signal.name,
|
|
39
|
+
explanation: `The limb array \`${signal.name}\` does not have an obvious per-limb bound in the parsed constraints.`,
|
|
40
|
+
impact: "Unbounded limbs can overflow their intended representation or admit multiple encodings.",
|
|
41
|
+
suggestedFix: "Add explicit per-limb range checks and recomposition constraints.",
|
|
42
|
+
tags: aliasOverflowRiskRule.tags
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
return issues;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=NS-ZK-010-alias-overflow-risk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-010-alias-overflow-risk.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-010-alias-overflow-risk.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,eAAe,GAAG,GAAG,CAAC;AAE5B,MAAM,CAAC,MAAM,qBAAqB,GAAS;IACzC,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,wBAAwB;IAC/B,WAAW,EAAE,gFAAgF;IAC7F,eAAe,EAAE,QAAQ;IACzB,IAAI,EAAE,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,CAAC;IACvC,OAAO,CAAC,OAAO;QACb,MAAM,MAAM,GAAG,EAAE,CAAC;QAClB,KAAK,MAAM,SAAS,IAAI,OAAO,CAAC,EAAE,CAAC,UAAU,EAAE,CAAC;YAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1D,IAAI,6BAA6B,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,IAAI,EAAE,CAAC,IAAI,KAAK,IAAI,eAAe,EAAE,CAAC;gBACjG,MAAM,aAAa,GAAG,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC;gBACjH,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,MAAM,CAAC,IAAI,CACT,UAAU,CAAC;wBACT,MAAM,EAAE,WAAW;wBACnB,KAAK,EAAE,wBAAwB;wBAC/B,QAAQ,EAAE,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;wBAC1C,UAAU,EAAE,QAAQ;wBACpB,QAAQ,EAAE,SAAS;wBACnB,UAAU,EAAE,SAAS,CAAC,IAAI;wBAC1B,WAAW,EAAE,mBAAmB,SAAS,CAAC,IAAI,WAAW,SAAS,CAAC,YAAY,IAAI,KAAK,kFAAkF;wBAC1K,MAAM,EAAE,mFAAmF;wBAC3F,YAAY,EAAE,wHAAwH;wBACtI,IAAI,EAAE,qBAAqB,CAAC,IAAI;qBACjC,CAAC,CACH,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QACD,KAAK,MAAM,MAAM,IAAI,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxK,MAAM,CAAC,IAAI,CACT,UAAU,CAAC;gBACT,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,wBAAwB;gBAC/B,QAAQ,EAAE,QAAQ;gBAClB,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,MAAM,CAAC,IAAI;gBACvB,WAAW,EAAE,oBAAoB,MAAM,CAAC,IAAI,uEAAuE;gBACnH,MAAM,EAAE,yFAAyF;gBACjG,YAAY,EAAE,mEAAmE;gBACjF,IAAI,EAAE,qBAAqB,CAAC,IAAI;aACjC,CAAC,CACH,CAAC;QACJ,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
export const unusedSignalRule = {
|
|
3
|
+
id: "NS-ZK-011",
|
|
4
|
+
title: "Unused signal",
|
|
5
|
+
description: "Detects declared signals that are never assigned and never constrained.",
|
|
6
|
+
defaultSeverity: "LOW",
|
|
7
|
+
tags: ["dead-code", "completeness"],
|
|
8
|
+
analyze(context) {
|
|
9
|
+
return context.ir.signals
|
|
10
|
+
.filter((signal) => signal.kind !== "output")
|
|
11
|
+
.filter((signal) => context.graph.assignmentsForSignal(signal.name).length === 0 && !context.graph.appearsInAnyConstraint(signal.name))
|
|
12
|
+
.map((signal) => buildIssue({
|
|
13
|
+
ruleId: "NS-ZK-011",
|
|
14
|
+
title: "Unused signal",
|
|
15
|
+
severity: "LOW",
|
|
16
|
+
confidence: "MEDIUM",
|
|
17
|
+
location: signal,
|
|
18
|
+
signalName: signal.name,
|
|
19
|
+
explanation: `The signal \`${signal.name}\` is declared but was not assigned or constrained in the parsed circuit.`,
|
|
20
|
+
impact: "Unused signals may indicate incomplete constraints or dead circuit logic.",
|
|
21
|
+
suggestedFix: "Remove the signal if it is dead, or add the intended assignment and constraints.",
|
|
22
|
+
tags: unusedSignalRule.tags
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=NS-ZK-011-unused-signal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-011-unused-signal.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-011-unused-signal.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAEtD,MAAM,CAAC,MAAM,gBAAgB,GAAS;IACpC,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,eAAe;IACtB,WAAW,EAAE,yEAAyE;IACtF,eAAe,EAAE,KAAK;IACtB,IAAI,EAAE,CAAC,WAAW,EAAE,cAAc,CAAC;IACnC,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,EAAE,CAAC,OAAO;aACtB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;aAC5C,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;aACtI,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACd,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,eAAe;YACtB,QAAQ,EAAE,KAAK;YACf,UAAU,EAAE,QAAQ;YACpB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,WAAW,EAAE,gBAAgB,MAAM,CAAC,IAAI,2EAA2E;YACnH,MAAM,EAAE,2EAA2E;YACnF,YAAY,EAAE,kFAAkF;YAChG,IAAI,EAAE,gBAAgB,CAAC,IAAI;SAC5B,CAAC,CACH,CAAC;IACN,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { buildIssue } from "../core/issue-builder.js";
|
|
2
|
+
import { isSelectorLikeName } from "../frontends/circom/circom-utils.js";
|
|
3
|
+
function looksLikeMux(snippet, signalName) {
|
|
4
|
+
if (!snippet)
|
|
5
|
+
return false;
|
|
6
|
+
const lower = snippet.toLowerCase();
|
|
7
|
+
return lower.includes(signalName.toLowerCase()) && (lower.includes("(1-") || lower.includes("1 -") || lower.includes("?") || lower.includes("*"));
|
|
8
|
+
}
|
|
9
|
+
export const suspiciousSelectorRule = {
|
|
10
|
+
id: "NS-ZK-012",
|
|
11
|
+
title: "Suspicious selector without booleanity",
|
|
12
|
+
description: "Detects selector-like signals used in conditional formulas without booleanity constraints.",
|
|
13
|
+
defaultSeverity: "HIGH",
|
|
14
|
+
tags: ["selectors", "booleanity", "branching"],
|
|
15
|
+
analyze(context) {
|
|
16
|
+
return context.ir.signals
|
|
17
|
+
.filter((signal) => isSelectorLikeName(signal.baseName))
|
|
18
|
+
.filter((signal) => !context.graph.hasBooleanityConstraint(signal.name))
|
|
19
|
+
.filter((signal) => context.graph.signalReferences(signal.name).some((ref) => looksLikeMux(ref.snippet, signal.baseName)))
|
|
20
|
+
.map((signal) => buildIssue({
|
|
21
|
+
ruleId: "NS-ZK-012",
|
|
22
|
+
title: "Suspicious selector without booleanity",
|
|
23
|
+
severity: "HIGH",
|
|
24
|
+
location: signal,
|
|
25
|
+
signalName: signal.name,
|
|
26
|
+
explanation: `The selector-like signal \`${signal.name}\` appears in conditional or multiplexer-style arithmetic without a booleanity constraint.`,
|
|
27
|
+
impact: "Selectors must usually be boolean. If not constrained to 0/1, a prover can create invalid linear combinations or bypass branches.",
|
|
28
|
+
suggestedFix: `Add \`${signal.name} * (${signal.name} - 1) === 0;\` or use a vetted selector gadget.`,
|
|
29
|
+
tags: suspiciousSelectorRule.tags
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=NS-ZK-012-suspicious-selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-ZK-012-suspicious-selector.js","sourceRoot":"","sources":["../../src/rules/NS-ZK-012-suspicious-selector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAEzE,SAAS,YAAY,CAAC,OAA2B,EAAE,UAAkB;IACnE,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACpC,OAAO,KAAK,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC;AACpJ,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAS;IAC1C,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,wCAAwC;IAC/C,WAAW,EAAE,4FAA4F;IACzG,eAAe,EAAE,MAAM;IACvB,IAAI,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,WAAW,CAAC;IAC9C,OAAO,CAAC,OAAO;QACb,OAAO,OAAO,CAAC,EAAE,CAAC,OAAO;aACtB,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;aACvD,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;aACvE,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;aACzH,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CACd,UAAU,CAAC;YACT,MAAM,EAAE,WAAW;YACnB,KAAK,EAAE,wCAAwC;YAC/C,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,MAAM;YAChB,UAAU,EAAE,MAAM,CAAC,IAAI;YACvB,WAAW,EAAE,8BAA8B,MAAM,CAAC,IAAI,4FAA4F;YAClJ,MAAM,EAAE,mIAAmI;YAC3I,YAAY,EAAE,SAAS,MAAM,CAAC,IAAI,OAAO,MAAM,CAAC,IAAI,iDAAiD;YACrG,IAAI,EAAE,sBAAsB,CAAC,IAAI;SAClC,CAAC,CACH,CAAC;IACN,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { buildIssue } from "../../core/issue-builder.js";
|
|
2
|
+
import { halo2Graph, hasHalo2 } from "./halo2-rule-utils.js";
|
|
3
|
+
export const halo2AssignedAdviceNotConstrainedRule = {
|
|
4
|
+
id: "NS-H2-001",
|
|
5
|
+
title: "Assigned advice not constrained",
|
|
6
|
+
description: "Detects Halo2 advice assignments that do not appear connected to gates, equality constraints, instance constraints, or lookups.",
|
|
7
|
+
defaultSeverity: "HIGH",
|
|
8
|
+
tags: ["halo2", "advice", "underconstraint"],
|
|
9
|
+
analyze(context) {
|
|
10
|
+
if (!hasHalo2(context))
|
|
11
|
+
return [];
|
|
12
|
+
const graph = halo2Graph(context);
|
|
13
|
+
if (!graph)
|
|
14
|
+
return [];
|
|
15
|
+
return (context.halo2?.assignments ?? [])
|
|
16
|
+
.filter((assignment) => assignment.assignmentType === "advice")
|
|
17
|
+
.filter((assignment) => !graph.isAssignmentConnected(assignment))
|
|
18
|
+
.map((assignment) => {
|
|
19
|
+
const node = graph.assignmentNode(assignment);
|
|
20
|
+
return buildIssue({
|
|
21
|
+
ruleId: "NS-H2-001",
|
|
22
|
+
title: "Assigned advice not constrained",
|
|
23
|
+
severity: /output|commit|root|instance|public/i.test(assignment.expression) ? "CRITICAL" : "HIGH",
|
|
24
|
+
confidence: "MEDIUM",
|
|
25
|
+
location: assignment,
|
|
26
|
+
signalName: assignment.label ?? assignment.columnName ?? assignment.target,
|
|
27
|
+
explanation: `The assigned advice value \`${assignment.label ?? assignment.columnName ?? "unknown"}\` was not connected to any parsed gate expression, equality edge, copy constraint, lookup, or public instance binding.`,
|
|
28
|
+
impact: "A prover-controlled advice value may be assigned in a region without being bound by the constraint system.",
|
|
29
|
+
suggestedFix: "Reference the assigned cell in a gate expression, lookup, constrain_equal call, or constrain_instance call as intended.",
|
|
30
|
+
tags: halo2AssignedAdviceNotConstrainedRule.tags,
|
|
31
|
+
metadata: {
|
|
32
|
+
regionName: node?.region?.name,
|
|
33
|
+
columnName: node?.columnName,
|
|
34
|
+
connectedBy: []
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=NS-H2-001-assigned-advice-not-constrained.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NS-H2-001-assigned-advice-not-constrained.js","sourceRoot":"","sources":["../../../src/rules/halo2/NS-H2-001-assigned-advice-not-constrained.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAEzD,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAE7D,MAAM,CAAC,MAAM,qCAAqC,GAAS;IACzD,EAAE,EAAE,WAAW;IACf,KAAK,EAAE,iCAAiC;IACxC,WAAW,EAAE,iIAAiI;IAC9I,eAAe,EAAE,MAAM;IACvB,IAAI,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,iBAAiB,CAAC;IAC5C,OAAO,CAAC,OAAO;QACb,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,OAAO,EAAE,CAAC;QAClC,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAC;QACtB,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,IAAI,EAAE,CAAC;aACtC,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,cAAc,KAAK,QAAQ,CAAC;aAC9D,MAAM,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;aAChE,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE;YAClB,MAAM,IAAI,GAAG,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;YAC9C,OAAO,UAAU,CAAC;gBAChB,MAAM,EAAE,WAAW;gBACnB,KAAK,EAAE,iCAAiC;gBACxC,QAAQ,EAAE,qCAAqC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM;gBACjG,UAAU,EAAE,QAAQ;gBACpB,QAAQ,EAAE,UAAU;gBACpB,UAAU,EAAE,UAAU,CAAC,KAAK,IAAI,UAAU,CAAC,UAAU,IAAI,UAAU,CAAC,MAAM;gBAC1E,WAAW,EAAE,+BAA+B,UAAU,CAAC,KAAK,IAAI,UAAU,CAAC,UAAU,IAAI,SAAS,yHAAyH;gBAC3N,MAAM,EAAE,4GAA4G;gBACpH,YAAY,EAAE,yHAAyH;gBACvI,IAAI,EAAE,qCAAqC,CAAC,IAAI;gBAChD,QAAQ,EAAE;oBACR,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI;oBAC9B,UAAU,EAAE,IAAI,EAAE,UAAU;oBAC5B,WAAW,EAAE,EAAE;iBAChB;aACF,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACP,CAAC;CACF,CAAC"}
|