CyberConnect - Audit

CertiKFiles named in the report

CertiK reviewed Cyber (CyberConnect - Audit) on 5 Sep 2023: 25 findings, 0 critical; 7 marked fixed, 18 open.

No open critical findings
Open the reportOpen Cyber’s security record

Report hosted on skynet.certik.com.

Trustblock does not review code.

Findings25

Statuses are the security firm’s own, from its report.

SeverityOpenAcknowledgedFixed
Critical000
High500
Medium004
Low1303
MediumIncomplete Signature ValidationFixed

The function `_requiresExpectedSigner()` is used to check if `expectedSigner` is the correct signer, which will be called by the function `preProcess()` from the contract `PermissionMw`. ```solidity function _requiresExpectedSigner( bytes32 digest, address expectedSigner, uint8 v, bytes32 r, bytes32 s, uint256 deadline ) internal view { require(deadline >= block.timestamp, "DEADLINE_EXCEEDED"); require( uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "INVALID_SIGNATURE_S_VAULE" ); address recoveredAddress = ecrecover(digest, v, r, s); require(recoveredAddress == expectedSigner, "INVALID_SIGNATURE"); } ``` If `ecrecover()` fails to recover an address, a zero address will be returned, which is not checked in `_requiresExpectedSigner()`. If the role `NameRegistry` assign a zero address to the signer through the function `setMwData()`, an invalid signature could bypass the permission check of the function `preProcess()` in the middleware `PermissionMw`. ```solidity //file src/middlewares/mocaid/PermissionMw.sol function setMwData(bytes calldata data) external override onlyNameRegistry { signer = abi.decode(data, (address)); } function preProcess( DataTypes.RegisterNameParams calldata params, bytes calldata data ) external payable override onlyNameRegistry { DataTypes.EIP712Signature memory sig; (sig.v, sig.r, sig.s, sig.deadline) = abi.decode( data, (uint8, bytes32, bytes32, uint256) ); _requiresExpectedSigner( _hashTypedDataV4( keccak256( abi.encode( Constants._REGISTER_TYPEHASH, keccak256(bytes(params.name)), params.parentNode, params.to, nonces[params.to]++, sig.deadline ) ) ), signer, sig.v, sig.r, sig.s, sig.deadline ); } ``` References: - https://docs.soliditylang.org/en/v0.8.9/units-and-global-variables.html?highlight=ecrecover#mathematical-and-cryptographic-functions

