Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,30 @@
[submodule "oracles/twap/lib/prb-math"]
path = oracles/twap/lib/prb-math
url = https://github.com/PaulRBerg/prb-math
[submodule "sandbox/lib/forge-std"]
path = sandbox/lib/forge-std
[submodule "sandbox/bridge/lib/forge-std"]
path = sandbox/bridge/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "tokens/olympus-dao/lib/openzeppelin-contracts"]
path = tokens/olympus-dao/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "tokens/olympus-dao/lib/forge-std"]
path = tokens/olympus-dao/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "sandbox/lib/openzeppelin-contracts"]
path = sandbox/lib/openzeppelin-contracts
[submodule "sandbox/bridge/lib/openzeppelin-contracts"]
path = sandbox/bridge/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "bridges/nomad/lib/forge-std"]
path = bridges/nomad/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "sandbox/token-access-control/lib/openzeppelin-contracts"]
path = sandbox/token-access-control/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "sandbox/token-access-control/lib/forge-std"]
path = sandbox/token-access-control/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "sandbox/token-supply/lib/openzeppelin-contracts"]
path = sandbox/token-supply/lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
[submodule "sandbox/token-supply/lib/forge-std"]
path = sandbox/token-supply/lib/forge-std
url = https://github.com/foundry-rs/forge-std
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
17 changes: 17 additions & 0 deletions sandbox/token-access-control/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

cache/
out/
21 changes: 21 additions & 0 deletions sandbox/token-access-control/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Drosera

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
9 changes: 9 additions & 0 deletions sandbox/token-access-control/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Token Access Control Example

- `TokenAccessControlTrap` monitors the list of addresses with the token minter role and checks for any changes. It is used in case of an access control exploit.

## Running the Examples

```bash
forge test
```
8 changes: 8 additions & 0 deletions sandbox/token-access-control/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
[rpc_endpoints]
mainnet = "https://eth.llamarpc.com"
1 change: 1 addition & 0 deletions sandbox/token-access-control/lib/forge-std
Submodule forge-std added at 07263d
1 change: 1 addition & 0 deletions sandbox/token-access-control/lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at dbb610
3 changes: 3 additions & 0 deletions sandbox/token-access-control/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
41 changes: 41 additions & 0 deletions sandbox/token-access-control/src/MockERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {AccessControlEnumerable} from "@openzeppelin/contracts/access/extensions/AccessControlEnumerable.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20, AccessControlEnumerable {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

constructor() ERC20("MyToken", "TKN") {
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
_grantRole(MINTER_ROLE, _msgSender());
}

function mint(address to, uint256 amount) public {
require(
hasRole(MINTER_ROLE, _msgSender()),
"MockERC20: must have minter role to mint"
);
_mint(to, amount);
}

function grantMinter(address account) public {
// vulnerable implementation
_grantRole(MINTER_ROLE, account);
}

function getRoleAddresses(
bytes32 role
) public view returns (address[] memory) {
uint256 count = getRoleMemberCount(role);
address[] memory addresses = new address[](count);
for (uint256 i = 0; i < count; ++i) {
address account = getRoleMember(role, i);
if (hasRole(role, account)) {
addresses[i] = account;
}
}
return addresses;
}
}
63 changes: 63 additions & 0 deletions sandbox/token-access-control/src/TokenAccessControlTrap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;
import {MockERC20} from "./MockERC20.sol";
import {console} from "forge-std/console.sol";

contract TokenAccessControlTrap {
MockERC20 public mockToken;

struct CustomCollectStruct {
address[] minterAddresses;
}

constructor(address _mockToken) {
mockToken = MockERC20(_mockToken);
}

function collect() external view returns (CustomCollectStruct memory) {
address[] memory minterAddresses = mockToken.getRoleAddresses(
mockToken.MINTER_ROLE()
);
return CustomCollectStruct({minterAddresses: minterAddresses});
}

function isValid(
CustomCollectStruct[] calldata dataPoints
) external pure returns (bool) {
if (dataPoints.length < 2) {
revert("DataPoints length should be at least 2");
}

for (uint256 i = 1; i < dataPoints.length; i++) {
address[] memory oldList = dataPoints[i - 1].minterAddresses;
address[] memory newList = dataPoints[i].minterAddresses;

console.log("oldListLength: ", oldList.length);
console.log("newListLength: ", newList.length);

if (oldList.length != newList.length) {
console.log("Address length mismatch detected!");
return false;
}

bool[] memory found = new bool[](oldList.length);

for (uint256 j = 0; j < oldList.length; j++) {
bool matchFound = false;
for (uint256 k = 0; k < newList.length; k++) {
if (oldList[j] == newList[k] && !found[k]) {
found[k] = true;
matchFound = true;
break;
}
}
if (!matchFound) {
console.log("Address mismatch detected!");
return false;
}
}
}
console.log("No change.");
return true;
}
}
41 changes: 41 additions & 0 deletions sandbox/token-access-control/test/MockERC20.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import "forge-std/Test.sol";
import "../src/MockERC20.sol";
import "forge-std/console.sol";

