@elytrasec/engine 0.4.1 → 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.
- package/dist/index.d.ts +1358 -0
- package/dist/index.js +78 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2921,7 +2921,13 @@ var securityRules = [
|
|
|
2921
2921
|
title: "Potential SQL injection",
|
|
2922
2922
|
description: "SQL query built with template literals or string concatenation. User input may flow into the query unsanitised.",
|
|
2923
2923
|
suggestion: "Use parameterised queries or prepared statements instead of string interpolation.",
|
|
2924
|
-
|
|
2924
|
+
// Catches:
|
|
2925
|
+
// .query(`SELECT ... ${x}`) — template literal interpolation
|
|
2926
|
+
// .query("SELECT '" + x + "'") — double-quote outer + escaped single inside (the OWASP #1 shape)
|
|
2927
|
+
// .query('SELECT \\'" + x + "\\'') — single-quote outer + escaped double inside
|
|
2928
|
+
// .queryRawUnsafe("SELECT ... " + req.x) — Prisma raw + concat
|
|
2929
|
+
// .execute("..." + var) — plain concat
|
|
2930
|
+
pattern: /(?:query|execute|exec|raw|queryRaw|queryRawUnsafe|executeRaw|executeRawUnsafe)\s*\(\s*(?:`[^`]*\$\{|"(?:[^"\\]|\\.)*"\s*\+|'(?:[^'\\]|\\.)*'\s*\+)/i,
|
|
2925
2931
|
multilinePattern: /(?:(?:let|const|var)\s+\w+\s*=\s*["'`](?:SELECT|INSERT|UPDATE|DELETE)\b[\s\S]{1,500}?\+\s*(?:req\.|params\.|query\.|body\.|args\.|user)|(?:let|const|var)\s+\w+\s*=\s*`[^`]*(?:SELECT|INSERT|UPDATE|DELETE)\b[^`]*\$\{[^`]*`)/gim,
|
|
2926
2932
|
severity: "critical",
|
|
2927
2933
|
category: "security",
|
|
@@ -3343,8 +3349,10 @@ var solidityRules2 = [
|
|
|
3343
3349
|
title: "Potential reentrancy \u2014 review external call sequencing",
|
|
3344
3350
|
description: "An external call via `.call{value:...}` is present. The classic reentrancy pattern requires the call to occur BEFORE a state update \u2014 a same-line regex can't verify the order across multiple lines, so this is flagged for manual or AI review. `.send` and `.transfer` are intentionally not flagged because their 2300-gas stipend prevents reentrancy except in pathological cases (post-EIP-1884 gas changes).",
|
|
3345
3351
|
suggestion: "Follow checks-effects-interactions: update state before external calls. Or use OpenZeppelin's ReentrancyGuard. Confirm by reading the surrounding function or running AI deep-review.",
|
|
3346
|
-
|
|
3347
|
-
|
|
3352
|
+
// Only fire when .call{value:} appears AND no reentrancy-guard / OZ helper is visible
|
|
3353
|
+
// in the same file. Reduces FP storm on Governor, ERC7579, ERC4626, etc.
|
|
3354
|
+
multilinePattern: /\.call\s*\{(?:[^}]*value[^}]*)?\}\s*\([\s\S]{0,80}?\)(?![\s\S]{0,3000}?(?:nonReentrant|ReentrancyGuard|_NOT_ENTERED|_REENTRANCY_GUARD|onlyRole|onlyGovernance|_checkAuthorized|Address\.functionCall|Address\.sendValue))/,
|
|
3355
|
+
severity: "medium",
|
|
3348
3356
|
category: "security",
|
|
3349
3357
|
confidence: "low",
|
|
3350
3358
|
languages: SOL
|
|
@@ -3354,7 +3362,9 @@ var solidityRules2 = [
|
|
|
3354
3362
|
title: "tx.origin used for authorization",
|
|
3355
3363
|
description: "tx.origin returns the original sender of a transaction. Using it for auth allows phishing attacks via intermediary contracts.",
|
|
3356
3364
|
suggestion: "Use msg.sender instead of tx.origin for authorization checks.",
|
|
3357
|
-
|
|
3365
|
+
// Only flag actual auth use (tx.origin in a require/== check or returned from a getter).
|
|
3366
|
+
// Suppress comments, immutable deployer fingerprints, and Lifebuoy-style "I might add ecrecover" notes.
|
|
3367
|
+
pattern: /(?:require\s*\(\s*tx\.origin\s*==|require\s*\(\s*msg\.sender\s*==\s*tx\.origin|if\s*\(\s*tx\.origin\s*==|return\s+tx\.origin\s*;)/,
|
|
3358
3368
|
severity: "high",
|
|
3359
3369
|
category: "security",
|
|
3360
3370
|
confidence: "high",
|
|
@@ -3365,10 +3375,12 @@ var solidityRules2 = [
|
|
|
3365
3375
|
title: "Unchecked low-level call return value",
|
|
3366
3376
|
description: "Low-level call() returns a boolean success flag. Ignoring it can silently swallow failures.",
|
|
3367
3377
|
suggestion: "Always check the return value: `(bool success, ) = addr.call{...}(...); require(success);`",
|
|
3368
|
-
|
|
3369
|
-
|
|
3378
|
+
// Match low-level call result discarded (no return capture). Suppress if (bool,...) = .call() or
|
|
3379
|
+
// .call returned to a variable; suppress if onlyGovernance / onlyOwner / nonReentrant context nearby.
|
|
3380
|
+
multilinePattern: /(?<![\)\]]\s*=\s*[^=])\b\w+\.call\s*[\({][^)]{0,200}?\)\s*;(?![\s\S]{0,500}?(?:onlyGovernance|onlyRole|nonReentrant|onlyOwner))/,
|
|
3381
|
+
severity: "medium",
|
|
3370
3382
|
category: "security",
|
|
3371
|
-
confidence: "
|
|
3383
|
+
confidence: "low",
|
|
3372
3384
|
languages: SOL
|
|
3373
3385
|
},
|
|
3374
3386
|
{
|
|
@@ -3465,8 +3477,11 @@ var solidityRules2 = [
|
|
|
3465
3477
|
title: "Chainlink latestRoundData without staleness check",
|
|
3466
3478
|
description: "Using latestRoundData() without checking updatedAt for staleness can return outdated prices.",
|
|
3467
3479
|
suggestion: "Check that `updatedAt` is recent: `require(block.timestamp - updatedAt < STALENESS_THRESHOLD)`.",
|
|
3468
|
-
pattern:
|
|
3469
|
-
multilinePattern
|
|
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))/,
|
|
3470
3485
|
severity: "high",
|
|
3471
3486
|
category: "security",
|
|
3472
3487
|
confidence: "medium",
|
|
@@ -3477,10 +3492,12 @@ var solidityRules2 = [
|
|
|
3477
3492
|
title: "balanceOf used for pricing without flash loan guards",
|
|
3478
3493
|
description: "Using balanceOf() for pricing calculations is vulnerable to flash loan manipulation.",
|
|
3479
3494
|
suggestion: "Use time-weighted average prices (TWAP) or Chainlink oracles instead of spot balanceOf for pricing.",
|
|
3480
|
-
|
|
3495
|
+
// Require the balanceOf call to be inside an actual function body (not a docstring/comment block),
|
|
3496
|
+
// and must NOT have TWAP/oracle/Chainlink keywords visible nearby.
|
|
3497
|
+
multilinePattern: /function\s+\w+[^{]*\{[\s\S]{0,2000}?balanceOf\s*\([^)]*\)[\s\S]{0,20}(?:\*|\/)\s*\w(?![\s\S]{0,400}?(?:TWAP|twap|Chainlink|chainlink|oracle\.|priceFeed|latestAnswer|getRoundData|observe\s*\())/,
|
|
3481
3498
|
severity: "high",
|
|
3482
3499
|
category: "security",
|
|
3483
|
-
confidence: "
|
|
3500
|
+
confidence: "low",
|
|
3484
3501
|
languages: SOL
|
|
3485
3502
|
},
|
|
3486
3503
|
{
|
|
@@ -4648,7 +4665,15 @@ var eip7702Rules = [
|
|
|
4648
4665
|
title: "EIP-7702 delegation: unguarded initializer",
|
|
4649
4666
|
description: "Contracts intended as EIP-7702 delegation targets must guard initializers \u2014 constructors don't run on delegated EOAs. An unguarded `initialize()` can be front-run, letting an attacker take ownership. The pattern also matches generic proxy initializers, which usually ARE guarded (just not by an `initializer` keyword on the same line), so confirm whether this contract is actually a 7702 target before treating as critical.",
|
|
4650
4667
|
suggestion: "If this is a 7702 delegation target: add `initializer` modifier (OpenZeppelin) or a boolean guard. For regular proxies, confirm the initializer is gated by the proxy factory.",
|
|
4651
|
-
|
|
4668
|
+
// Suppress: initialize() with explicit access guard (onlyOwner, onlyEntryPoint, noDelegateCall),
|
|
4669
|
+
// initializer modifier, ERC1967/UUPS proxy patterns, or 'once' guard.
|
|
4670
|
+
// Broader suppression: ERC-4337/7579 accounts use _setOwner pattern, OZ uses initializer modifier,
|
|
4671
|
+
// proxies use ERC1967, V4 PoolManager.initialize is permissionless-by-design with noDelegateCall.
|
|
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))/,
|
|
4652
4677
|
severity: "high",
|
|
4653
4678
|
category: "solidity",
|
|
4654
4679
|
confidence: "low",
|
|
@@ -4672,10 +4697,12 @@ var tstoreRules = [
|
|
|
4672
4697
|
title: "Transient storage (TSTORE) used \u2014 verify reentrancy safety",
|
|
4673
4698
|
description: "Transient storage (EIP-1153, live since Cancun) does not impose the 2300 gas minimum of SSTORE-based guards. If this contract also uses `transfer()` or `send()` for ETH, those calls are no longer reentrancy-safe \u2014 the callee has enough gas to re-enter via TLOAD/TSTORE paths.",
|
|
4674
4699
|
suggestion: 'Audit every ETH transfer in this contract. Replace `transfer()`/`send()` with `call{value:}("")` + explicit reentrancy check. Ensure TSTORE lock is written before any external call.',
|
|
4675
|
-
|
|
4676
|
-
|
|
4700
|
+
// Only fire on ACTUAL tstore() use inside inline assembly with no nonReentrant-style guard
|
|
4701
|
+
// visible in the same file. Imports of TransientSlot alone are not a finding.
|
|
4702
|
+
multilinePattern: /assembly\s*\{[^}]{0,400}?\btstore\s*\((?![\s\S]{0,2000}?(?:nonReentrant|ReentrancyGuard|_NOT_ENTERED|_REENTRANCY_GUARD|LockBitmap|onlyByPosm|onlyByPoolManager))/,
|
|
4703
|
+
severity: "medium",
|
|
4677
4704
|
category: "solidity",
|
|
4678
|
-
confidence: "
|
|
4705
|
+
confidence: "low",
|
|
4679
4706
|
languages: SOL
|
|
4680
4707
|
},
|
|
4681
4708
|
{
|
|
@@ -4696,7 +4723,9 @@ var uniswapV4Rules = [
|
|
|
4696
4723
|
title: "Uniswap v4 hook: missing PoolManager sender validation",
|
|
4697
4724
|
description: "Hook callbacks (`beforeSwap`, `afterSwap`, `beforeAddLiquidity`, etc.) must verify `msg.sender == address(poolManager)`. Without this check, any address can call the hook directly and manipulate its state.",
|
|
4698
4725
|
suggestion: 'Add `require(msg.sender == address(poolManager), "not PoolManager");` as the first line of every hook callback, or inherit from `BaseHook` which enforces this automatically.',
|
|
4699
|
-
|
|
4726
|
+
// Require function BODY (not interface signature ending in ;) AND missing sender check.
|
|
4727
|
+
// Skip interface files and abstract declarations.
|
|
4728
|
+
multilinePattern: /function\s+(?:before|after)(?:Swap|AddLiquidity|RemoveLiquidity|Initialize|Donate)\s*\([^)]*\)\s*(?:external|public)[^{;]*\{(?![\s\S]{0,300}?(?:msg\.sender\s*==\s*(?:address\s*\()?\s*(?:poolManager|manager|_poolManager|POOL_MANAGER|i_poolManager)|onlyByPoolManager|onlyByManager|onlyPoolManager|BaseHook|_validatePoolManager))/,
|
|
4700
4729
|
severity: "critical",
|
|
4701
4730
|
category: "solidity",
|
|
4702
4731
|
confidence: "medium",
|
|
@@ -4719,7 +4748,9 @@ var uniswapV4Rules = [
|
|
|
4719
4748
|
title: "Uniswap v4 hook: BalanceDelta returned without settle/take",
|
|
4720
4749
|
description: "Hooks that return a modified `BalanceDelta` must ensure the delta is fully consumed (settled or taken) within the same unlock cycle. An unconsumed delta causes `CurrencyNotSettled` revert; partial consumption can silently strand tokens.",
|
|
4721
4750
|
suggestion: "Ensure `poolManager.settle()` or `poolManager.take()` is called for both currency0 and currency1 before returning from the unlock callback.",
|
|
4722
|
-
|
|
4751
|
+
// Only fire on hook callbacks specifically (before/after-Swap etc.) — not interface decls,
|
|
4752
|
+
// not PoolManager itself (which is the singleton, returns deltas BY DESIGN).
|
|
4753
|
+
multilinePattern: /function\s+(?:before|after)(?:Swap|AddLiquidity|RemoveLiquidity|Donate)\s*\([^)]*\)\s*(?:external|public)[^{;]*returns\s*\([^)]*BalanceDelta[^)]*\)[^{;]*\{(?![\s\S]{0,800}?(?:settle|\.take\s*\(|poolManager\.))/,
|
|
4723
4754
|
severity: "high",
|
|
4724
4755
|
category: "solidity",
|
|
4725
4756
|
confidence: "low",
|
|
@@ -4741,7 +4772,9 @@ var uniswapV4Rules = [
|
|
|
4741
4772
|
title: "Uniswap v4 hook: spot sqrtPriceX96 used for pricing without limit",
|
|
4742
4773
|
description: "Using spot `sqrtPriceX96` read from pool state inside a hook callback for pricing decisions without a `sqrtPriceLimitX96` bound is sandwich-attackable. The price reflects post-swap state which may be manipulated.",
|
|
4743
4774
|
suggestion: "Pass `sqrtPriceLimitX96` to all swap calls from hooks. Use a TWAP oracle for pricing rather than spot price.",
|
|
4744
|
-
|
|
4775
|
+
// Only fire inside hook callback functions (before/after Swap/AddLiquidity), not library files
|
|
4776
|
+
// (Pool.sol, TickMath.sol, etc.) which legitimately work with raw sqrt prices.
|
|
4777
|
+
multilinePattern: /function\s+(?:before|after)(?:Swap|AddLiquidity|RemoveLiquidity|Donate)\s*\([^)]*\)\s*(?:external|public)[^{;]*\{[\s\S]{0,1500}?sqrtPriceX96\s*[*/+\-]\s*\w(?![\s\S]{0,300}?(?:TWAP|twap|sqrtPriceLimitX96|observe\s*\())/,
|
|
4745
4778
|
severity: "high",
|
|
4746
4779
|
category: "solidity",
|
|
4747
4780
|
confidence: "low",
|
|
@@ -4773,7 +4806,9 @@ var hackReplayRules = [
|
|
|
4773
4806
|
title: "Euler hack pattern \u2014 donate function reachable from liquidation path",
|
|
4774
4807
|
description: "A donate/donateTo* function in a lending or vault context can be weaponized to artificially worsen the caller's health factor, then trigger self-liquidation at a profit. This is the exact vector that drained $197M from Euler Finance in March 2023.",
|
|
4775
4808
|
suggestion: "Either remove the donate function, or (a) require donations to come from accounts with no active borrows, (b) recompute health AFTER the donation as if the donated assets remained the donor's, and (c) prevent same-block liquidation of the donor.",
|
|
4776
|
-
|
|
4809
|
+
// Only flag donate functions in lending/health/liquidation contexts. Skip pure liquidity-pool
|
|
4810
|
+
// donate functions (Uniswap V4 PoolManager.donate gives fees to LPs — different semantics).
|
|
4811
|
+
multilinePattern: /function\s+donate[A-Za-z]*\s*\([^)]*\)[^{;]*\{[\s\S]{0,2000}?(?:healthFactor|accountHealth|isHealthy|borrow|collateral|liquidat|debt|vault|reserves)/,
|
|
4777
4812
|
severity: "high",
|
|
4778
4813
|
category: "hack-replay",
|
|
4779
4814
|
confidence: "medium",
|
|
@@ -4808,8 +4843,12 @@ var hackReplayRules = [
|
|
|
4808
4843
|
title: "zkSync hack pattern \u2014 admin-only sweep/drain/rescue function",
|
|
4809
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.",
|
|
4810
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.",
|
|
4811
|
-
|
|
4812
|
-
|
|
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",
|
|
4813
4852
|
category: "hack-replay",
|
|
4814
4853
|
confidence: "medium",
|
|
4815
4854
|
languages: SOL
|
|
@@ -4841,7 +4880,12 @@ var hackReplayRules = [
|
|
|
4841
4880
|
title: "Beanstalk hack pattern \u2014 governance execute() with no timelock between vote and call",
|
|
4842
4881
|
description: "A governance contract exposes an execute() / executeProposal() / propose() function callable in the same block as voting. This is the exact $182M Beanstalk vector (April 2022): an attacker flash-loaned the governance token, voted yes on a self-draining proposal, and called execute() in the same transaction. A timelock between successful vote and execution would have made the flash-loan economically pointless.",
|
|
4843
4882
|
suggestion: "Add a queue/execute split: successful proposals enter a TimelockController with a minimum delay (24-48h is standard). Require execute() to verify block.timestamp >= queuedAt + delay. Never let voting power, proposal acceptance, and code execution happen atomically.",
|
|
4844
|
-
|
|
4883
|
+
// Expanded negative lookahead to suppress OZ Governor/AccessManager/AccessControl
|
|
4884
|
+
// patterns that use proper auth/delay even if "timelock" keyword isn't right next to execute().
|
|
4885
|
+
// Suppress: ERC-4337/7579 account abstraction execute(mode, data), OZ Governor, AccessManager,
|
|
4886
|
+
// anything with auth modifiers. Real Beanstalk-shape is execute(proposalId) on a governance contract.
|
|
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))/,
|
|
4845
4889
|
severity: "critical",
|
|
4846
4890
|
category: "hack-replay",
|
|
4847
4891
|
confidence: "low",
|
|
@@ -4909,8 +4953,15 @@ var hackReplayRules = [
|
|
|
4909
4953
|
title: "Wormhole hack pattern \u2014 guardian/validator signature accepted without strict set verification",
|
|
4910
4954
|
description: "A function verifies a signature against a validator/guardian set but does not strictly check that the set hash matches the expected current set (or compares against a default/empty value). The $325M Wormhole hack (Feb 2022) exploited a stub `verify_signatures` path that accepted forged signatures because the validator set comparison was missing/insecure. ecrecover-based signature schemes need every validator set rotation tracked by an explicit hash check.",
|
|
4911
4955
|
suggestion: "Verify the signed message includes a strict hash of the current validator set. Reject signatures where the recovered signer is not in the active set. Never use `default!()` or zero-initialized guardian arrays in signature paths.",
|
|
4912
|
-
|
|
4913
|
-
|
|
4956
|
+
// Suppress legitimate EIP-712 / Permit / typed-data signature flows (OZ ECDSA, EIP-2612 permits,
|
|
4957
|
+
// ERC-4337 user-op validation) which use ecrecover correctly. Only fire on bridge/oracle-shaped
|
|
4958
|
+
// contexts where signer-set verification matters.
|
|
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))/,
|
|
4964
|
+
severity: "high",
|
|
4914
4965
|
category: "hack-replay",
|
|
4915
4966
|
confidence: "low",
|
|
4916
4967
|
languages: SOL
|
|
@@ -5035,7 +5086,9 @@ var rugSurfaceRules = [
|
|
|
5035
5086
|
title: "Role admin can grant itself any role",
|
|
5036
5087
|
description: "DEFAULT_ADMIN_ROLE or a self-managed role can call grantRole() on itself or on other privileged roles. Single admin compromise = full takeover.",
|
|
5037
5088
|
suggestion: "Use AccessControlEnumerable + revoke admin's ability to manage its own role. Move sensitive roles to a separate multisig.",
|
|
5038
|
-
|
|
5089
|
+
// Only flag PUBLIC/EXTERNAL grantRole(role, msg.sender) or non-constructor _grantRole.
|
|
5090
|
+
// OZ AccessControl's constructor _grantRole(DEFAULT_ADMIN_ROLE, initialAdmin) is legit setup.
|
|
5091
|
+
multilinePattern: /function\s+\w+\s*\([^)]*\)\s*(?:external|public)[^{]*\{[\s\S]{0,400}?(?:_setupRole\s*\(\s*DEFAULT_ADMIN_ROLE|_grantRole\s*\(\s*DEFAULT_ADMIN_ROLE|grantRole\s*\([^,]+,\s*msg\.sender\s*\))/,
|
|
5039
5092
|
severity: "high",
|
|
5040
5093
|
category: "rug-surface",
|
|
5041
5094
|
confidence: "medium",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elytrasec/engine",
|
|
3
|
-
"version": "0.4.
|
|
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>",
|