MediumMissing Validation on the Return Values of `usdOrFixed

In the contract `StableFeeMiddleware`, the function `usdOracle.getRoundData()` returns the token's Oracle price. According to the documentation, it must validate the returned **timestamps**, **round ID**, and **price** to ensure the price is valid. ```solidity function _getPriceAt(uint80 roundId) internal view returns (int256) { // prettier-ignore ( /* uint80 roundID */, int price, /*uint startedAt*/, /*uint timeStamp*/, /*uint80 answeredInRound*/ ) = usdOracle.getRoundData(roundId); return price; } ``` Moreover, The function **_getPrice()** is using `latestRoundData()` to get the token's price by Chainlink, but there are no validations that the data is not stale. Reference: - https://docs.chain.link/data-feeds/historical-data#getrounddata-return-values

MediumMoca xp set up validation can be bypassedFixed

The value for moca xp can be updated via `setMocaXP` with a limitation of `_MAX_XP` by the operator. ```solidity=374 function setMocaXP(uint256 tokenId, uint256 xp) external { require(xp <= _MAX_XP, "XP_TOO_BIG"); DataTypes.MetadataPair[] memory pairs = new DataTypes.MetadataPair[](1); pairs[0] = DataTypes.MetadataPair(_MOCA_XP_KEY, xp.toString()); batchSetGatedMetadatas(tokenId, pairs); emit MocaXPSet(tokenId, xp); } ``` However, since `batchSetGatedMetadatas` is external function, the operator can invoke `batchSetGatedMetadatas` directly to bypass the validation on moca xp value.

MediumPotential Manipulation on Registration costFixed

When register cyber ID, there is a pre-register process when the middleware of the contract is not address(0) ```solidity=177 function register( string calldata cid, address to, bytes32 secret, uint8 durationYear, bytes calldata middlewareData ) external payable { ... uint256 cost; if (middleware != address(0)) { cost = ICyberIdMiddleware(middleware).preRegister{ value: msg.value }( DataTypes.RegisterCyberIdParams( msg.sender, cid, to, durationYear ), middlewareData ); } _register(cid, to, durationYear, cost); } ``` During the preregister process in stable fee middleware, it will query the fee and calculate the cost: ```solidity=83 function preRegister( DataTypes.RegisterCyberIdParams calldata params, bytes calldata data ) external payable override returns (uint256) { uint80 roundId = abi.decode(data, (uint80)); uint256 cost = getPriceWeiAt(params.cid, roundId, params.durationYear); _chargeAndRefundOverPayment(cost, params.msgSender); return cost; } ``` However, the `roundID` is based on the `middlewareData`, which is user's input. As a result, the user has the chance to select the price for the registration.

LowMissing Access RestrictionFixed

The following function are missing access control in `StableFeeMiddleware`: - `preRegister` - `preRenew` - `preBid`

LowMultiple ways for owner update in `TrustOnlyMiddlFixed

In contract `TrustOnlyMiddleware`, the `_owner` can be updated by multiple parties 1. via `setMwData`(by NameRegistry) 2. via `transferOwnership`(by current owner).

LowPotential Stuck Tokens When `middleware` is `addrFixed

The function `register()` is intended to register a new cid and ultimately calls the function `preRegister()` from `middleware`, which will charge the required native tokens. ```solidity function register( string calldata cid, address to, bytes32 secret, uint8 durationYear, bytes calldata middlewareData ) external payable { //... uint256 cost; if (middleware != address(0)) { cost = ICyberIdMiddleware(middleware).preRegister{ value: msg.value }( DataTypes.RegisterCyberIdParams( msg.sender, cid, to, durationYear ), middlewareData ); } _register(cid, to, durationYear, cost); } ``` However, if the middleware is zero address, the function `preBid()` won't be called and no cost will be calculated. As a result, the receiving token `msg.value` will be locked in the contract permanently. Moreover, the same thing will happen in other functions `renew()` and `bid()`.

Contracts in scope

No deployed address is recorded for this audit.

The report names a repository or files. Open the report to see its scope.

Repository and files the report names (34)

  • src/base/CyberNFT1155.sol
  • src/periphery/TokenReceiver.sol
  • src/base/CyberNFT721.sol
  • src/middlewares/mocaid/PermissionMw.sol
  • src/core/Content.sol
  • src/core/Subscribe.sol
  • src/libraries/DataTypes.sol
  • src/base/MetadataResolver.sol
  • src/middlewares/PermissionMw.sol
  • src/core/MiddlewareManager.sol
  • src/middlewares/base/OnlyEngineMw.sol
  • src/deployer/Deployer.sol
  • src/factory/CyberAccountFactory.sol
  • src/dependencies/solmate/Owned.sol
  • src/core/W3st.sol
  • src/core/Essence.sol
  • src/middlewares/LimitedTimePaidMw.sol
  • src/middlewares/cyberid/StableFeeMiddleware.sol
  • src/core/CyberEngine.sol
  • src/libraries/Constants.sol
  • src/dependencies/solmate/SBTERC721.sol
  • src/libraries/LibString.sol
  • src/dependencies/solmate/ERC1155.sol
  • src/core/MocaId.sol
  • src/deployer/SubscribeDeployer.sol
  • src/base/EIP712.sol
  • src/middlewares/base/FeeMw.sol
  • src/core/CyberId.sol
  • src/middlewares/cyberid/base/LowerCaseCyberIdMiddleware.sol
  • src/middlewares/base/Treasury.sol
  • src/middlewares/cyberid/TrustOnlyMiddleware.sol
  • src/core/Soul.sol
  • src/dependencies/solmate/ERC721.sol
  • src/deployer/Create2Deployer.sol

Part of Cyber’s security record

Cyber
Audits7 audits by 2 firmsLast audit Aug 2024, by HashEx.
Critical findingsNo open critical findingsAcross 7 reports.
Open Cyber’s security record

Security firm

CertiK

1,045 projects reviewed · 1,376 reports · latest May 2025

See CertiK’s audits

Something wrong on this page? A missing report, a wrong date, a finding that was fixed since. Tell us and we check it against its source.

Tell us