contract MockERC20Test is Test {
MockERC20 public token;
address public minter;
address public user;

function setUp() public {
minter = address(this);
user = address(0x123);
token = new MockERC20();
}

function testMinterRole() public view {
assertTrue(token.hasRole(token.MINTER_ROLE(), minter));
}

function testMint() public {
uint256 mintAmount = 1000 * 10 ** 18;
token.mint(user, mintAmount);
assertEq(token.balanceOf(user), mintAmount);
}

function testMintFailWithoutRole() public {
uint256 mintAmount = 1000 * 10 ** 18;
vm.prank(user);
vm.expectRevert("MockERC20: must have minter role to mint");
token.mint(user, mintAmount);
}

function testMinterAddresses() public view {
address[] memory minterAddresses = token.getRoleAddresses(token.MINTER_ROLE());
assertEq(minterAddresses.length, 1);
assertEq(minterAddresses[0], minter);
}
}
73 changes: 73 additions & 0 deletions sandbox/token-access-control/test/TokenAccessControlTrap.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";
import {TokenAccessControlTrap} from "../src/TokenAccessControlTrap.sol";
import {MockERC20} from "../src/MockERC20.sol";

/// @dev forge test --match-contract LocalTrapTest -vv
contract TokenAccessControlTrapTest is Test {
MockERC20 public mockToken;
// Trap Config lives on chain while the Trap itself lives off chain
address public trapConfig = address(0xDEADBEEF);
address public user;
address public anotherUser;

function setUp() public {
mockToken = new MockERC20();
user = address(0x123);
anotherUser = address(0x456);
}

function test_TokenAccessControlTrap() external {
TokenAccessControlTrap.CustomCollectStruct[]
memory data = new TokenAccessControlTrap.CustomCollectStruct[](2);
address tokenAccessControlTrap = address(
new TokenAccessControlTrap(address(mockToken))
);

data[0] = TokenAccessControlTrap(tokenAccessControlTrap).collect();
data[1] = data[0];
bool isValid = TokenAccessControlTrap(tokenAccessControlTrap).isValid(
data
);
assert(isValid);

// Perform exploit, add unknown minter
vm.prank(user);
mockToken.grantMinter(user);

TokenAccessControlTrap.CustomCollectStruct[]
memory newData = new TokenAccessControlTrap.CustomCollectStruct[](
2
);

newData[0] = data[0];
newData[1] = TokenAccessControlTrap(tokenAccessControlTrap).collect();
isValid = TokenAccessControlTrap(tokenAccessControlTrap).isValid(
newData
);

// Reset and add another minter
vm.prank(address(this));
mockToken.revokeRole(mockToken.MINTER_ROLE(), user);

vm.prank(anotherUser);
mockToken.grantMinter(anotherUser);

TokenAccessControlTrap.CustomCollectStruct[]
memory sameLengthDifferentContent = new TokenAccessControlTrap.CustomCollectStruct[](
2
);
sameLengthDifferentContent[0] = newData[1];
sameLengthDifferentContent[1] = TokenAccessControlTrap(
tokenAccessControlTrap
).collect();

isValid = TokenAccessControlTrap(tokenAccessControlTrap).isValid(
sameLengthDifferentContent
);

assert(!isValid);
}
}
17 changes: 17 additions & 0 deletions sandbox/token-supply/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/

# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
Cargo.lock

# These are backup files generated by rustfmt
**/*.rs.bk

# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb

cache/
out/
21 changes: 21 additions & 0 deletions sandbox/token-supply/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Drosera

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
9 changes: 9 additions & 0 deletions sandbox/token-supply/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Token Access Control Example

- `TokenSupplyTrapTest` monitors total token supply and checks if it is largely changed. It is used in case of mint abuse exploit.

## Running the Examples

```bash
forge test
```
8 changes: 8 additions & 0 deletions sandbox/token-supply/foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
[rpc_endpoints]
mainnet = "https://eth.llamarpc.com"
1 change: 1 addition & 0 deletions sandbox/token-supply/lib/forge-std
Submodule forge-std added at 07263d
1 change: 1 addition & 0 deletions sandbox/token-supply/lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at dbb610
3 changes: 3 additions & 0 deletions sandbox/token-supply/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
ds-test/=lib/forge-std/lib/ds-test/src/
forge-std/=lib/forge-std/src/
Loading