@elytrasec/engine 0.4.2 → 0.4.3

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.
Files changed (2) hide show
  1. package/dist/index.js +23 -9
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3477,8 +3477,11 @@ var solidityRules2 = [
3477
3477
  title: "Chainlink latestRoundData without staleness check",
3478
3478
  description: "Using latestRoundData() without checking updatedAt for staleness can return outdated prices.",
3479
3479
  suggestion: "Check that `updatedAt` is recent: `require(block.timestamp - updatedAt < STALENESS_THRESHOLD)`.",
3480
- pattern: /latestRoundData\s*\(/,
3481
- multilinePattern: /latestRoundData\s*\([\s\S]{0,500}(?!updatedAt)/g,
3480
+ // Removed single-line pattern: it fired on EVERY interface declaration of latestRoundData().
3481
+ // Only the multilinePattern below should fire — it scopes to actual usage with no staleness check.
3482
+ // Match real usage (destructured assignment), suppress when staleness/sequencer/heartbeat
3483
+ // checks are visible in the next ~1500 chars. Skip pure interface declarations.
3484
+ multilinePattern: /\(\s*[^)]*\)\s*=\s*[^;]*\.latestRoundData\s*\(\s*\)(?![\s\S]{0,1500}?(?:updatedAt|staleness|MAX_DELAY|STALENESS|grace|GRACE|timestamp\s*-\s*updatedAt|block\.timestamp\s*-\s*\w+|sequencer|isSequencerUp|MAX_HEARTBEAT|HEARTBEAT|maxAge))/,
3482
3485
  severity: "high",
3483
3486
  category: "security",
3484
3487
  confidence: "medium",
@@ -4666,7 +4669,11 @@ var eip7702Rules = [
4666
4669
  // initializer modifier, ERC1967/UUPS proxy patterns, or 'once' guard.
4667
4670
  // Broader suppression: ERC-4337/7579 accounts use _setOwner pattern, OZ uses initializer modifier,
4668
4671
  // proxies use ERC1967, V4 PoolManager.initialize is permissionless-by-design with noDelegateCall.
4669
- multilinePattern: /function\s+initialize\s*\([^)]*\)\s*(?:external|public)[^{;]*\{(?![\s\S]{0,800}?(?:initializer|reinitializer|_disableInitializers|require\s*\([^)]*!\s*initialized|require\s*\([^)]*owner\s*==\s*address\s*\(\s*0|noDelegateCall|onlyOwner|onlyEntryPoint|onlyProxy|ERC1967|UUPS|_setOwner|_initialize|EntryPoint|UserOperation|getStorage|StorageSlot))/,
4672
+ // Suppress when function declaration line itself contains a guard modifier
4673
+ // (initializer, onlyOwner, onlyProxy, noDelegateCall, payable initializer, etc.) — these
4674
+ // come BEFORE the opening { so the prior lookahead-after-brace missed them.
4675
+ // Then suppress when proxy/4337/UUPS/admin patterns appear in body.
4676
+ multilinePattern: /function\s+initialize\s*\([^)]*\)\s*(?:external|public)\s+(?!.*(?:initializer|reinitializer|onlyOwner|onlyProxy|onlyEntryPoint|noDelegateCall|onlyRole))[^{;]*\{(?![\s\S]{0,800}?(?:initializer|reinitializer|_disableInitializers|require\s*\([^)]*!\s*initialized|require\s*\([^)]*owner\s*==\s*address\s*\(\s*0|noDelegateCall|onlyOwner|onlyEntryPoint|onlyProxy|ERC1967|UUPS|_setOwner|_initialize|_setImplementation|_changeAdmin|EntryPoint|UserOperation|getStorage|StorageSlot|IMPLEMENTATION_SLOT|ADMIN_SLOT|require\s*\([^)]*==\s*address\s*\(\s*0\s*\)|_logic\s*\.\s*delegatecall))/,
4670
4677
  severity: "high",
4671
4678
  category: "solidity",
4672
4679
  confidence: "low",
@@ -4836,8 +4843,12 @@ var hackReplayRules = [
4836
4843
  title: "zkSync hack pattern \u2014 admin-only sweep/drain/rescue function",
4837
4844
  description: "An admin-gated function with a name like sweep*, drain*, rescue*, emergencyWithdraw*, forceMint*, adminMint* is the exact category of function the zkSync airdrop attacker exploited (April 2025, $5M loss). Even with a timelock check inside, these functions are high-value targets \u2014 a compromised admin key bypasses everything. The pattern is reviewed manually because a same-line regex cannot verify whether a timelock guard is enforced.",
4838
4845
  suggestion: "Audit this function carefully: (a) is it gated by a TimelockController contract address check, not just an onlyOwner modifier? (b) is the action bounded (max amount per call, per epoch)? (c) is there an N-of-M multisig requirement at the modifier level? If any answer is no, add it before mainnet.",
4839
- pattern: /function\s+(?:sweep[A-Za-z]*|drain[A-Za-z]*|rescue[A-Za-z]*|emergencyWithdraw[A-Za-z]*|forceMint[A-Za-z]*|adminMint[A-Za-z]*)\s*\([^)]*\)\s*(?:external|public)/,
4840
- severity: "high",
4846
+ // Suppress when function has a multi-element lock (onlyRescuer with named slot),
4847
+ // onlyPoolAdmin pattern (Aave), or other named-role modifiers that imply auditing-already-done.
4848
+ // Real catch is "onlyOwner" plain — anonymous single-role drain.
4849
+ multilinePattern: /function\s+(?:sweep[A-Za-z]*|drain[A-Za-z]*|rescue[A-Za-z]*|emergencyWithdraw[A-Za-z]*|forceMint[A-Za-z]*|adminMint[A-Za-z]*)\s*\([^)]*\)\s*(?:external|public)(?!.*(?:onlyRescuer|onlyPoolAdmin|onlyEmergencyAdmin|onlyRiskAdmin|onlyAclAdmin|onlyRoleAdmin|onlyConfigurator|hasRole))[^{;]*\{/,
4850
+ // Downgraded from high to medium: this is a "review needed" finding, not "definitely vulnerable"
4851
+ severity: "medium",
4841
4852
  category: "hack-replay",
4842
4853
  confidence: "medium",
4843
4854
  languages: SOL
@@ -4873,9 +4884,8 @@ var hackReplayRules = [
4873
4884
  // patterns that use proper auth/delay even if "timelock" keyword isn't right next to execute().
4874
4885
  // Suppress: ERC-4337/7579 account abstraction execute(mode, data), OZ Governor, AccessManager,
4875
4886
  // anything with auth modifiers. Real Beanstalk-shape is execute(proposalId) on a governance contract.
4876
- // Suppress: interface signatures (ending in ;), OZ Governor/IGovernor, AccessManager, account
4877
- // abstraction execute() patterns, anything with proper auth. Requires actual function BODY.
4878
- multilinePattern: /function\s+(?:execute(?:Proposal)?|propose(?:AndExecute)?)\s*\([^)]*\)\s*(?:external|public)(?:\s+payable)?(?:\s+returns)?[^{;]*\{(?![\s\S]{0,1500}?(?:timelock|Timelock|TimelockController|queued|block\.timestamp\s*[><]=?\s*\w+\s*\+|onlyRole|onlyGovernance|onlyEntryPoint|onlyEntryPointOrSelf|_checkAuthorized|_authorizeUpgrade|AccessControl|AccessManager|Governor|IGovernor|hasRole|getMinDelay|delay\s*\(|_executeOperations|hashOperation|isOperationReady|EntryPoint|ERC7579|ERC4337|ERC7821|IAccount|_validateUserOp|UserOperation|state\s*\(\s*proposalId|_castVote|votingDelay))/,
4887
+ // Expanded suppression: ERC-2771 meta-tx forwarders, ERC-6551 token-bound accounts.
4888
+ multilinePattern: /function\s+(?:execute(?:Proposal)?|propose(?:AndExecute)?)\s*\([^)]*\)\s*(?:external|public)(?:\s+payable)?(?:\s+returns)?[^{;]*\{(?![\s\S]{0,1500}?(?:timelock|Timelock|TimelockController|queued|block\.timestamp\s*[><]=?\s*\w+\s*\+|onlyRole|onlyGovernance|onlyEntryPoint|onlyEntryPointOrSelf|_checkAuthorized|_authorizeUpgrade|AccessControl|AccessManager|Governor|IGovernor|hasRole|getMinDelay|delay\s*\(|_executeOperations|hashOperation|isOperationReady|EntryPoint|ERC7579|ERC4337|ERC7821|ERC2771|ERC6551|Forwarder|ForwardRequest|MetaTx|TokenBoundAccount|_verify|IAccount|_validateUserOp|UserOperation|state\s*\(\s*proposalId|_castVote|votingDelay))/,
4879
4889
  severity: "critical",
4880
4890
  category: "hack-replay",
4881
4891
  confidence: "low",
@@ -4946,7 +4956,11 @@ var hackReplayRules = [
4946
4956
  // Suppress legitimate EIP-712 / Permit / typed-data signature flows (OZ ECDSA, EIP-2612 permits,
4947
4957
  // ERC-4337 user-op validation) which use ecrecover correctly. Only fire on bridge/oracle-shaped
4948
4958
  // contexts where signer-set verification matters.
4949
- multilinePattern: /(?:ecrecover|ECDSA\.recover|\.recover)\s*\([\s\S]{0,400}?\)(?![\s\S]{0,800}?(?:guardianSet|validatorSet|currentSetHash|setHash|_hashTypedDataV4|EIP712|DOMAIN_SEPARATOR|PERMIT_TYPEHASH|DELEGATION_TYPEHASH|ERC1271|isValidSignature|nonces\s*\[|require\s*\([^)]*==\s*expected))/,
4959
+ // EIP-712 / permit / typed-data patterns can appear BEFORE or AFTER ecrecover. Use a
4960
+ // wrapping check: require the recover call to be in a CONTEXT that contains typed-data
4961
+ // keywords within ~1000 chars on either side. We approximate by lookahead-only with a
4962
+ // bigger window AND lookbehind-by-context (check if file declares typed-data structures).
4963
+ multilinePattern: /(?:ecrecover|ECDSA\.recover|\w+\.recover)\s*\([\s\S]{0,400}?\)(?![\s\S]{0,1500}?(?:guardianSet|validatorSet|currentSetHash|setHash|_hashTypedDataV4|EIP712|EIP_712|DOMAIN_SEPARATOR|PERMIT_TYPEHASH|DELEGATION_TYPEHASH|ERC1271|isValidSignature|_?nonces\s*\[|_approve|_approveDelegation|_useCheckedNonce|_useNonce|require\s*\([^)]*==\s*expected))/,
4950
4964
  severity: "high",
4951
4965
  category: "hack-replay",
4952
4966
  confidence: "low",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elytrasec/engine",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "Core analysis engine for Elytra \u2014 173 detection rules including 12 famous-hack patterns and 11 rug-surface checks, static + AI scanning, scoring.",
5
5
  "license": "MIT",
6
6
  "author": "ElytraSec <hello@elytrasec.io>",