019bab3b-aa91-70e2-a7d3-704d770ca089
Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?
| ID | Type | Status | Attempts | Heartbeat |
|---|---|---|---|---|
| 38345 | artifact.fetch | complete | 1 / 3 | 3 months ago |
| 38346 | proxy.resolve | complete | 1 / 3 | 3 months ago |
| 38347 | tool.cast_disasm | complete | 1 / 3 | 3 months ago |
| 38348 | analysis.bundle | complete | 1 / 3 | 3 months ago |
| 38349 | capability.graph | complete | 1 / 3 | 3 months ago |
| 38350 | detector.run | complete | 1 / 3 | 3 months ago |
| 38351 | validation.fork | complete | 1 / 3 | 3 months ago |
| 38962 | report.consolidate | complete | 1 / 6 | 3 months ago |
| 38963 | tool.slither | failed | 2 / 2 | 3 months ago |
{
"method": "etherscan+onchain",
"status": "confirmed",
"etherscan_hint": {
"source": "etherscan",
"etherscan": {
"proxy_flag": true,
"implementation": "0x927a83c679a5e1a6435d6bfaef7f20d4db23e2cc"
}
},
"implementation": "0x927a83c679a5e1a6435d6bfaef7f20d4db23e2cc",
"onchain_method": "eip1967"
}
Traceback (most recent call last):
File "/var/www/tripwire/worker/worker.py", line 198, in main
payload = dispatch_job(job_type, job_id, job_meta, run_data, config, laravel_client)
File "/var/www/tripwire/worker/worker.py", line 114, in dispatch_job
return slither_run.run(job_id, job_meta, run_data, config, log, laravel_client)
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/worker/jobs/slither_run.py", line 1084, in run
stdout_text, stderr_text, result_json = _run_slither(
~~~~~~~~~~~~^
workspace,
^^^^^^^^^^
...<4 lines>...
target_path=target_path,
^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/var/www/tripwire/worker/jobs/slither_run.py", line 958, in _run_slither
raise RuntimeError(f"slither failed (exit {result.returncode}): {stderr_text or stdout_text}")
RuntimeError: slither failed (exit 1): 'forge clean' running (wd: /tmp/slither-374awzpr)
'forge config --json' running
'forge build --build-info --skip ./test/** ./script/** --force' running (wd: /tmp/slither-374awzpr)
Traceback (most recent call last):
File "/var/www/tripwire/.venv/bin/slither", line 10, in <module>
sys.exit(main())
~~~~^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 776, in main
main_impl(all_detector_classes=detectors, all_printer_classes=printers)
~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 882, in main_impl
) = process_all(filename, args, detector_classes, printer_classes)
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/slither/__main__.py", line 96, in process_all
compilations = compile_all(target, **vars(args))
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 722, in compile_all
compilations.append(CryticCompile(target, **kwargs))
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 211, in __init__
self._compile(**kwargs)
~~~~~~~~~~~~~^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/crytic_compile.py", line 633, in _compile
self._platform.compile(self, **kwargs)
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/foundry.py", line 102, in compile
hardhat_like_parsing(
~~~~~~~~~~~~~~~~~~~~^
crytic_compile, str(self._target), build_directory, str(self._project_root)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
)
^
File "/var/www/tripwire/.venv/lib/python3.13/site-packages/crytic_compile/platform/hardhat.py", line 52, in hardhat_like_parsing
raise InvalidCompilation(txt)
crytic_compile.platform.exceptions.InvalidCompilation: Compilation failed. Can you run build command?
/tmp/slither-374awzpr/out/build-info is not a directory.
{"no_findings":false,"summary":"Found 1 low-severity issue: validator-manager signatures for registerValidators are replayable, enabling duplicate validator deposits if approvals are reused.","findings":[{"severity":"low","category":"auth","title":"Validator-manager signature can be replayed for registerValidators, allowing duplicate validator deposits","exploit_scenario":"A manager signature used in registerValidators is revealed on-chain. Because the vault does not track used nonces for registerValidators (it uses validatorsRegistryRoot as the nonce) and does not reject already-registered validator pubkeys, an attacker can replay the same manager signature (and any reusable keeper approvals) to call registerValidators again. This can trigger repeated deposits for the same validators, locking additional vault liquidity or bypassing the intended fundValidators path.","recommendation":"Bind registerValidators to a true monotonic nonce (e.g., validatorsManagerNonce) or track used signature/message hashes. Also reject already-registered v2 validator pubkeys (and add duplicate checks for v1) during registration to prevent replayed deposits.","evidence":"registerValidators passes validatorsRegistryRoot as the signature nonce and _isValidatorsManager does not enforce single-use of that nonce; getValidatorDeposits does not prevent re-registration of already tracked validators.","locations":["contracts/vaults/modules/VaultValidators.sol","contracts/libraries/ValidatorUtils.sol"]}]}
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-o6mhmkuz
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019bab63-f125-7b21-8ae3-27bca80ec02a
--------
user
Context: The user requested the proxy at 0xb36fc5e542cb4fc562a624912f55da2758998113.
Audit the implementation at 0x927a83c679a5e1a6435d6bfaef7f20d4db23e2cc.
Findings should refer to the implementation unless explicitly about proxy mechanics.
You are a security auditor. Audit this Solidity codebase for exploitable vulnerabilities.
Focus on: reentrancy, access control, upgradeability/proxy issues, signature validation, oracle manipulation,
accounting/invariant breaks, external call patterns, unchecked return values, and dangerous delegatecall.
Scope rules:
- You MUST fully read `AUDIT_BUNDLE.txt` before concluding. It contains the complete contents of all first-party Solidity sources in this workspace.
- Treat `lib/` and `@openzeppelin/` as third-party dependencies; skip them unless there is a concrete misuse angle in first-party code.
- If there are only dependency files and no first-party sources, set no_findings=true and explain in summary.
Output MUST be valid JSON matching the provided schema.
- If you find nothing meaningful: set no_findings=true and findings=[].
- Otherwise: set no_findings=false and include findings with severity/category/title and file/line locations when possible.
Output constraints (IMPORTANT):
- severity MUST be exactly one of: info, low, medium, high, critical (lowercase).
- category MUST be exactly one of: access_control, reentrancy, math, oracle, upgradeability, auth, other.
Keep summary short and concrete.
mcp startup: no servers
thinking
**Reading bundle file**
exec
/bin/bash -lc ls in /tmp/codex-o6mhmkuz succeeded in 8ms:
AUDIT_BUNDLE.txt
codex-output-schema.json
contracts
lib
thinking
**Opening AUDIT_BUNDLE**
exec
/bin/bash -lc 'cat AUDIT_BUNDLE.txt' in /tmp/codex-o6mhmkuz succeeded in 22ms:
This file concatenates the full contents of first-party Solidity sources for auditing.
Do not treat dependency code under lib/ or @openzeppelin/ as first-party.
================================================================================
FILE: contracts/base/Multicall.sol
================================================================================
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import "../interfaces/IMulticall.sol";
/**
* @title Multicall
* @author StakeWise
* @notice Enables calling multiple methods in a single call to the contract
*/
abstract contract Multicall is IMulticall {
/// @inheritdoc IMulticall
thinking
**Searching for EthVault contract implementation**
exec
/bin/bash -lc 'rg -n "contract EthVault|EthVault" AUDIT_BUNDLE.txt' in /tmp/codex-o6mhmkuz succeeded in 16ms:
219:FILE: contracts/interfaces/IEthVault.sol
238: * @title IEthVault
240: * @notice Defines the interface for the EthVault contract
242:interface IEthVault is
255: * @dev Struct for deploying the EthVault contract
269: struct EthVaultConstructorArgs {
285: * @dev Struct for initializing the EthVault contract
290: struct EthVaultInitParams {
297: * @notice Initializes or upgrades the EthVault contract. Must transfer security deposit during the deployment.
298: * @param params The encoded parameters for initializing the EthVault contract
333:FILE: contracts/interfaces/IEthVaultFactory.sol
340: * @title IEthVaultFactory
344:interface IEthVaultFactory {
2807:FILE: contracts/vaults/ethereum/EthVault.sol
2814:import {IEthVault} from "../../interfaces/IEthVault.sol";
2815:import {IEthVaultFactory} from "../../interfaces/IEthVaultFactory.sol";
2830: * @title EthVault
2834:contract EthVault is
2847: IEthVault
2855: * @param args The arguments for initializing the EthVault contract
thinking
**Reviewing EthVault and related modules**
exec
/bin/bash -lc "sed -n '2700,3400p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 19ms:
uint256 startIndex;
uint256 totalFeeAssets = msg.value;
for (uint256 i = 0; i < validatorsCount;) {
bytes calldata validator = validators[startIndex:startIndex + _validatorWithdrawalLength];
bytes calldata publicKey = validator[:48];
// convert gwei to wei by multiplying by 1 gwei
uint256 withdrawnAmount = (uint256(uint64(bytes8(validator[48:56]))) * 1 gwei);
uint256 feePaid = uint256(bytes32(Address.functionStaticCall(validatorsWithdrawals, "")));
// submit validator withdrawal
Address.functionCallWithValue(validatorsWithdrawals, validator, feePaid);
totalFeeAssets -= feePaid;
emit IVaultValidators.ValidatorWithdrawalSubmitted(publicKey, withdrawnAmount, feePaid);
unchecked {
// cannot realistically overflow
++i;
startIndex += _validatorWithdrawalLength;
thinking
**Inspecting additional modules for vulnerabilities**
exec
/bin/bash -lc "sed -n '3400,4200p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 8ms:
/// @inheritdoc IVaultFee
uint16 public override feePercent;
uint64 private _lastUpdateTimestamp;
/// @inheritdoc IVaultFee
function setFeeRecipient(address _feeRecipient) external override {
_checkAdmin();
_setFeeRecipient(_feeRecipient);
}
/// @inheritdoc IVaultFee
function setFeePercent(uint16 _feePercent) external override {
_checkAdmin();
_setFeePercent(_feePercent, false);
}
/**
* @dev Internal function for updating the fee recipient externally or from the initializer
thinking
**Pausing to review state**
exec
/bin/bash -lc "sed -n '4200,5000p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 6ms:
_totalShares -= SafeCast.toUint128(burnedShares);
_totalAssets -= SafeCast.toUint128(exitedAssets);
}
/**
* @dev Internal function for minting shares
* @param owner The address of the owner to mint shares to
* @param shares The number of shares to mint
*/
function _mintShares(address owner, uint256 shares) internal virtual {
// update total shares
_totalShares += SafeCast.toUint128(shares);
// mint shares
unchecked {
// cannot overflow because the sum of all user
// balances can't exceed the max uint256 value
_balances[owner] += shares;
}
}
thinking
**Analyzing EthVault and related contracts for vulnerabilities**
I'm focusing on EthVault’s implementation within the bundle, checking potential issues like reentrancy, signature validation, and nonce use. I'm scrutinizing deposit and minting flows, external calls in burning tokens, and how validator signatures are managed, especially around nonce handling and possible replay risks. Let's explore related modules like ValidatorUtils and EIP712 for deeper insights.
exec
/bin/bash -lc 'rg -n "library ValidatorUtils|isValidManagerSignature|Validators" AUDIT_BUNDLE.txt' in /tmp/codex-o6mhmkuz succeeded in 8ms:
73:import {IKeeperValidators} from "./IKeeperValidators.sol";
158: IKeeperValidators.ApprovalParams calldata keeperParams,
170: function registerValidators(
172: IKeeperValidators.ApprovalParams calldata keeperParams,
188:FILE: contracts/interfaces/IEthValidatorsRegistry.sol
194:import {IValidatorsRegistry} from "./IValidatorsRegistry.sol";
197: * @title IEthValidatorsRegistry
203:interface IEthValidatorsRegistry is IValidatorsRegistry {
230:import {IVaultValidators} from "./IVaultValidators.sol";
247: IVaultValidators,
650:FILE: contracts/interfaces/IKeeperValidators.sol
660: * @title IKeeperValidators
664:interface IKeeperValidators is IKeeperOracles, IKeeperRewards {
670: event ValidatorsApproval(address indexed vault, string exitSignaturesIpfsHash);
687: event ValidatorsMinOraclesUpdated(uint256 oracles);
722: function approveValidators(ApprovalParams calldata params) external;
742: function setValidatorsMinOracles(uint256 _validatorsMinOracles) external;
1309:FILE: contracts/interfaces/IValidatorsRegistry.sol
1316: * @title IValidatorsRegistry
1320:interface IValidatorsRegistry {
thinking
**Reviewing ValidatorUtils for vulnerabilities**
exec
/bin/bash -lc "sed -n '2480,2725p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 6ms:
revert Errors.LiquidationDisabled();
}
// check health factor violation in case of liquidation
if (
Math.mulDiv(data.depositedAssets * _wad, config.liqThresholdPercent, data.mintedAssets * _maxPercent)
>= _hfLiqThreshold
) {
revert Errors.InvalidHealthFactor();
}
receivedAssets = Math.mulDiv(
osTokenVaultController.convertToAssets(data.redeemedOsTokenShares), config.liqBonusPercent, _maxPercent
);
} else {
receivedAssets = osTokenVaultController.convertToAssets(data.redeemedOsTokenShares);
}
// check whether received assets are valid
if (receivedAssets > data.depositedAssets || receivedAssets > data.availableAssets) {
revert Errors.InvalidReceivedAssets();
}
thinking
**Analyzing potential replay and reentrancy risks**
I'm examining how `registerValidators` might be vulnerable to signature replay since it uses a root as nonce that may not increment, allowing replays to re-register validators and potentially top up deposits repeatedly. I’m also checking if reentrancy is possible via external calls, but the deposit contract appears trusted and non-reentrant. I want to verify details on keeper approvals and nonce usage to see how these impact security.
exec
/bin/bash -lc "sed -n '620,760p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 9ms:
* @param vault The address of the Vault
* @return `true` if Vault is collateralized, `false` otherwise
*/
function isCollateralized(address vault) external view returns (bool);
/**
* @notice Update rewards data
* @param params The struct containing rewards update parameters
*/
function updateRewards(RewardsUpdateParams calldata params) external;
/**
* @notice Harvest rewards. Can be called only by Vault.
* @param params The struct containing rewards harvesting parameters
* @return totalAssetsDelta The total reward/penalty accumulated by the Vault since the last sync
* @return unlockedMevDelta The Vault execution reward that can be withdrawn from shared MEV escrow. Only used by shared MEV Vaults.
* @return harvested `true` when the rewards were harvested, `false` otherwise
*/
function harvest(HarvestParams calldata params)
external
thinking
**Analyzing potential reentrancy and state update risks**
I'm reviewing mint, burn, and transfer functions for vulnerabilities, focusing on external calls that might allow reentrancy before state updates complete. I'll look into trusted assumptions around external contracts like the vault controller and escrow, and plan to investigate related modules such as the deposit data registry next.
exec
/bin/bash -lc 'rg -n "contract DepositDataRegistry|DepositDataRegistry" AUDIT_BUNDLE.txt' in /tmp/codex-o6mhmkuz succeeded in 9ms:
67:FILE: contracts/interfaces/IDepositDataRegistry.sol
78: * @title IDepositDataRegistry
80: * @notice Defines the interface for DepositDataRegistry
82:interface IDepositDataRegistry is IMulticall {
266: * @param depositDataRegistry The address of the DepositDataRegistry contract
1996: * @notice Function for updating the validators manager. Can only be called by the admin. Default is the DepositDataRegistry contract.
4334:import {IDepositDataRegistry} from "../../interfaces/IDepositDataRegistry.sol";
4373: /// deprecated. Deposit data management is moved to DepositDataRegistry contract
4376: /// deprecated. Deposit data management is moved to DepositDataRegistry contract
4574: // migrate deposit data variables to DepositDataRegistry contract
4576: IDepositDataRegistry(_depositDataRegistry).migrate(
thinking
**Investigating contract presence and references**
exec
/bin/bash -lc 'rg -n "''^FILE:" AUDIT_BUNDLE.txt' in /tmp/codex-o6mhmkuz succeeded in 15ms:
5:FILE: contracts/base/Multicall.sol
32:FILE: contracts/interfaces/IConsolidationsChecker.sol
67:FILE: contracts/interfaces/IDepositDataRegistry.sol
188:FILE: contracts/interfaces/IEthValidatorsRegistry.sol
219:FILE: contracts/interfaces/IEthVault.sol
333:FILE: contracts/interfaces/IEthVaultFactory.sol
382:FILE: contracts/interfaces/IKeeperOracles.sol
447:FILE: contracts/interfaces/IKeeperRewards.sol
650:FILE: contracts/interfaces/IKeeperValidators.sol
746:FILE: contracts/interfaces/IMulticall.sol
768:FILE: contracts/interfaces/IOsTokenConfig.sol
835:FILE: contracts/interfaces/IOsTokenVaultController.sol
1018:FILE: contracts/interfaces/IOsTokenVaultEscrow.sol
1232:FILE: contracts/interfaces/IOwnMevEscrow.sol
1273:FILE: contracts/interfaces/ISharedMevEscrow.sol
1309:FILE: contracts/interfaces/IValidatorsRegistry.sol
1330:FILE: contracts/interfaces/IVaultAdmin.sol
1376:FILE: contracts/interfaces/IVaultEnterExit.sol
1489:FILE: contracts/interfaces/IVaultEthStaking.sol
1540:FILE: contracts/interfaces/IVaultFee.sol
thinking
**Analyzing EthVault contract vulnerabilities**
exec
/bin/bash -lc "sed -n '2140,2245p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 6ms:
FILE: contracts/libraries/EIP712Utils.sol
================================================================================
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title EIP712Utils
* @author StakeWise
* @notice Includes functionality for calculating EIP712 hashes
*/
library EIP712Utils {
bytes32 private constant _domainTypeHash =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
bytes32 private constant _versionHash = keccak256("1");
/**
* @notice Computes the hash of the EIP712 typed data
* @dev This function is used to compute the hash of the EIP712 typed data
* @param name The name of the domain
thinking
**Analyzing upgrade and exit queue vulnerabilities**
exec
/bin/bash -lc "sed -n '1330,1425p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 6ms:
FILE: contracts/interfaces/IVaultAdmin.sol
================================================================================
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
/**
* @title IVaultState
* @author StakeWise
* @notice Defines the interface for the VaultAdmin contract
*/
interface IVaultAdmin {
/**
* @notice Event emitted on metadata ipfs hash update
* @param caller The address of the function caller
* @param metadataIpfsHash The new metadata IPFS hash
*/
event MetadataUpdated(address indexed caller, string metadataIpfsHash);
/**
thinking
**Analyzing potential vulnerabilities and logic consistency**
I'm carefully evaluating various contract functions for possible security issues like signature replay, reentrancy, underflow, and state inconsistencies, especially in validator management, asset accounting, and token redemption flows; I’m noting assumptions about trusted keepers and external calls while highlighting subtle edge cases and where protections may rely on external trust.
thinking
**Evaluating upgrade and signature handling intricacies**
I'm investigating external calls during minting and redeeming tokens, upgrade initialization logic, and potential signature replay issues in validator management; I’m also reviewing reentrancy risks in external calls and subtle nonce increments that could unintentionally disrupt signature workflows while considering contract trust assumptions.
thinking
**Reviewing upgrade paths and queue index handling**
exec
/bin/bash -lc "sed -n '2241,2425p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 7ms:
FILE: contracts/libraries/ExitQueue.sol
================================================================================
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {Errors} from "./Errors.sol";
/**
* @title ExitQueue
* @author StakeWise
* @notice ExitQueue represent checkpoints of burned shares and exited assets
*/
library ExitQueue {
/**
* @notice A struct containing checkpoint data
* @param totalTickets The cumulative number of tickets (shares) exited
* @param exitedAssets The number of assets that exited in this checkpoint
thinking
**Analyzing complex contract interactions and potential risks**
thinking
**Reviewing upgradeability and reentrancy considerations**
I'm carefully weighing the upgrade pattern, external calls, and reentrancy guards; after thorough checks, state updates before external calls appear safe, and no concerning upgradeability gaps stand out. It seems acceptable to report no findings while confirming the review scope and careful analysis of key modules.
exec
/bin/bash -lc "sed -n '2423,2506p' AUDIT_BUNDLE.txt" in /tmp/codex-o6mhmkuz succeeded in 31ms:
FILE: contracts/libraries/OsTokenUtils.sol
================================================================================
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.22;
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IOsTokenConfig} from "../interfaces/IOsTokenConfig.sol";
import {IOsTokenVaultController} from "../interfaces/IOsTokenVaultController.sol";
import {Errors} from "./Errors.sol";
/**
* @title OsTokenUtils
* @author StakeWise
* @notice Includes functionality for handling osToken redemptions
*/
library OsTokenUtils {
uint256 private constant _wad = 1e18;
uint256 private constant _hfLiqThreshold = 1e18;
uint256 private constant _maxPercent = 1e18;
thinking
**Finding replay vulnerability in validator registration**
codex
{"no_findings":false,"summary":"Found 1 low-severity issue: validator-manager signatures for registerValidators are replayable, enabling duplicate validator deposits if approvals are reused.","findings":[{"severity":"low","category":"auth","title":"Validator-manager signature can be replayed for registerValidators, allowing duplicate validator deposits","exploit_scenario":"A manager signature used in registerValidators is revealed on-chain. Because the vault does not track used nonces for registerValidators (it uses validatorsRegistryRoot as the nonce) and does not reject already-registered validator pubkeys, an attacker can replay the same manager signature (and any reusable keeper approvals) to call registerValidators again. This can trigger repeated deposits for the same validators, locking additional vault liquidity or bypassing the intended fundValidators path.","recommendation":"Bind registerValidators to a true monotonic nonce (e.g., validatorsManagerNonce) or track used signature/message hashes. Also reject already-registered v2 validator pubkeys (and add duplicate checks for v1) during registration to prevent replayed deposits.","evidence":"registerValidators passes validatorsRegistryRoot as the signature nonce and _isValidatorsManager does not enforce single-use of that nonce; getValidatorDeposits does not prevent re-registration of already tracked validators.","locations":["contracts/vaults/modules/VaultValidators.sol","contracts/libraries/ValidatorUtils.sol"]}]}
tokens used
64,440
{
"codex": {
"ran_at": "2026-01-11T04:57:57.314760+00:00",
"returncode": 0
},
"project": {
"mode": "source",
"file_count": 62,
"import_count": 89,
"first_party_contracts_count": 42
},
"summary": "Found 1 low-severity issue: validator-manager signatures for registerValidators are replayable, enabling duplicate validator deposits if approvals are reused.",
"raw_final": {
"summary": "Found 1 low-severity issue: validator-manager signatures for registerValidators are replayable, enabling duplicate validator deposits if approvals are reused.",
"findings": [
{
"title": "Validator-manager signature can be replayed for registerValidators, allowing duplicate validator deposits",
"category": "auth",
"evidence": "registerValidators passes validatorsRegistryRoot as the signature nonce and _isValidatorsManager does not enforce single-use of that nonce; getValidatorDeposits does not prevent re-registration of already tracked validators.",
"severity": "low",
"locations": [
"contracts/vaults/modules/VaultValidators.sol",
"contracts/libraries/ValidatorUtils.sol"
],
"recommendation": "Bind registerValidators to a true monotonic nonce (e.g., validatorsManagerNonce) or track used signature/message hashes. Also reject already-registered v2 validator pubkeys (and add duplicate checks for v1) during registration to prevent replayed deposits.",
"exploit_scenario": "A manager signature used in registerValidators is revealed on-chain. Because the vault does not track used nonces for registerValidators (it uses validatorsRegistryRoot as the nonce) and does not reject already-registered validator pubkeys, an attacker can replay the same manager signature (and any reusable keeper approvals) to call registerValidators again. This can trigger repeated deposits for the same validators, locking additional vault liquidity or bypassing the intended fundValidators path."
}
],
"no_findings": false
},
"no_findings": false,
"proxy_context": {
"proxy_run_id": "019bab3b-aa91-70e2-a7d3-704d770ca089",
"proxy_address": "0xb36fc5e542cb4fc562a624912f55da2758998113",
"implementation_run_id": "019bab3c-4a33-7098-9325-ebfbff005bf9",
"implementation_address": "0x927a83c679a5e1a6435d6bfaef7f20d4db23e2cc"
},
"schema_version": 1
}
00000000: PUSH1 0x80 00000002: PUSH1 0x40 00000004: MSTORE 00000005: PUSH32 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 00000026: SLOAD 00000027: PUSH0 00000028: SWAP1 00000029: DUP2 0000002a: SWAP1 0000002b: PUSH1 0x01 0000002d: PUSH1 0x01 0000002f: PUSH1 0xa0 00000031: SHL 00000032: SUB 00000033: AND 00000034: CALLDATASIZE 00000035: DUP3 00000036: DUP1 00000037: CALLDATACOPY 00000038: DUP2 00000039: CALLDATASIZE 0000003a: SWAP2 0000003b: GAS 0000003c: DELEGATECALL 0000003d: RETURNDATASIZE 0000003e: PUSH0 0000003f: DUP1 00000040: RETURNDATACOPY 00000041: ISZERO 00000042: PUSH1 0x48 00000044: JUMPI 00000045: RETURNDATASIZE 00000046: PUSH0 00000047: RETURN 00000048: JUMPDEST 00000049: RETURNDATASIZE 0000004a: PUSH0 0000004b: REVERT 0000004c: INVALID 0000004d: LOG2 0000004e: PUSH5 0x6970667358 00000054: UNKNOWN(0x22) 00000055: SLT 00000056: KECCAK256 00000057: MSIZE 00000058: UNKNOWN(0x0D) 00000059: UNKNOWN(0xE6) 0000005a: AND 0000005b: UNKNOWN(0x2A) 0000005c: CALL 0000005d: UNKNOWN(0xAC) 0000005e: UNKNOWN(0xA5) 0000005f: UNKNOWN(0xF7) 00000060: UNKNOWN(0xD3) 00000061: DUP1 00000062: UNKNOWN(0xE4) 00000063: MSIZE 00000064: CODECOPY 00000065: UNKNOWN(0x4C) 00000066: UNKNOWN(0xE9) 00000067: PUSH30
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| critical | upgradeability | detector | Untrusted DELEGATECALL target reachable | 70 | no | no |
viewtrace_id: 0x317a5c4c8e4afef6abdd6bde2124ea12aea89f192b90f84187f65a296956ce78
call_targets:
0x927a83c679a5e1a6435d6bfaef7f20d4db23e2cc
target_varies:
no
classification:
constant_target
validation_json{
"sink": "DELEGATECALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x317a5c4c8e4afef6abdd6bde2124ea12aea89f192b90f84187f65a296956ce78",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0x927a83c679a5e1a6435d6bfaef7f20d4db23e2cc"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0x",
"selector": "fallback",
"calldata_variants": [
"0x"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 60,
"opcode": "DELEGATECALL",
"returndata_checked": null,
"value_expression_category": "n/a",
"target_expression_category": "computed"
}
],
"guards_detected": [],
"delegatecall_count": 1,
"untrusted_delegatecall_count": 1
}
|
| high | other | detector | Untrusted CALL target/value reachable | 60 | no | no |
viewtrace_id: 0x317a5c4c8e4afef6abdd6bde2124ea12aea89f192b90f84187f65a296956ce78
call_targets:
0xb36fc5e542cb4fc562a624912f55da2758998113
target_varies:
no
classification:
constant_target
validation_json{
"sink": "CALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x317a5c4c8e4afef6abdd6bde2124ea12aea89f192b90f84187f65a296956ce78",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0xb36fc5e542cb4fc562a624912f55da2758998113"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0x",
"selector": "fallback",
"calldata_variants": [
"0x"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 92,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
}
],
"call_count": 1,
"guards_detected": [],
"untrusted_call_count": 1
}
|
| high | other | detector | ETH value transfer possible | 55 | no | no |
viewtrace_id: 0x317a5c4c8e4afef6abdd6bde2124ea12aea89f192b90f84187f65a296956ce78
call_targets:
0xb36fc5e542cb4fc562a624912f55da2758998113
target_varies:
no
classification:
constant_target
validation_json{
"sink": "CALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x317a5c4c8e4afef6abdd6bde2124ea12aea89f192b90f84187f65a296956ce78",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0xb36fc5e542cb4fc562a624912f55da2758998113"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0x",
"selector": "fallback",
"calldata_variants": [
"0x"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 92,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
}
],
"eth_value_calls": 1,
"guards_detected": []
}
|
| medium | upgradeability | cast | DELEGATECALL present | 80 | no | — |
viewevidence_json{
"notes": "delegatecall enables execution in caller context; common in proxies/upgradeable patterns",
"delegatecall_count": 1
}
|
| low | auth | codex | Validator-manager signature can be replayed for registerValidators, allowing duplicate validator deposits | 65 | no | — |
viewevidence_json{
"evidence": "registerValidators passes validatorsRegistryRoot as the signature nonce and _isValidatorsManager does not enforce single-use of that nonce; getValidatorDeposits does not prevent re-registration of already tracked validators.",
"locations": [
"contracts/vaults/modules/VaultValidators.sol",
"contracts/libraries/ValidatorUtils.sol"
],
"recommendation": "Bind registerValidators to a true monotonic nonce (e.g., validatorsManagerNonce) or track used signature/message hashes. Also reject already-registered v2 validator pubkeys (and add duplicate checks for v1) during registration to prevent replayed deposits.",
"exploit_scenario": "A manager signature used in registerValidators is revealed on-chain. Because the vault does not track used nonces for registerValidators (it uses validatorsRegistryRoot as the nonce) and does not reject already-registered validator pubkeys, an attacker can replay the same manager signature (and any reusable keeper approvals) to call registerValidators again. This can trigger repeated deposits for the same validators, locking additional vault liquidity or bypassing the intended fundValidators path."
}
|
| info | upgradeability | cast | Likely proxy-like runtime | 60 | no | — |
viewevidence_json{
"notes": "short runtime with SLOAD and DELEGATECALL resembles minimal proxy/upgradeable pattern",
"sload_count": 1,
"total_opcodes": 61,
"delegatecall_count": 1
}
|