
o1js/Mina zkApps और Noir सर्किट में zk सर्किट साउंडनेस बग्स के लिए डिपेंडेंसी-फ्री स्टैटिक एनालाइज़र
कम्युनिटी पैकेज:
o1js-scanआधिकारिक o1js Community Packages निर्देशिका में सूचीबद्ध है।
नवीनतम: 0.20.0 — एनालाइज़र अब उन कॉन्ट्रैक्ट्स को पढ़ता है जो
extends TokenContractकरते हैं। इस रिलीज़ से पहले कॉन्ट्रैक्ट गेट केवलSmartContractसे मेल खाता था, इसलिए इकोसिस्टम में हर फंजिबल टोकन, NFT कलेक्शन और AMM पूल "no findings" के रूप में स्कैन होता था। यदि आपने 0.20.0 से पहले किसी टोकन कॉन्ट्रैक्ट को स्कैन किया था, तो उसे दोबारा स्कैन करें। देखें CHANGELOG।
इनमें zk सर्किट साउंडनेस बग्स के लिए एक तेज़, निर्भरता-रहित स्टैटिक एनालाइज़र:
.ts / .js) — @method बॉडीज़ से बने Kimchi सर्किट.nr) — Aztec का Rust-जैसा ZK DSL (aztec-nr-आकार के पैटर्न सहित)सुरक्षा-महत्वपूर्ण बग्स आमतौर पर प्रूविंग सिस्टम में नहीं होते — वे
एप्लिकेशन के अपने कंस्ट्रेंट्स में होते हैं: ऐसे विटनेस जिन्हें प्रूवर नियंत्रित करता है लेकिन सर्किट
उन्हें कभी बाइंड नहीं करता। o1js-scan Mina और Noir इकोसिस्टम में Circom के सगे-संबंधियों के लिए
अंडर-कंस्ट्रेंड-सिग्नल स्कैनर है।```bash
pip install o1js-scan
o1js-scan path/to/zkapp # o1js + Noir (auto) noir-scan path/to/circuits # same binary — Noir-friendly alias noir-scan . --lang noir --fail-on high --sarif noir.sarif
### उदाहरण
एक vault दिया गया है जिसका `withdraw` amount एक prover-नियंत्रित witness है जो
कभी भी on-chain state से bound नहीं होता:```console
$ o1js-scan examples/vulnerable_vault.ts --include-examples
LOW O1JS_UNCONSTRAINED_RECIPIENT vulnerable_vault.ts:23 fn=withdraw Recipient `to` is prover-chosen in `withdraw`
HIGH O1JS_UNCONSTRAINED_WITNESS vulnerable_vault.ts:23 fn=withdraw Unconstrained witness `amount` flows to send_amount in `withdraw`
o1js-scan: 2 finding(s) [1 high, 1 low] in 1 of 1 file(s) — fails (--fail-on high)
$ echo $?
1
--include-examples यहाँ केवल इसलिए आवश्यक है क्योंकि डेमो फ़ाइल
examples/ के अंतर्गत रहती है, जिसे पथ वर्गीकरणकर्ता डिफ़ॉल्ट रूप से डाउनग्रेड कर देता है ताकि किसी रेपो का
अपना नमूना कोड उसके बिल्ड को विफल न कर सके। आपके src/ में वही अनुबंध बिना किसी फ़्लैग के
HIGH रिपोर्ट करता है।
HIGH निष्कर्ष वह ड्रेन करने योग्य बग है। ठीक किया गया अनुबंध
(examples/safe_vault.ts) इसे हटा देता है और 0 के साथ बाहर निकल जाता है, केवल प्रोवर-चयनित प्राप्तकर्ता पर
सूचनात्मक LOW बनाए रखते हुए:```console
$ o1js-scan examples/safe_vault.ts --include-examples
LOW O1JS_UNCONSTRAINED_RECIPIENT safe_vault.ts:23 fn=withdraw Recipient to is prover-chosen in withdraw
o1js-scan: 1 finding(s) [1 low] in 1 of 1 file(s) — passes (--fail-on high)
$ echo $?
0
o1js और Noir के vulnerable/fixed जोड़ों के लिए [`examples/`](https://github.com/auditinfra-io/o1js-scan/blob/main/examples) देखें।
## विषय-सूची
- [इंस्टॉल](#install)
- [उपयोग](#usage) · [किसी निष्कर्ष को दबाना](#suppressing-a-reviewed-finding)
- [GitHub Action](#github-action)
- [यह क्या पहचानता है — o1js](#what-it-detects-o1js) · [Noir](#what-it-detects-noir)
- [ज्ञात सीमाएँ](#known-limitations) · [यह टूल कहाँ रुक जाता है](#where-this-tool-stops)
- [गोपनीयता और निजी कोड](#privacy-and-private-code)
- [पोस्ट-क्वांटम समीक्षा](#post-quantum-review)
- [संगतता](#compatibility) · [यह कैसे काम करता है](#how-it-works)
- [योगदान](#roadmap--contributing)
## इंस्टॉल```bash
pip install o1js-scan
एक पृथक वैश्विक CLI इंस्टॉल के लिए, pipx का उपयोग करें:```bash
pipx install o1js-scan
Node/npm-आधारित Noir, Aztec, या o1js ऐप रिपॉज़िटरी के लिए, npm wrapper इंस्टॉल करें:```bash
npm install -D o1js-scan
npx noir-scan . --lang noir --fail-on high
npm पैकेज उसी Python विश्लेषक के चारों ओर एक पतला रैपर है और इसके लिए PATH (python3 या python) पर Python 3.8+ की आवश्यकता होती है। किसी विशिष्ट इंटरप्रेटर को चुनने के लिए O1JS_SCAN_PYTHON सेट करें।
या स्रोत से:```bash git clone https://github.com/auditinfra-io/o1js-scan cd o1js-scan pip install -e .
कोई तृतीय-पक्ष Python निर्भरता नहीं। Python 3.8+। `noir-scan` कंसोल स्क्रिप्ट `o1js-scan` के साथ (समान एंट्री पॉइंट) इंस्टॉल होती है, जिसमें npm रैपर के माध्यम से भी शामिल है।
## उपयोग```bash
# scan a directory (recursively; skips node_modules, target/, .git, …)
o1js-scan path/to/project
# Noir-only / o1js-only
noir-scan circuits --lang noir
o1js-scan src --lang o1js
# scan a single file
o1js-scan src/MyContract.ts
noir-scan src/main.nr
# machine-readable output for CI
o1js-scan src --json
# SARIF 2.1.0 for GitHub code scanning (writes o1js-scan.sarif by default)
o1js-scan src --sarif
noir-scan . --lang noir --sarif noir.sarif
# choose which severity fails CI (critical|high|medium|low|none; default high)
o1js-scan src --fail-on medium
# progressive/power-user gate (equivalent to --fail-on medium)
o1js-scan src --strict
# test code is excluded by default (both backends); opt back in
o1js-scan src --include-tests
# example code is downgraded to LOW by default; keep original severity
o1js-scan src --include-examples
o1js-scan --version
Exit code 1 होता है जब --fail-on स्तर (डिफ़ॉल्ट high) पर या उससे ऊपर कोई finding मौजूद हो और अन्यथा 0 — इसलिए आप इसे सीधे CI में डाल सकते हैं। डिफ़ॉल्ट के साथ, low/medium finding (नीचे दिए गए informational recipient rule सहित) build को fail नहीं करता; केवल रिपोर्ट करने के लिए --fail-on none उपयोग करें, या अधिक कड़ाई से gate करने के लिए --strict (जो --fail-on medium का shorthand है) उपयोग करें, जबकि low-severity findings को advisory मानते हुए। दोनों विकल्प mutually exclusive हैं ताकि CI configuration अस्पष्ट न हो। अनुपस्थित scan path stderr पर error के साथ 2 return करता है, इसलिए एक typo चुपचाप CI को clean run के रूप में पास नहीं कर सकता। प्रत्येक run stderr पर एक-पंक्ति का summary (severity के अनुसार counts और gate verdict) print करता है।
Test code डिफ़ॉल्ट रूप से बाहर रखा जाता है — दोनों backends। Tests जानबूझकर invalid values और bad transactions बनाते हैं ताकि साबित हो सके कि asserts उन्हें reject करते हैं, इसलिए वहाँ कोई finding circuit bug के बजाय test का उद्देश्य होता है। कोई file test code मानी जाती है जब:
*.test.ts / *.spec.ts से match करता हो (और .js/.jsx/.tsx/.mjs/.cjs variants), या *_test.nr / test_*.nr;test/, tests/, __tests__/, spec/ या __mocks__/ directory के अंतर्गत हो;#[test] / attribute हो, या वह / block के अंदर हो — block-scoped, इसलिए production file के अंत में स्थित test module बाकी file को silence नहीं करता।उन्हें report करने के लिए --include-tests पास करें।
Example code downgrade किया जाता है, हटाया नहीं जाता। examples/ या example/ directory में, या *.eg.ts नाम की file में (.nr और अन्य JS/TS extensions भी) कोई finding, एक note के साथ LOW पर लाया जाता है — अभी भी report होता है, अब build fail नहीं कर सकता। Example code जानबूझकर सरल बनाया जाता है, और किसी framework के अपने examples को vulnerabilities के रूप में flag करना शोर है; लेकिन यह test code की तुलना में production में copy होने की संभावना बहुत अधिक होती है, इसीलिए इसे छिपाने के बजाय downgrade किया जाता है। मूल severity बनाए रखने के लिए --include-examples पास करें।
जब भी कोई policy लागू होती है, run stderr पर एक पंक्ति print करता है — जैसे 6 file(s) skipped as test code, 1 finding(s) downgraded as examples — ताकि एक quiet scan कभी चुपचाप quiet न हो। Counts SARIF में invocation.properties के अंतर्गत भी दिखाई देते हैं। trade-off पर ध्यान दें: detection केवल path-based है (कोई describe(/it( parsing नहीं), इसलिए tests/ के अंतर्गत रखा production circuit skip हो जाएगा — stderr line ही वह तरीका है जिससे आप इसे नोटिस करते हैं।
Tree walk करते समय skip की जाने वाली directories: node_modules, target (nargo), .git, dist, build, __pycache__, .venv, venv.
बिना gate ढीला किए किसी triaged finding को silence करें, flagged line पर — या उसके ऊपर की line पर — एक inline comment के साथ:```ts this.send({ to, amount }); // o1js-scan-disable-line O1JS_UNCONSTRAINED_WITNESS
// o1js-scan-disable-next-line this.send({ to, amount });
| `--no-ssl` | SSL सत्यापन अक्षम करें |
| `--proxy` | प्रॉक्सी URL (http/https/socks5) |
| `--timeout` | अनुरोध टाइमआउट सेकंड में |
| `--user-agent` | कस्टम User-Agent स्ट्रिंग |
| `--headers` | कस्टम हेडर (key:value,key:value) |
| `--cookie` | कुकी स्ट्रिंग |
| `--follow-redirects` | रीडायरेक्ट का अनुसरण करें |
| `--max-redirects` | अधिकतम रीडायरेक्ट |
| `--retries` | पुनः प्रयास संख्या |
| `--delay` | अनुरोधों के बीच विलंब (सेकंड) |
| `--random-agent` | यादृच्छिक User-Agent का उपयोग करें |
| `--tor` | Tor नेटवर्क के माध्यम से रूट करें |
| `--verbose` | विस्तृत आउटपुट |
| `--silent` | साइलेंट मोड |
| `--output` | आउटपुट फ़ाइल |
| `--format` | आउटपुट प्रारूप (json/xml/csv) |
| `--threads` | थ्रेड की संख्या |
| `--config` | कॉन्फ़िगरेशन फ़ाइल पथ |```nr
let inv = unsafe { hint(x) }; // o1js-scan-disable-line NOIR_UNCONSTRAINED_WITNESS
एक या अधिक rule ids की सूची दें ताकि केवल उन्हीं को दबाया जाए; एक bare directive (कोई ids नहीं) target line पर हर rule को दबा देता है।
एक library के रूप में:```python from o1js_scan import analyze_file, analyze_project
for path, finding in analyze_project("src", lang="auto"): print(path, finding.rule_id, finding.severity.value, finding.title)
## GitHub Action
कुछ पंक्तियों में स्कैनर को CI में जोड़ें। निष्कर्ष PR diff पर एनोटेशन के रूप में और रिपॉज़िटरी के **Security → Code scanning** टैब में अलर्ट के रूप में दिखाई देते हैं।```yaml
# .github/workflows/o1js-scan.yml
name: o1js-scan
on: [push, pull_request]
permissions:
contents: read
security-events: write # required to upload SARIF to code scanning
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: auditinfra-io/[email protected]
with:
path: src # optional, defaults to the repo root
lang: auto # auto | o1js | noir
# version: 0.20.0 # optional, pin the scanner version
# fail-on: high # optional, fail the job on high/critical
उन Noir प्रोजेक्ट्स के लिए अनुशंसित जो कोड-स्कैनिंग अलर्ट और उच्च-गंभीरता गेट चाहते हैं:```yaml
या बिना Action के:```bash
pip install o1js-scan
noir-scan . --lang noir --fail-on high --sarif noir.sarif
Inputs: `path` (डिफ़ॉल्ट `.`), `lang` (`auto`|`o1js`|`noir`, डिफ़ॉल्ट `auto`),
`version` (इंस्टॉल करने के लिए PyPI संस्करण, डिफ़ॉल्ट नवीनतम), `upload-sarif` (डिफ़ॉल्ट
`true`), `fail-on` (`critical`|`high`|`medium`|`low`|`none`, डिफ़ॉल्ट `none`),
`fail-on-findings` (अप्रचलित, डिफ़ॉल्ट `false`), `include-tests` (डिफ़ॉल्ट
`false`), `include-examples` (डिफ़ॉल्ट `false`)। आउटपुट: `sarif-file`। SARIF
अपलोड के लिए `security-events: write` और कोड स्कैनिंग सक्षम होना आवश्यक है।
रिपोर्ट और गेट एक ही आर्ग्युमेंट ऐरे से बनाए जाते हैं, इसलिए `include-tests`
और `include-examples` दोनों पर लागू होते हैं — जो SARIF आप पढ़ते हैं और जिस exit code पर आप
गेट करते हैं, वे हमेशा एक ही स्रोत सेट का वर्णन करते हैं। रिपोर्टिंग पास
`--fail-on none` पर चलता है ताकि findings कभी भी SARIF अपलोड को ब्लॉक न करें, लेकिन एक परिचालन
विफलता (एक पथ जो मौजूद नहीं है, एक CLI उपयोग त्रुटि) अभी भी स्टेप को विफल कर देती है
बजाय इसके कि इसे एक स्वच्छ स्कैन के रूप में रिपोर्ट किया जाए।
`fail-on-findings: true` संगतता के लिए रखा गया है और जब `fail-on` को `none` पर छोड़ा जाता है तो यह `fail-on: high` पर मैप होता है; यह एक अप्रचलन चेतावनी देता है। `fail-on` को प्राथमिकता दें, जो किसी भी गंभीरता पर गेट कर सकता है।
## यह क्या पहचानता है (o1js)
### एक नज़र में समर्थित नियम
<!-- BEGIN GENERATED RULE SUMMARY -->
| बैकएंड | नियम | High-सक्षम | Medium-सक्षम | Low-सक्षम |
|---------|------:|-------------:|---------------:|------------:|
| o1js | 18 | 11 | 12 | 2 |
| Noir | 11 | 4 | 9 | 1 |
| **कुल** | **29** | **15** | **21** | **3** |
<!-- END GENERATED RULE SUMMARY -->
गणनाएँ प्रत्येक बैकएंड द्वारा समर्थित विशिष्ट नियम ID हैं। एक नियम जो संदर्भ के अनुसार
गंभीरता निर्धारित करता है (उदाहरण के लिए, एक मान हस्तांतरण के लिए high और एक स्टेट राइट के लिए
medium) एक से अधिक गंभीरता कॉलम में दिखाई देता है, इसलिए गंभीरता कॉलम जानबूझकर
नियम कुल तक नहीं जुड़ते। वर्तमान में कोई critical- या info-गंभीरता नियम नहीं हैं। पूर्ण विवरण और
false-positive गार्ड नीचे दिए गए हैं।
<!-- BEGIN GENERATED O1JS RULE TABLE -->
| नियम | गंभीरता | इसका क्या अर्थ है |
|------|----------|---------------|
| `O1JS_MISSING_STATE_PRECONDITION` | high | `this.x.get()` बिना मेल खाते `requireEquals(...)` / `getAndRequireEquals()` के पढ़ा गया। एक बिना शर्त `get()` **कोई** अकाउंट पूर्वशर्त नहीं जोड़ता, इसलिए प्रूफ `x` को उसके ऑन-चेन मान से बाइंड नहीं करता — एक प्रूवर किसी भी मान को प्रतिस्थापित कर सकता है। |
| `O1JS_UNCONSTRAINED_WITNESS` | high / medium | एक `@method` आर्ग्युमेंट (एक प्रूवर-नियंत्रित निजी witness) एक send **amount** (`this.send(...)` या एक same-method `AccountUpdate.create*(...).send(...)`) या एक स्टेट `.set(...)` में प्रवाहित होता है और **कभी** assert नहीं किया जाता। एक under-constrained Circom सिग्नल का सीधा अनुरूप। High जब यह एक मान हस्तांतरण तक पहुँचता है। |
| `O1JS_UNCONSTRAINED_PROVABLE_WITNESS` | high / medium / low | एक `Provable.witness(...)` लोकल **बिना** किसी in-circuit assertion के एक send/state प्रभाव में प्रवाहित होता है। witness कॉलबैक सर्किट के *बाहर* चलता है (यह केवल एक प्रूवर संकेत है), इसलिए परिणाम एक नया प्रूवर-नियंत्रित मान है — `@method` args के अलावा अन्य witness स्रोत। इसे पुनः व्युत्पन्न और assert किया जाना चाहिए (`x.assertEquals(<recomputed>)`) या स्टेट से बाइंड किया जाना चाहिए। High एक send amount पर (`this.send(...)` या same-method `AccountUpdate.create*`)। |
| `O1JS_UNCONSTRAINED_RECIPIENT` | low | एक `@method` आर्ग्युमेंट **केवल** एक send के `to:` प्राप्तकर्ता के रूप में उपयोग किया जाता है। यह आमतौर पर इरादा होता है (एक उपयोगकर्ता अपने स्वयं के निकासी गंतव्य का नाम देता है) और यह सूचनात्मक है — यह केवल तभी मायने रखता है जब गंतव्य एक निश्चित ट्रेजरी या एक स्टेट-रिकॉर्ड किया गया पता होना चाहिए। CI exit-code गेट को **नहीं** ट्रिप करता। |
| `O1JS_WITNESS_NOT_BOUND_TO_STATE` | medium | एक witness केवल *तुच्छ रूप से* constrained है (उदा. `> 0`, या एक स्थिरांक के विरुद्ध तुलना) एक प्रभाव से पहले — कभी भी ऑन-चेन स्टेट से बंधा नहीं। पुष्टि करें कि ऑफ-चेन ऑर्केस्ट्रेशन इसे सुरक्षित बनाता है, या बैलेंस अपने स्थायी मान तक drainable है। |
| `O1JS_STALE_MERKLE_ROOT` | high | एक method एक प्रूवर-आपूर्ति witness (`computeRootAndKey` / `calculateRoot`) से एक Merkle root की पुनर्गणना करता है लेकिन पुनर्गणना किए गए roots में से **किसी को भी** वर्तमान ऑन-चेन root से बाइंड नहीं करता। लाइव root के विरुद्ध `this.root.requireEquals(...)` / `assertEquals` के बिना, एक प्रूवर एक निर्मित या पुराने ट्री के लिए एक witness पास कर सकता है — सदस्यता को फोर्ज करना या पुराने स्टेट को रीप्ले करना। बाइंडिंग एक undecorated same-class helper (`this.verifyX(witness)`) में रह सकती है; helper propagation उसे कवर करता है। |
| `O1JS_UNVERIFIED_PROOF` | high | एक `@method` पैरामीटर जो `Proof<...>` / `SelfProof<...>` / `DynamicProof<...>` के रूप में टाइप किया गया है, उसके सार्वजनिक फ़ील्ड्स का उपयोग करने से पहले कभी `.verify()` नहीं किया जाता। एक Proof पास करना इसे सत्यापित नहीं करता — एक स्पष्ट verify के बिना प्रूवर एक मनमाना proof ऑब्जेक्ट आपूर्ति कर सकता है, और इसके `publicOutput` का कोई भी उपयोग unconstrained है। यह तब भी फायर होता है जब `.verifyIf(flag)` एक unconstrained `@method` आर्ग्युमेंट द्वारा गेट किया जाता है और proof के सार्वजनिक फ़ील्ड्स पढ़े जाते हैं, क्योंकि प्रूवर शर्त को गलत बना सकता है। |
| `O1JS_UNASSERTED_BOOL` | high / medium | एक o1js predicate (`equals` / `lessThanOrEqual` / …) एक `Bool` लौटाता है और **कोई** constraint नहीं जोड़ता जब तक कि परिणाम assert या उपयोग न किया जाए। HIGH जब कॉल एक बिना शर्त छोड़ा गया स्टेटमेंट है; MEDIUM जब एक लोकल को असाइन किया जाता है जिसे फिर कभी संदर्भित नहीं किया जाता। |
| `O1JS_UNCONSTRAINED_SENDER` | high / medium | `this.sender.getUnconstrained()` tx sender को बिना सिद्ध किए लौटाता है। HIGH जब वह मान (या उससे एक लोकल) एक assert / स्टेट `.set` / `send` (vacuous check) में प्रवाहित होता है; अन्यथा MEDIUM। `this.sender.getAndRequireSignature()` को प्राथमिकता दें, या विस्तारित मुहावरा `AccountUpdate.createSigned(sender)`। **शांत रहता है जब** (1) वही `@method` कहीं भी `this.sender.getAndRequireSignature()` को भी कॉल करता है (हस्ताक्षर आवश्यकता method-scoped है), या (2) witnessed sender मान `AccountUpdate.createSigned(...)` / उसी key पर एक `AccountUpdate.create(...).requireSignature()` का आर्ग्युमेंट है (आर्ग्युमेंट पहचान आवश्यक — एक अलग key पर `createSigned` suppress नहीं करता)। |
| `MissingRangeCheck` | high | एक कच्चा `Field` (range-checked `UInt64`/`UInt32` नहीं) एक हस्तांतरण राशि के रूप में उपयोग किया जाता है। एक `Field` p mod का एक तत्व है और range-bounded नहीं है। |
| `O1JS_WEAK_PERMISSIONS` | high / medium | `editState` / `send` को `proofOrSignature()` या `none()` पर सेट करना, जिससे zkApp अकाउंट key हस्ताक्षर करके सर्किट को बायपास कर सके। `setVerificationKey` / `setPermissions` को `signature` / `proofOrSignature` / `none` पर छोड़े जाने को भी फ्लैग करता है (Mina के प्रलेखित अपग्रेड ट्रेनिंग व्हील्स); HIGH जब उसी `permissions.set` में एक कमजोर `editState`/`send` के साथ संयुक्त हो। |
| `O1JS_LOGIC_OUTSIDE_PROOF` | high | `Provable.asProver(...)` या एक `Provable.witness*` कॉलबैक के अंदर सुरक्षा लॉजिक (assert / approve / send / स्टेट `.set`)। वे कॉलबैक सर्किट के *बाहर* चलते हैं — एक दुर्भावनापूर्ण प्रूवर उन्हें हटा सकता है और फिर भी एक सत्यापनकर्ता proof उत्पन्न कर सकता है। |
| `O1JS_APPROVE_WITHOUT_BINDING` | medium | एक `@method` `approve` / `approveAccountUpdate` / `approveBase` को कॉल करता है बिना `balanceChange` / `publicKey` पढ़े और बिना `assertCanMint` / `assertCanBurn` / एक `forEachUpdate` संरक्षण जाँच के — Mina FlawedTokenContract आर्किटाइप। |
| `O1JS_VACUOUS_ASSERT` | high / medium | एक assert जो निर्माण द्वारा संतुष्ट है: `x.assertEquals(x)`, `x.equals(x).assertTrue()`, या `Bool(true).assertTrue()`। स्व-तुलना के लिए HIGH (लगभग हमेशा एक टाइपो); स्थिर Bool asserts के लिए MEDIUM। |
| `O1JS_CONDITIONAL_ASSERT` | medium | `if <flag> { ... }` के अंदर एक assert जहाँ `<flag>` एक प्रूवर-नियंत्रित `@method` `Bool` (या `.toBoolean()` से एक लोकल) है। एक JS conditional सर्किट को उस तरह constrain नहीं करता जैसे `Provable.if` करता है। सटीकता के लिए इनलाइन तुलनाएँ अरिपोर्ट रहती हैं। |
| `O1JS_GUARDED_INVERSE` | medium | एक `Provable.if` शाखा के अंदर एक `.div()` / `.inv()` / `.sqrt()`, जो उसी मान पर एक शर्त द्वारा गार्ड किया गया है जिस पर यह विफल होता है। दोनों शाखाओं का मूल्यांकन in-circuit किया जाता है और ये कॉल बिना शर्त assert करते हैं कि व्युत्क्रम या मूल मौजूद है, इसलिए गार्ड assertion को छोड़ नहीं पाता — सर्किट ठीक उसी इनपुट के लिए असंतोषजनक है जिसे संभालने के लिए गार्ड लिखा गया था, और method उसके लिए कभी सिद्ध नहीं किया जा सकता। Veridise द्वारा `V-O1J-VUL-060` के रूप में रिपोर्ट किया गया। पहले एक सुरक्षित भाजक की गणना करें (`Provable.if(isZero, Field(1), d)`) और बाद में परिणाम चुनें। **शांत रहता है जब** गार्ड भाजक के बारे में कुछ नहीं कहता, इसलिए एक सुरक्षित विभाजन के आसपास एक असंबंधित `Provable.if` फ्लैग नहीं किया जाता। |
| `O1JS_PRECONDITION_OVERWRITTEN` | medium | एक method में **एक ही** प्रॉपर्टी पर दो या अधिक `requireEquals` / `requireBetween` / `requireNothing` कॉल, भिन्न आर्ग्युमेंट्स के साथ। पूर्वशर्तें AccountUpdate पर *सेट* की जाती हैं बजाय संचित होने के, इसलिए प्रत्येक कॉल पिछली को ओवरराइट कर देती है और केवल अंतिम लागू होती है — in-circuit assertions के विपरीत, जो compose होती हैं। `a.requireEquals(b)` फिर `a.requireEquals(c)` का अर्थ है `a === c`, न कि `a === b`। Veridise द्वारा `V-O1J-VUL-012` के रूप में रिपोर्ट किया गया। **शांत रहता है जब** आर्ग्युमेंट्स समान हों (idempotent, कुछ भी नहीं खोया), `getAndRequireEquals()` पर (एक अलग method, इसलिए बार-बार स्टेट रीड ठीक है), और जब कॉल परस्पर अनन्य JS शाखाओं में हों, जो सर्किट-बिल्ड समय पर हल होती हैं। वह अंतिम छूट एक वास्तविक overwrite को छिपा सकती है जो एक असंबंधित `if`/`else` को पार करती है। |
| `O1JS_STATE_READ_AFTER_WRITE` | medium | एक `@state` फ़ील्ड को उसी method में उसी फ़ील्ड पर एक `set(...)` पूरा होने के बाद पढ़ा जाता है (`get()` / `getAndRequireEquals()`)। `set()` AccountUpdate पर परिवर्तन रिकॉर्ड करता है लेकिन `get()` तक write-through नहीं करता, इसलिए रीड अभी भी लेखन से पहले का मान देखता है और उस पर बनाई गई कोई भी अंकगणित चुपचाप उस लेखन से बंद हो जाती है। Veridise द्वारा `V-O1J-VUL-030` के रूप में रिपोर्ट किया गया। स्टेट को वापस पढ़ने के बजाय नए मान को एक लोकल में रखें। **शांत रहता है जब** रीड लेखन के अपने आर्ग्युमेंट्स के अंदर नेस्टेड हो (read-modify-write मुहावरा `this.x.set(this.x.getAndRequireEquals().add(1))`, जो सही है), और जब लेखन और रीड परस्पर अनन्य JS शाखाओं में हों। एक ही method तक सीमित — क्रॉस-method कैशिंग मामला जिसका Veridise भी वर्णन करता है, उसे call-graph ज्ञान की आवश्यकता है जो इस नियम के पास नहीं है। |
<!-- END GENERATED O1JS RULE TABLE -->
### False-positive गार्ड (o1js)
विश्लेषक सही कोड पर शांत रहने के लिए डिज़ाइन किया गया है:
- **Signature-gated methods छोड़ दिए जाते हैं।** एक `@method` जो
`this.requireSignature()` (या `getAndRequireSignature`, `AccountUpdate.createSigned`,
`Signature.verify`) को कॉल करता है, owner/admin-gated है — इसके आर्ग्युमेंट्स key
धारक द्वारा चुने जाते हैं, किसी मनमाने प्रूवर द्वारा नहीं — इसलिए इसके witnesses फ्लैग नहीं किए जाते। यह
`onlyOwner` का o1js समकक्ष है।
- **State-bound witnesses छोड़ दिए जाते हैं।** एक आर्ग्युमेंट जो `getAndRequireEquals()`-व्युत्पन्न
मान के बराबर (या एक क्रम तुलना द्वारा उससे बाउंड) assert किया गया है, वह सुदृढ़ है और रिपोर्ट नहीं किया जाएगा। यह दोनों प्रत्यक्ष रूप को कवर करता है —
`amount.assertLessThanOrEqual(bal)` — और श्रृंखलाबद्ध रूप
`amount.lessThanOrEqual(bal).assertTrue()`। बाइंडिंग जो एक
undecorated same-class helper (`this.verifyX(arg)`) में रहती है, उसे भी मान्यता दी जाती है,
ऐसे helpers की एक श्रृंखला के माध्यम से भी।
- **Verified proofs छोड़ दिए जाते हैं।** एक `Proof` / `SelfProof` / `DynamicProof` /
`*Proof`-टाइप्ड आर्ग्युमेंट जिस पर `.verify()` कॉल किया जाता है, सत्यापित सर्किट द्वारा constrained है — उस पर (और उसके `publicOutput` /
`publicInput` पर) witness findings दबा दिए जाते हैं। एक `.verifyIf(flag)` को केवल तब श्रेय दिया जाता है जब
शर्त एक unconstrained method आर्ग्युमेंट न हो, या स्वयं assert की गई हो। यही बात कैनोनिकल OffchainState रैपर
`this.offchainState.settle(proof)` पर भी लागू होती है (फ्रेमवर्क `settle` के अंदर सत्यापित करता है)।
एक हाथ से बनाए गए `.settle(proof)` को
सत्यापित माना **नहीं** जाता। विपरीत मामला (proof-typed arg कभी सत्यापित नहीं और OffchainState-settled नहीं) `O1JS_UNVERIFIED_PROOF` के रूप में रिपोर्ट किया जाता है।
- **Asserted / उपयोग किए गए Bools छोड़ दिए जाते हैं।** एक predicate जो
`.assertTrue()` / `.assertFalse()` के साथ श्रृंखलाबद्ध है, `Provable.if(...)` में नेस्टेड है, या
एक लोकल को असाइन किया गया है जिसे बाद में संदर्भित किया जाता है, उसे
`O1JS_UNASSERTED_BOOL` के रूप में रिपोर्ट नहीं किया जाता।
- **Authenticated senders छोड़ दिए जाते हैं।** `this.sender.getUnconstrained()`
तब फायर नहीं होता जब वही `@method` `this.sender.getAndRequireSignature()` को भी कॉल करता है, या जब वह witnessed मान
`AccountUpdate.createSigned(...)` को पास किया जाता है / उससे बने AccountUpdate पर
`.requireSignature()` के माध्यम से authenticated किया जाता है (आर्ग्युमेंट
पहचान आवश्यक)।
- विश्लेषण से पहले टिप्पणियाँ और स्ट्रिंग लिटरल हटा दिए जाते हैं, इसलिए एक स्ट्रिंग के अंदर एक `assert`
गलत परिणाम नहीं बना सकता।
## यह क्या पहचानता है (Noir)
वही soundness विचार — under-constrained witnesses — [Noir](https://noir-lang.org) (`.nr`) सर्किट पर लागू होता है। स्कैनर को `.nr`
फ़ाइलों पर इंगित करें (या `--lang noir` का उपयोग करें) और यह उन्हें Noir नियम सेट के साथ विश्लेषण करता है।
वही lexical, dependency-free दृष्टिकोण। aztec-nr oracle /
`unsafe` मुहावरों के विरुद्ध कैलिब्रेट किया गया — देखें [`docs/noir_calibration.md`](https://github.com/auditinfra-io/o1js-scan/blob/main/docs/noir_calibration.md)।
<!-- BEGIN GENERATED NOIR RULE TABLE -->
| नियम | गंभीरता | इसका क्या अर्थ है |
|------|----------|---------------|
| `NOIR_UNCONSTRAINED_WITNESS` | high | एक `unsafe { ... }` ब्लॉक से बाइंड किया गया मान — एक `unconstrained fn` (oracle / Brillig hint) का परिणाम — जिसे कभी `assert` / `assert_eq` (या एक पुष्टि करने वाले helper / merkle check) द्वारा पुनः-constrained नहीं किया जाता। hint सर्किट के **बाहर** चलता है। `O1JS_UNCONSTRAINED_PROVABLE_WITNESS` का अनुरूप। |
| `NOIR_UNCONSTRAINED_INPUT` | medium | `fn main` का एक निजी (witness) इनपुट जो **किसी** `assert` / `assert_eq` में प्रवाहित नहीं होता और सार्वजनिक आउटपुट का **हिस्सा नहीं** है। `O1JS_UNCONSTRAINED_WITNESS` का अनुरूप। |
| `NOIR_UNCONSTRAINED_PUBLIC_INPUT` | medium | `fn main` का एक **सार्वजनिक** इनपुट जो किसी constraint और किसी आउटपुट तक नहीं पहुँचता — सर्किट इसे कभी नहीं पढ़ता। निजी-witness नियम का *द्वैत*: सत्यापनकर्ता मान की आपूर्ति करता है और मानता है कि कथन उसके बारे में है, जबकि सर्किट इसे अनदेखा करता है (उदा. एक `merkle_root: pub Field` जो कभी जाँचा नहीं जाता, इसलिए सदस्यता वास्तव में कभी सिद्ध नहीं हुई)। MEDIUM क्योंकि जानबूझकर अप्रयुक्त सार्वजनिक इनपुट एक proof को एक संदर्भ (nonce / chain id / recipient) से बाइंड करने का एक वैध मुहावरा भी है, जो lexically अप्रभेद्य है — इसलिए यह डिफ़ॉल्ट `--fail-on high` पर CI को गेट नहीं करता। |
| `NOIR_UNCHECKED_CAST` | medium | एक प्रूवर-नियंत्रित मान जिसे एक संकीर्ण unsigned प्रकार (`as u8`/`u16`/`u32`) में cast किया गया है **बिना** किसी range assertion के। o1js `MissingRangeCheck` का अनुरूप। |
| `NOIR_UNCONSTRAINED_ARRAY_INDEX` | medium | एक प्रूवर-नियंत्रित मान जिसे एक array index (`arr[i]`) के रूप में उपयोग किया गया है **बिना** उस पर किसी भी प्रकार की जाँच के। Noir की अंतर्निहित bounds check केवल यह स्थापित करती है कि index *रेंज में* है — यह नहीं कि यह *सही* index है — इसलिए प्रूवर किसी भी तत्व का चयन करने के लिए स्वतंत्र रहता है और फिर भी एक सत्यापनकर्ता proof उत्पन्न कर सकता है। यह Merkle path positions, note selection और allow-list membership के पीछे selector-freedom बग है। Suppress किया जाता है जब index range-bounded हो, एक समानता द्वारा pinned हो, एक cast से पहले bounded हो (`index.assert_max_bit_size::<8>(); let i = index as u32;`), या जब वापस पढ़ा गया मान स्वयं एक `assert_eq` द्वारा pinned हो। |
| `NOIR_UNASSERTED_BOOL` | high / medium | एक तुलना जिसका `bool` परिणाम **छोड़ दिया गया** है। o1js `O1JS_UNASSERTED_BOOL` का अनुरूप। |
| `NOIR_CONDITIONAL_ASSERT` | medium | `if <flag> { ... }` के अंदर एक `assert` जहाँ `<flag>` एक प्रूवर-नियंत्रित बिना शर्त `bool` या प्रूवर-नियंत्रित मानों से व्युत्पन्न एक लोकल है। एक conditional के अंदर एक constraint केवल तब लागू होता है जब शर्त सत्य हो, इसलिए एक प्रूवर-चयनित शाखा जाँच को छोड़ सकती है। सटीकता के लिए इनलाइन तुलनाएँ (`if x != 0`) अकेली छोड़ दी जाती हैं; गार्ड को एक लोकल को असाइन करना (`let gate = x != 0; if gate`) रिपोर्ट किया जाता है जब तक कि `gate` स्वयं assert न किया गया हो। |
| `NOIR_CONDITIONAL_CONSTRAIN` | medium | एक `constrain_*` / `confirm_*` / `verify_*` कॉल केवल एक प्रूवर-नियंत्रित `if` के अंतर्गत, जबकि एक `unsafe` hint अभी भी आउटपुट तक पहुँचता है। |
| `NOIR_UNUSED_CHECK_RESULT` | high / medium | एक `check_*` / `confirm_*` / `verify_*` / `constrain_*` परिणाम छोड़ दिया जाता है (बिना शर्त कॉल) या असाइन किया जाता है और कभी assert नहीं किया जाता — जाँच सर्किट को बाइंड नहीं करती। |
| `NOIR_VACUOUS_CONSTRAINT` | high / medium | एक constraint जो निर्माण द्वारा संतुष्ट है: एक स्व-तुलना (`assert(x == x)`, `assert_eq(x, x)`, `x >= x`) या एक स्थिर शर्त (`assert(true)`)。 यह कोई प्रतिबंध नहीं जोड़ता, लेकिन पंक्ति एक जाँच के रूप में *पढ़ी* जाती है — जो इसे एक अनुपस्थित constraint से अधिक खतरनाक बनाती है, क्योंकि समीक्षा वहीं रुक जाती है। स्व-तुलना के लिए HIGH (लगभग हमेशा एक वास्तविक जाँच के लिए एक टाइपो: `assert(computed == expected)` को `assert(expected == expected)` के रूप में गलत टाइप किया गया); एक स्थिरांक के लिए MEDIUM, जो अधिक बार एक प्लेसहोल्डर होता है। `x != x` फ्लैग **नहीं** किया जाता — वह असंतोषजनक है, एक liveness बग बजाय एक मौन soundness छेद के। |
| `NOIR_UNSAFE_MISSING_SAFETY` | low | एक `unsafe { ... }` ब्लॉक जिसमें कोई निकटवर्ती `// Safety:` टिप्पणी नहीं है। सूचनात्मक; डिफ़ॉल्ट `--fail-on high` पर CI विफल नहीं करता। |
<!-- END GENERATED NOIR RULE TABLE -->
### False-positive गार्ड (Noir)
- **Assert / let-hop / same-file confirm helpers** `unsafe` hints को बाइंड करते हैं।
- **Call-site नाम** `constrain_*` / `confirm_*` / `verify_*` /
`check_(non_)membership*` / `public_data_storage_read` args को श्रेय देते हैं (छोड़ी गई जाँचों के लिए unused-result detection के साथ)।
- **प्रलेखित जानबूझकर unconstrained** (निकटवर्ती `// Safety:` आवश्यक):
`random()`, `avm::…`, और kernel/rollup/discovery आस्थगित शब्दावली।
- **Tuple `let` + asserted flags** membership जाँचों में पास किए गए merkle witnesses को बाइंड करते हैं।```console
$ noir-scan examples/noir_unconstrained.nr --include-examples
HIGH NOIR_UNCONSTRAINED_WITNESS noir_unconstrained.nr:16 fn=main Unconstrained `unsafe` result `inv` in `main`
LOW NOIR_UNSAFE_MISSING_SAFETY noir_unconstrained.nr:16 fn= `unsafe` block without a `// Safety:` comment
noir-scan: 2 finding(s) [1 high, 1 low] in 1 of 1 file(s) — fails (--fail-on high)
$ noir-scan examples/noir_constrained.nr --include-examples
noir-scan: no findings in 1 o1js or Noir file(s) — passes (--fail-on high)
जैसा कि ऊपर o1js उदाहरण के साथ है, --include-examples केवल इसलिए आवश्यक है क्योंकि
ये डेमो फ़ाइलें examples/ के अंतर्गत रहती हैं।
विश्लेषक एक निर्भरता-मुक्त लेक्सिकल फ्रंटएंड और एक हल्की सिमेंटिक परत है जो समान-क्लास हेल्पर्स के माध्यम से एलियास ट्रैकिंग और इंटरप्रोसीजरल प्रोपेगेशन करता है। यह TypeScript कंपाइलर फ्रंटएंड, टाइप चेकर, या होल-प्रोग्राम डेटाफ्लो इंजन नहीं है, और इस स्कैनर में कोई SMT या औपचारिक-प्रमाण परत नहीं है। ट्राइएज करते समय इन ब्लाइंड स्पॉट्स को ध्यान में रखें — ये इस निर्भरता-मुक्त डिज़ाइन के लिए ज्ञात और जानबूझकर हैं, बग नहीं:
केवल सरल एलियासेस का अनुसरण किया जाता है। विटनेस ट्रैकिंग सादे
समान-मेथड एलियासेस जैसे const q = qty का अनुसरण करती है, लेकिन व्युत्पन्न एक्सप्रेशन या
डीस्ट्रक्चरिंग का नहीं: ```ts
const q = qty; this.send({ to: dest, amount: q }); // followed
const q = qty.add(1); this.send({ to: dest, amount: q }); // not followed
const slot = this.root; slot.get(); // missing precondition missed
क्रॉस-मेथड बाइंडिंग केवल समान-क्लास हेल्पर चेन को कवर करती है। एक
अनडेकोरेटेड समान-क्लास हेल्पर जिसे this.verifyX(arg) के रूप में कॉल किया जाता है, कॉलर के आर्ग्युमेंट को स्टेट-बाइंड कर सकता है, और 0.19.0 से उनकी चेन
(@method → हेल्पर A → हेल्पर B) को एक निश्चित बिंदु तक फॉलो किया जाता है। हेल्पर→हेल्पर चरण केवल एक बेयर पैरामीटर रेफरेंस को मैप करता है, इसलिए
helperA(x.add(1)) प्रोपेगेट नहीं होता। फ्री और इम्पोर्टेड फंक्शन अभी भी फॉलो नहीं किए जाते, और हेल्पर आर्ग्युमेंट का लोकल-वेरिएबल एलियासिंग एक डॉक्युमेंटेड लिमिटेशन बना हुआ है।
Unasserted-Bool डिटेक्शन स्टेटमेंट-शेप्ड है। Tier A केवल उन बेयर
एक्सप्रेशन स्टेटमेंट्स को फ्लैग करता है जिनका सबसे बाहरी कॉल एक Bool प्रेडिकेट है और उसके बाद कुछ भी चेन नहीं है। Provable.if(...) के अंदर नेस्टेड प्रेडिकेट्स, या असाइन किए गए और बाद में उपयोग किए गए, फ्लैग नहीं होते। एक Bool लोकल के जटिल कंट्रोल-फ्लो उपयोग अभी भी छूट सकते हैं यदि नाम का कभी संदर्भ न लिया जाए (फेल्योर मोड: मिस, न कि फॉल्स पॉज़िटिव)।
सिग्नेचर-गेटिंग मेथड-लेवल और सबस्ट्रिंग-आधारित है।
_method_is_signature_gated एक पूरे @method को ओनर-गेटेड मानता है यदि उसमें कोई सिग्नेचर आइडियम हो, और यह किसी वेरिफायर को केवल तब पहचानता है जब रिसीवर नाम में शाब्दिक रूप से हो — इसलिए को गेटिंग के रूप में पहचाना जाता, जबकि एक बड़े मेथड में कहीं और एक असंबंधित सिग्नेचर चेक ओवर-सप्रेस कर सकता है। यह प्रति मेथड ऑल-ऑर-नथिंग है।
येी कारण हैं कि फाइंडिंग्स मानव समीक्षा के लिए एक शुरुआती बिंदु हैं, प्रमाण नहीं। एक डेटाफ्लो-अवेयर रीराइट जानबूझकर लेक्सिकल एनालाइज़र के दायरे से बाहर है।
o1js-scan जानबूझकर एक उथला, सिंगल-फाइल लेक्सिकल पास है — कोई पार्सर नहीं, कोई डेटाफ्लो नहीं, कोई सॉल्वर नहीं। यही इसे डिपेंडेंसी-फ्री और CI में तत्काल बनाता है, और यही एक कठोर सीमा भी है। ऊपर दी गई सीमाएँ कोई बैकलॉग नहीं हैं; वे डिज़ाइन के परिणाम हैं।
इसलिए यह स्पष्ट रूप से बताना उचित है कि यह टूल आपको क्या बता सकता है और क्या नहीं:
यह ट्रेड-ऑफ उस लिंटर के लिए सही है जिसे आप हर कमिट पर चलाते हैं। यदि आप किसी ऐसी चीज़ पर काम कर रहे हैं जहाँ अंतर मायने रखता है — वास्तविक मूल्य रखने वाला एक प्रोटोकॉल, एक सर्किट जिसे आप गलत नहीं कर सकते — तो इसे पहला पास मानें और एक वास्तविक समीक्षा के लिए बजट रखें।
गहन विश्लेषण के लिए, अलग पूर्ण स्कैनर
audit-engine-cli रिपॉज़िटरी में मेंटेन किया जाता है।
o1js-scan जानबूझकर हल्का, ओपन स्कैनर है; पूर्ण स्कैनर का प्रोप्राइटरी डिटेक्शन नॉलेज और इम्प्लीमेंटेशन डिटेल्स यहाँ पुनरुत्पादित नहीं किए गए हैं। एक्सेस या अधिक पूर्ण सर्किट समीक्षा के लिए, संपर्क करें:
[email protected]।
इंस्टॉल किया गया CLI फाइलों का विश्लेषण लोकली करता है। इसमें कोई टेलीमेट्री, नेटवर्क
क्लाइंट, अकाउंट, या अपलोड स्टेप नहीं है, और इसके Python रनटाइम में कोई थर्ड-पार्टी
डिपेंडेंसी नहीं है। o1js-scan path/to/private-repo चलाने से सोर्स
या फाइंडिंग्स कहीं नहीं भेजे जाते।
कंपाइलर लॉग्स की तरह, स्कैनर आउटपुट में पाथ, आइडेंटिफायर, और सोर्स फ्रैगमेंट हो सकते हैं। SARIF भी सटीक रिपॉज़िटरी लोकेशन की पहचान करता है, और GitHub Action इसे GitHub कोड स्कैनिंग पर अपलोड करता है। वही रिपॉज़िटरी और CI एक्सेस कंट्रोल उपयोग करें जो आप पहले से स्कैन किए जा रहे सोर्स के लिए उपयोग करते हैं।
क्या आप किसी एप्लिकेशन को साझा किए बिना एक उपयोगी फॉल्स-पॉज़िटिव या मिस्ड-डिटेक्शन रिपोर्ट योगदान करना चाहते हैं? आविष्कृत नामों और कॉन्स्टेंट्स के साथ सिंटैक्स को रीप्रोड्यूस करें, बिज़नेस लॉजिक को एक-एक स्टेटमेंट करके हटाएँ, और पोस्ट करने से पहले सत्यापित करें कि सिंथेटिक स्निपेट अभी भी वही नियम ट्रिगर करता है। प्राइवेसी-सेफ योगदान गाइड में एक ठोस चेकलिस्ट और प्राइवेट सर्किट का खुलासा किए बिना o1js समुदाय की मदद करने के कई तरीके हैं।
यह सीमा ओपन स्कैनर को बेहतर होने से नहीं रोकती। सार्वजनिक o1js डॉक्युमेंटेशन और रिपॉज़िटरी नए नियमों और कम्पैटिबिलिटी फिक्स्चर का समर्थन कर सकती हैं; सिंथेटिक उदाहरण फॉल्स पॉज़िटिव और छूटी हुई कंस्ट्रेंट्स का परीक्षण कर सकते हैं; और पार्सर रेज़िलिएंस, डायग्नोस्टिक्स, SARIF, परफॉर्मेंस, पैकेजिंग, और कैलिब्रेशन सभी एक प्राइवेट ऑडिट तकनीक या क्लाइंट कोड प्रकाशित किए बिना सुधर सकते हैं। ओपन स्कैनर को स्वतंत्र रूप से व्याख्या योग्य दावे करने चाहिए; प्राइवेट रिसर्च अलग ऑडिट इंजन में रह सकती है।
क्वांटम जोखिम सर्किट सुरक्षा से संबंधित है, लेकिन यह एक मिसिंग-कंस्ट्रेंट
नियम नहीं है। o1js-scan यह निर्धारित नहीं करता कि कोई सिग्नेचर, हैश, कमिटमेंट,
Kimchi प्रूफ सिस्टम, या Mina स्वयं पोस्ट-क्वांटम सुरक्षा लक्ष्य को पूरा करता है या नहीं। उन
उत्तरों का निर्भर करता है ठोस प्रिमिटिव और पैरामीटर्स, प्लेटफॉर्म अनुमानों,
डिप्लॉयमेंट के आवश्यक लाइफटाइम, और इसकी माइग्रेशन योजना पर—न कि केवल एक
TypeScript आइडेंटिफायर पर जिसे एक लेक्सिकल स्कैनर देख सकता है।
O(1) Labs के Qubit or Not Qubit से प्रेरित, पोस्ट-क्वांटम समीक्षा गाइड उस सीमा को एक o1js-विशिष्ट इन्वेंटरी और क्रिप्टो-एजिलिटी चेकलिस्ट में बदल देती है। इसे इस स्कैनर के साथ उपयोग करें, बजाय एक क्लीन स्कैन को पोस्ट-क्वांटम मूल्यांकन के रूप में व्याख्यायित करने के।
o1js 1.x, 2.x और 3.x पर काम करता है, जिसमें Mesa हार्ड फोर्क भी शामिल है जिसे o1js
3.0.0 लक्षित करता है। o1js-scan TypeScript सोर्स का टेक्स्ट के रूप में विश्लेषण करता है और इसकी o1js पर कोई रनटाइम
डिपेंडेंसी नहीं है — कुछ भी वर्ज़न-पिन्ड नहीं है। यह आधुनिक
require* प्रीकंडीशन API (getAndRequireEquals, requireEquals,
requireSignature, getAndRequireSignature),
@method / @method() / @method.returns(...) डेकोरेटर्स, एनोटेटेड @state
फील्ड्स, this.send({...}), लो-लेवल AccountUpdate.balance.subInPlace(...)
ट्रांसफर्स, और Permissions.* पर की-आधारित है। स्थापित फॉर्म
1.x → 2.x → 3.x सीमाओं के पार संगत बने हुए हैं, जबकि स्कैनर नए
डॉक्युमेंटेड डेकोरेटर और लो-लेवल ट्रांसफर वेरिएंट्स को भी स्वीकार करता है।
2.x ओनर-ऑथ आइडियम को
सिग्नेचर-गेटिंग के रूप में पहचाना जाता है। (लेगेसी प्रीकंडीशन्स अभी भी स्वीकार किए जाते हैं,
इसलिए पुराना कोड भी नहीं टूटता।)
Mesa के ब्रेकिंग चेंजेज़ सभी रनटाइम- और प्रोटोकॉल-लेवल हैं — Transaction.setFeePerSnarkCost() और TransactionCost.* कॉन्स्टेंट्स का हटाया जाना, नया VerificationKey.toJSON() शेप, रीजनरेटेड वेरिफिकेशन कीज़,
MAX_ZKAPP_STATE_FIELDS का 8 से 32 तक बढ़ाया जाना, और mina-signer v4
ट्रांज़ैक्शन फॉर्मेट। इनमें से कोई भी उस API का नाम नहीं बदलता जिस पर यह स्कैनर मैच करता है, इसलिए Mesa के लिए कोई नियम नहीं बदला, और यह दावा करने के बजाय सत्यापित किया गया है।
scripts/o1js_release_matrix.sh दो पिन्ड o1js रिलीज़ को स्कैन करता है जो
प्रोटोकॉल सीमा के दोनों ओर हैं — 2.15.0 (9620ef08, अंतिम 2.x रिलीज़) और
3.0.0 (cc18a919, Mesa) — और हर फाइंडिंग की तुलना
tests/fixtures/o1js_release_matrix.json से करता है:
33 फाइंडिंग्स सीमा के पार समान हैं, कोई भी नहीं खोया, और सभी तीन
नई फाइंडिंग्स src/examples/zkapps/big-state-zkapp.ts में हैं — 32-स्टेट-फील्ड
उदाहरण जो केवल इसलिए मौजूद है क्योंकि Mesa ने MAX_ZKAPP_STATE_FIELDS बढ़ाया। वह
डेल्टा एक टेस्ट द्वारा पिन्ड है, इसलिए यह चुपचाप ड्रिफ्ट नहीं कर सकता। मैट्रिक्स हर
CI बिल्ड पर चलता है; साप्ताहिक o1js-upstream-canary जॉब अतिरिक्त रूप से o1js को
HEAD पर ट्रैक करता है, किसी भी रिलीज़ से आगे।
समतुल्य कंस्ट्रेंट स्पेलिंग्स को विश्लेषण के लिए नॉर्मलाइज़ किया जाता है: इंस्टेंस
assertEquals(...), स्टैटिक Provable.assertEqual(Type, ...), और
equals(...).assertTrue() इक्वलिटी चेन्स सभी समान ऑपरेंड्स को बाइंड करते हैं। मेथड
एक्सट्रैक्शन लंबाई-संरक्षण करने वाले कमेंट और स्ट्रिंग
मास्किंग के बाद ब्रेस-बैलेंस्ड है, और मल्टीलाइन डेकोरेटर्स, नेस्टेड कॉलबैक-शेप्ड पैरामीटर
टाइप्स, TypeScript एक्सेस मॉडिफायर्स, और मल्टीलाइन आइडेंटिटी एलियासेस (जिसमें
पैरेंथेसाइज़्ड और as Type फॉर्म्स भी शामिल हैं) को स्वीकार करता है।
Noir विश्लेषण Aztec / nargo प्रोजेक्ट्स (.nr) द्वारा उपयोग किए जाने वाले Noir सिंटैक्स को लक्षित करता है; यह
nargo को इनवोक या सर्किट्स को कंपाइल नहीं करता।
यह एक लेक्सिकल एनालाइज़र है, पूर्ण TypeScript या Noir पार्सर नहीं — o1js और Noir सोर्सेस ब्रेस-डेलिमिटेड और रेगेक्स-ट्रैक्टेबल हैं, और आउटपुट का उद्देश्य एक मानव द्वारा ट्राइएज किया जाना है। यही इसे डिपेंडेंसी-फ्री और CI में चलाने के लिए तत्काल बनाए रखता है। फाइंडिंग्स समीक्षा के लिए एक शुरुआती बिंदु हैं, प्रमाण नहीं।
योगदान का स्वागत है — नए नियम परिवार, अधिक FP गार्ड्स, और वास्तविक-दुनिया के
कैलिब्रेशन आर्कीटाइप्स सभी मूल्यवान हैं। देखें CONTRIBUTING.md।
Community Packages लिस्टिंग से o1js रिपॉज़िटरी में एक एडवाइज़री चेक तक के प्रस्तावित पथ के लिए, भेजने-के-लिए-तैयार o1js अपस्ट्रीम इंटीग्रेशन प्रस्ताव देखें।
टेस्ट और लिंटर इसके साथ चलाएँ:```bash pip install -e ".[dev]" pytest # unit tests + Noir/o1js corpus ruff check . # lint npm run format:check # prettier, npm wrapper only
## लाइसेंस
Apache-2.0. देखें [`LICENSE`](https://github.com/auditinfra-io/o1js-scan/blob/main/LICENSE)।
#[test(...)]mod test { … }mod tests { … }signaturesig.verify(admin, msg)सेंडर ऑथेंटिकेशन नाम-आधारित और केवल समान-मेथड है।
O1JS_UNCONSTRAINED_SENDER तब सप्रेस करता है जब this.sender.getAndRequireSignature()
या AccountUpdate.createSigned(<that sender>) उसी @method बॉडी में दिखाई दे। एक सिग्नेचर आवश्यकता जो केवल एक हेल्पर में रहती है
(this.requireSenderSig() → अंदर getAndRequireSignature) उसे फॉलो नहीं किया जाता — फेल्योर मोड सही कोड पर एक फॉल्स पॉज़िटिव है जो इस आइडियम को रैप करता है, न कि एक छूटी हुई असली बग।
Noir क्रॉस-क्रेट हेल्पर्स को केवल नाम कन्वेंशन से पहचाना जाता है (कोई
Nargo.toml / इम्पोर्ट रिज़ॉल्यूशन नहीं)। फॉल्स पॉज़िटिव की तुलना में मिस को प्राथमिकता दें।
this.sender.getAndRequireSignature()assertEquals| Release | Findings | HIGH | MEDIUM | LOW | Files |
|---|
| o1js 2.15.0 | 36 | 8 | 26 | 2 | 18 |
| o1js 3.0.0 (Mesa) | 39 | 8 | 29 | 2 | 19 |