@elytrasec/engine 0.4.2 → 0.4.4
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.js +61 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3028,12 +3028,48 @@ var securityRules = [
|
|
|
3028
3028
|
title: "Potential command injection",
|
|
3029
3029
|
description: "Shell command built with string concatenation or template literals. Attacker-controlled input may escape the intended command.",
|
|
3030
3030
|
suggestion: "Use execFile() with an argument array or a library like execa that avoids shell interpretation.",
|
|
3031
|
-
|
|
3031
|
+
// Catches:
|
|
3032
|
+
// JS: exec(`ls ${dir}`), execSync("cmd " + x), spawn("...", ...)
|
|
3033
|
+
// Py: os.system(f"ping {host}"), os.popen("cmd " + x), subprocess.call("..." + x, shell=True)
|
|
3034
|
+
pattern: /(?:exec|execSync|spawn|spawnSync|system|popen|run|call|check_output|getoutput)\s*\(\s*(?:`[^`]*\$\{|[fF]"(?:[^"\\]|\\.)*\{|[fF]'(?:[^'\\]|\\.)*\{|"(?:[^"\\]|\\.)*"\s*\+|'(?:[^'\\]|\\.)*'\s*\+)/,
|
|
3032
3035
|
severity: "critical",
|
|
3033
3036
|
category: "security",
|
|
3034
3037
|
confidence: "medium",
|
|
3035
3038
|
languages: [...JS_TS, ...PY]
|
|
3036
3039
|
},
|
|
3040
|
+
{
|
|
3041
|
+
id: "cp-sec-python-eval",
|
|
3042
|
+
title: "Python eval() / exec() with dynamic input",
|
|
3043
|
+
description: "Python `eval()` or `exec()` executes arbitrary code from a string. Reachable via `request.args`, `request.form`, or any user-controlled variable, this is direct RCE.",
|
|
3044
|
+
suggestion: "Avoid eval/exec entirely. For arithmetic, use `ast.literal_eval`. For dispatch, use an explicit allow-list dict.",
|
|
3045
|
+
pattern: /\b(?:eval|exec)\s*\(\s*(?:request\.|input\s*\(|sys\.argv|os\.environ|[\w.]+\.(?:args|form|query|params|json|body))/,
|
|
3046
|
+
severity: "critical",
|
|
3047
|
+
category: "security",
|
|
3048
|
+
confidence: "high",
|
|
3049
|
+
languages: PY
|
|
3050
|
+
},
|
|
3051
|
+
{
|
|
3052
|
+
id: "cp-sec-python-eval-loose",
|
|
3053
|
+
title: "Python eval() / exec() call",
|
|
3054
|
+
description: "Python's `eval()` and `exec()` evaluate arbitrary code. Even when input looks trusted, these are common code-injection vectors.",
|
|
3055
|
+
suggestion: "Replace with `ast.literal_eval` (for data literals) or an explicit allow-list. If you must use eval, scope the globals/locals dict to empty.",
|
|
3056
|
+
pattern: /\b(?:eval|exec)\s*\(\s*(?!{|\[|'\)|"\)|None|globals|locals)/,
|
|
3057
|
+
severity: "high",
|
|
3058
|
+
category: "security",
|
|
3059
|
+
confidence: "low",
|
|
3060
|
+
languages: PY
|
|
3061
|
+
},
|
|
3062
|
+
{
|
|
3063
|
+
id: "cp-sec-go-path-traversal",
|
|
3064
|
+
title: "Go file read with unsanitized user input (path traversal)",
|
|
3065
|
+
description: "`ioutil.ReadFile` / `os.ReadFile` / `os.Open` constructed via concatenation with HTTP request data is path-traversal-vulnerable. Attackers pass `../../etc/passwd`.",
|
|
3066
|
+
suggestion: "Use `filepath.Clean` and verify the resolved path starts with the expected base directory. Better: maintain an allow-list of accepted file IDs.",
|
|
3067
|
+
pattern: /(?:ioutil\.ReadFile|os\.ReadFile|os\.Open(?:File)?)\s*\([^)]*(?:\+\s*\w+\s*\)|r\.URL\.Query|r\.FormValue|mux\.Vars)/,
|
|
3068
|
+
severity: "high",
|
|
3069
|
+
category: "security",
|
|
3070
|
+
confidence: "medium",
|
|
3071
|
+
languages: [".go"]
|
|
3072
|
+
},
|
|
3037
3073
|
{
|
|
3038
3074
|
id: "cp-sec-open-redirect",
|
|
3039
3075
|
title: "Potential open redirect",
|
|
@@ -3477,8 +3513,11 @@ var solidityRules2 = [
|
|
|
3477
3513
|
title: "Chainlink latestRoundData without staleness check",
|
|
3478
3514
|
description: "Using latestRoundData() without checking updatedAt for staleness can return outdated prices.",
|
|
3479
3515
|
suggestion: "Check that `updatedAt` is recent: `require(block.timestamp - updatedAt < STALENESS_THRESHOLD)`.",
|
|
3480
|
-
pattern:
|
|
3481
|
-
multilinePattern
|
|
3516
|
+
// Removed single-line pattern: it fired on EVERY interface declaration of latestRoundData().
|
|
3517
|
+
// Only the multilinePattern below should fire — it scopes to actual usage with no staleness check.
|
|
3518
|
+
// Match real usage (destructured assignment), suppress when staleness/sequencer/heartbeat
|
|
3519
|
+
// checks are visible in the next ~1500 chars. Skip pure interface declarations.
|
|
3520
|
+
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
3521
|
severity: "high",
|
|
3483
3522
|
category: "security",
|
|
3484
3523
|
confidence: "medium",
|
|
@@ -4666,7 +4705,11 @@ var eip7702Rules = [
|
|
|
4666
4705
|
// initializer modifier, ERC1967/UUPS proxy patterns, or 'once' guard.
|
|
4667
4706
|
// Broader suppression: ERC-4337/7579 accounts use _setOwner pattern, OZ uses initializer modifier,
|
|
4668
4707
|
// proxies use ERC1967, V4 PoolManager.initialize is permissionless-by-design with noDelegateCall.
|
|
4669
|
-
|
|
4708
|
+
// Suppress when function declaration line itself contains a guard modifier
|
|
4709
|
+
// (initializer, onlyOwner, onlyProxy, noDelegateCall, payable initializer, etc.) — these
|
|
4710
|
+
// come BEFORE the opening { so the prior lookahead-after-brace missed them.
|
|
4711
|
+
// Then suppress when proxy/4337/UUPS/admin patterns appear in body.
|
|
4712
|
+
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
4713
|
severity: "high",
|
|
4671
4714
|
category: "solidity",
|
|
4672
4715
|
confidence: "low",
|
|
@@ -4836,8 +4879,12 @@ var hackReplayRules = [
|
|
|
4836
4879
|
title: "zkSync hack pattern \u2014 admin-only sweep/drain/rescue function",
|
|
4837
4880
|
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
4881
|
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
|
-
|
|
4840
|
-
|
|
4882
|
+
// Suppress when function has a multi-element lock (onlyRescuer with named slot),
|
|
4883
|
+
// onlyPoolAdmin pattern (Aave), or other named-role modifiers that imply auditing-already-done.
|
|
4884
|
+
// Real catch is "onlyOwner" plain — anonymous single-role drain.
|
|
4885
|
+
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))[^{;]*\{/,
|
|
4886
|
+
// Downgraded from high to medium: this is a "review needed" finding, not "definitely vulnerable"
|
|
4887
|
+
severity: "medium",
|
|
4841
4888
|
category: "hack-replay",
|
|
4842
4889
|
confidence: "medium",
|
|
4843
4890
|
languages: SOL
|
|
@@ -4873,9 +4920,8 @@ var hackReplayRules = [
|
|
|
4873
4920
|
// patterns that use proper auth/delay even if "timelock" keyword isn't right next to execute().
|
|
4874
4921
|
// Suppress: ERC-4337/7579 account abstraction execute(mode, data), OZ Governor, AccessManager,
|
|
4875
4922
|
// anything with auth modifiers. Real Beanstalk-shape is execute(proposalId) on a governance contract.
|
|
4876
|
-
//
|
|
4877
|
-
|
|
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))/,
|
|
4923
|
+
// Expanded suppression: ERC-2771 meta-tx forwarders, ERC-6551 token-bound accounts.
|
|
4924
|
+
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
4925
|
severity: "critical",
|
|
4880
4926
|
category: "hack-replay",
|
|
4881
4927
|
confidence: "low",
|
|
@@ -4946,7 +4992,11 @@ var hackReplayRules = [
|
|
|
4946
4992
|
// Suppress legitimate EIP-712 / Permit / typed-data signature flows (OZ ECDSA, EIP-2612 permits,
|
|
4947
4993
|
// ERC-4337 user-op validation) which use ecrecover correctly. Only fire on bridge/oracle-shaped
|
|
4948
4994
|
// contexts where signer-set verification matters.
|
|
4949
|
-
|
|
4995
|
+
// EIP-712 / permit / typed-data patterns can appear BEFORE or AFTER ecrecover. Use a
|
|
4996
|
+
// wrapping check: require the recover call to be in a CONTEXT that contains typed-data
|
|
4997
|
+
// keywords within ~1000 chars on either side. We approximate by lookahead-only with a
|
|
4998
|
+
// bigger window AND lookbehind-by-context (check if file declares typed-data structures).
|
|
4999
|
+
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
5000
|
severity: "high",
|
|
4951
5001
|
category: "hack-replay",
|
|
4952
5002
|
confidence: "low",
|
|
@@ -5465,6 +5515,7 @@ function scanFile(relPath, content, rules, changedRanges) {
|
|
|
5465
5515
|
if (!rule.multilinePattern) continue;
|
|
5466
5516
|
if (rule.id === "cp-clean-callback-hell" && isTestFile(relPath)) continue;
|
|
5467
5517
|
if (rule.id === "cp-sec-command-injection" && isScriptDir(relPath)) continue;
|
|
5518
|
+
if (rule.id === "cp-hack-wormhole-unchecked-signature-set" && /\b(?:EIP712|DOMAIN_SEPARATOR|_hashTypedDataV4|PERMIT_TYPEHASH|DELEGATION_TYPEHASH|ERC1271|EIP712Upgradeable)\b/.test(content)) continue;
|
|
5468
5519
|
rule.multilinePattern.lastIndex = 0;
|
|
5469
5520
|
const isGlobal = rule.multilinePattern.flags.includes("g");
|
|
5470
5521
|
let match;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elytrasec/engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
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>",
|