diff --git a/.circleci/cargo.yml b/.circleci/cargo.yml new file mode 100644 index 0000000000000..32b65e6a23cc5 --- /dev/null +++ b/.circleci/cargo.yml @@ -0,0 +1,32 @@ +version: 2.1 +# +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci-web3-gamefi.yml b/.circleci/ci-web3-gamefi.yml new file mode 100644 index 0000000000000..ad53a8e498202 --- /dev/null +++ b/.circleci/ci-web3-gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.circleci/ci.yml b/.circleci/ci.yml new file mode 100644 index 0000000000000..1b5df6d6e668e --- /dev/null +++ b/.circleci/ci.yml @@ -0,0 +1,31 @@ +version: 2.1 +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test diff --git a/.circleci/ci_cargo.yml b/.circleci/ci_cargo.yml new file mode 100644 index 0000000000000..46a18d45a5fca --- /dev/null +++ b/.circleci/ci_cargo.yml @@ -0,0 +1,37 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.88.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/ci_v1.yml b/.circleci/ci_v1.yml new file mode 100644 index 0000000000000..82c6de5b42b73 --- /dev/null +++ b/.circleci/ci_v1.yml @@ -0,0 +1,31 @@ +version: 2.1 + +jobs: + build-and-test: + docker: + - image: cimg/rust:1.89.0 + steps: + - checkout + - restore_cache: + keys: + - v1-cargo-{{ checksum "Cargo.lock" }} + - v1-cargo- + - run: + name: "Check formatting" + command: cargo fmt -- --check + - run: + name: "Run tests" + command: cargo test + - save_cache: + key: v1-cargo-{{ checksum "Cargo.lock" }} + paths: + - "~/.cargo/bin" + - "~/.cargo/registry/index" + - "~/.cargo/registry/cache" + - "~/.cargo/git/db" + - "target" + +workflows: + ci: + jobs: + - build-and-test diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000000..4168efef0971f --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,32 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/reference/configuration-reference +version: 2.1 + +# Define a job to be invoked later in a workflow. +# See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#jobs-overview & https://circleci.com/docs/reference/configuration-reference/#jobs +jobs: + say-hello: + # Specify the execution environment. You can specify an image from Docker Hub or use one of our convenience images from CircleCI's Developer Hub. + # See: https://circleci.com/docs/guides/execution-managed/executor-intro/ & https://circleci.com/docs/reference/configuration-reference/#executor-job + docker: + # Specify the version you desire here + # See: https://circleci.com/developer/images/image/cimg/base + - image: cimg/base:current + + # Add steps to the job + # See: https://circleci.com/docs/guides/orchestrate/jobs-steps/#steps-overview & https://circleci.com/docs/reference/configuration-reference/#steps + steps: + # Checkout the code as the first step. + - checkout + - run: + name: "Say hello" + command: "echo Hello, World!" + +# Orchestrate jobs using workflows +# See: https://circleci.com/docs/guides/orchestrate/workflows/ & https://circleci.com/docs/reference/configuration-reference/#workflows +workflows: + say-hello-workflow: # This is the name of the workflow, feel free to change it to better match your workflow. + # Inside the workflow, you define the jobs you want to run. + jobs: + - say-hello + diff --git a/.circleci/dev_stage.yml b/.circleci/dev_stage.yml new file mode 100644 index 0000000000000..5ba351727d22b --- /dev/null +++ b/.circleci/dev_stage.yml @@ -0,0 +1,70 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- + + jobs: + my-job: + steps: + - run: echo "Hello, world!" + - run: + command: echo "This step will automatically rerun up to 3 times if it fails with a 10 second delay between attempts" + max_auto_reruns: 3 + auto_rerun_delay: 10s + + workflows: + dev_stage_pre-prod: + jobs: + - test_dev: + filters: # using regex filters requires the entire branch to match + branches: + only: # only branches matching the below regex filters will run + - dev + - /user-.*/ + - test_stage: + filters: + branches: + only: stage + - test_pre-prod: + filters: + branches: + only: /pre-prod(?:-.+)?$/ + + + build-test-deploy: + jobs: + - build: + filters: # required since `test` has tag filters AND requires `build` + tags: + only: /^config-test.*/ + - test: + requires: + - build + filters: # required since `deploy` has tag filters AND requires `test` + tags: + only: /^config-test.*/ + - deploy: + requires: + - test + filters: + tags: + only: /^config-test.*/ + branches: + ignore: /.*/ diff --git a/.circleci/web3_defi_gamefi.yml b/.circleci/web3_defi_gamefi.yml new file mode 100644 index 0000000000000..edb6605e3f101 --- /dev/null +++ b/.circleci/web3_defi_gamefi.yml @@ -0,0 +1,26 @@ +# Use the latest 2.1 version of CircleCI pipeline process engine. +# See: https://circleci.com/docs/configuration-reference + +version: 2.1 +executors: + my-custom-executor: + docker: + - image: cimg/base:stable + auth: + # ensure you have first added these secrets + # visit app.circleci.com/settings/project/github/Dargon789/foundry/environment-variables + username: $DOCKER_HUB_USER + password: $DOCKER_HUB_PASSWORD +jobs: + web3-defi-game-project-: + + executor: my-custom-executor + steps: + - checkout + - run: | + # echo Hello, World! + +workflows: + my-custom-workflow: + jobs: + - web3-defi-game-project- diff --git a/.codesandbox/tasks.json b/.codesandbox/tasks.json new file mode 100644 index 0000000000000..b34104d5de54e --- /dev/null +++ b/.codesandbox/tasks.json @@ -0,0 +1,7 @@ +{ + // These tasks will run in order when initializing your CodeSandbox project. + "setupTasks": [], + + // These tasks can be run from CodeSandbox. Running one will open a log in the app. + "tasks": {} +} diff --git a/.deps/remix-tests/remix_accounts.sol b/.deps/remix-tests/remix_accounts.sol new file mode 100644 index 0000000000000..c1c42dc96b93e --- /dev/null +++ b/.deps/remix-tests/remix_accounts.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library TestsAccounts { + function getAccount(uint index) pure public returns (address) { + address[15] memory accounts; + accounts[0] = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4; + + accounts[1] = 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2; + + accounts[2] = 0x4B20993Bc481177ec7E8f571ceCaE8A9e22C02db; + + accounts[3] = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB; + + accounts[4] = 0x617F2E2fD72FD9D5503197092aC168c91465E7f2; + + accounts[5] = 0x17F6AD8Ef982297579C203069C1DbfFE4348c372; + + accounts[6] = 0x5c6B0f7Bf3E7ce046039Bd8FABdfD3f9F5021678; + + accounts[7] = 0x03C6FcED478cBbC9a4FAB34eF9f40767739D1Ff7; + + accounts[8] = 0x1aE0EA34a72D944a8C7603FfB3eC30a6669E454C; + + accounts[9] = 0x0A098Eda01Ce92ff4A4CCb7A4fFFb5A43EBC70DC; + + accounts[10] = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c; + + accounts[11] = 0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C; + + accounts[12] = 0x4B0897b0513fdC7C541B6d9D7E929C4e5364D2dB; + + accounts[13] = 0x583031D1113aD414F02576BD6afaBfb302140225; + + accounts[14] = 0xdD870fA1b7C4700F2BD7f44238821C26f7392148; +return accounts[index]; + } +} diff --git a/.deps/remix-tests/remix_tests.sol b/.deps/remix-tests/remix_tests.sol new file mode 100644 index 0000000000000..b8b9960362203 --- /dev/null +++ b/.deps/remix-tests/remix_tests.sol @@ -0,0 +1,225 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity >=0.4.22 <0.9.0; + +library Assert { + + event AssertionEvent( + bool passed, + string message, + string methodName + ); + + event AssertionEventUint( + bool passed, + string message, + string methodName, + uint256 returned, + uint256 expected + ); + + event AssertionEventInt( + bool passed, + string message, + string methodName, + int256 returned, + int256 expected + ); + + event AssertionEventBool( + bool passed, + string message, + string methodName, + bool returned, + bool expected + ); + + event AssertionEventAddress( + bool passed, + string message, + string methodName, + address returned, + address expected + ); + + event AssertionEventBytes32( + bool passed, + string message, + string methodName, + bytes32 returned, + bytes32 expected + ); + + event AssertionEventString( + bool passed, + string message, + string methodName, + string returned, + string expected + ); + + event AssertionEventUintInt( + bool passed, + string message, + string methodName, + uint256 returned, + int256 expected + ); + + event AssertionEventIntUint( + bool passed, + string message, + string methodName, + int256 returned, + uint256 expected + ); + + function ok(bool a, string memory message) public returns (bool result) { + result = a; + emit AssertionEvent(result, message, "ok"); + } + + function equal(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventUint(result, message, "equal", a, b); + } + + function equal(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventInt(result, message, "equal", a, b); + } + + function equal(bool a, bool b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBool(result, message, "equal", a, b); + } + + // TODO: only for certain versions of solc + //function equal(fixed a, fixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function equal(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a == b); + // emit AssertionEvent(result, message); + //} + + function equal(address a, address b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventAddress(result, message, "equal", a, b); + } + + function equal(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a == b); + emit AssertionEventBytes32(result, message, "equal", a, b); + } + + function equal(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "equal", a, b); + } + + function notEqual(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventUint(result, message, "notEqual", a, b); + } + + function notEqual(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventInt(result, message, "notEqual", a, b); + } + + function notEqual(bool a, bool b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBool(result, message, "notEqual", a, b); + } + + // TODO: only for certain versions of solc + //function notEqual(fixed a, fixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + // TODO: only for certain versions of solc + //function notEqual(ufixed a, ufixed b, string message) public returns (bool result) { + // result = (a != b); + // emit AssertionEvent(result, message); + //} + + function notEqual(address a, address b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventAddress(result, message, "notEqual", a, b); + } + + function notEqual(bytes32 a, bytes32 b, string memory message) public returns (bool result) { + result = (a != b); + emit AssertionEventBytes32(result, message, "notEqual", a, b); + } + + function notEqual(string memory a, string memory b, string memory message) public returns (bool result) { + result = (keccak256(abi.encodePacked(a)) != keccak256(abi.encodePacked(b))); + emit AssertionEventString(result, message, "notEqual", a, b); + } + + /*----------------- Greater than --------------------*/ + function greaterThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventUint(result, message, "greaterThan", a, b); + } + + function greaterThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a > b); + emit AssertionEventInt(result, message, "greaterThan", a, b); + } + // TODO: safely compare between uint and int + function greaterThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative uint "a" always greater + result = true; + } else { + result = (a > uint(b)); + } + emit AssertionEventUintInt(result, message, "greaterThan", a, b); + } + function greaterThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative uint "b" always greater + result = false; + } else { + result = (uint(a) > b); + } + emit AssertionEventIntUint(result, message, "greaterThan", a, b); + } + /*----------------- Lesser than --------------------*/ + function lesserThan(uint256 a, uint256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventUint(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, int256 b, string memory message) public returns (bool result) { + result = (a < b); + emit AssertionEventInt(result, message, "lesserThan", a, b); + } + // TODO: safely compare between uint and int + function lesserThan(uint256 a, int256 b, string memory message) public returns (bool result) { + if(b < int(0)) { + // int is negative int "b" always lesser + result = false; + } else { + result = (a < uint(b)); + } + emit AssertionEventUintInt(result, message, "lesserThan", a, b); + } + + function lesserThan(int256 a, uint256 b, string memory message) public returns (bool result) { + if(a < int(0)) { + // int is negative int "a" always lesser + result = true; + } else { + result = (uint(a) < b); + } + emit AssertionEventIntUint(result, message, "lesserThan", a, b); + } +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000000..53a505774ac88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,39 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. Chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, Safari] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md new file mode 100644 index 0000000000000..48d5f81fa4229 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/custom.md @@ -0,0 +1,10 @@ +--- +name: Custom issue template +about: Describe this issue template's purpose here. +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000000..bbcbbe7d61558 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/scripts/compare-nightly.sh b/.github/scripts/compare-nightly.sh index 674cc0fe01754..c2cec964cf52d 100755 --- a/.github/scripts/compare-nightly.sh +++ b/.github/scripts/compare-nightly.sh @@ -19,7 +19,10 @@ warn = float(os.environ["WARN"]) fail = float(os.environ["FAIL"]) prev_path = os.environ.get("PREV_JSON", "") -prev = json.load(open(prev_path)) if prev_path and os.path.isfile(prev_path) else {} +prev = {} +if prev_path and os.path.isfile(prev_path): + with open(prev_path) as f: + prev = json.load(f) with open(os.environ["TODAY_JSON"]) as f: today = json.load(f) @@ -39,7 +42,10 @@ for key in all_keys: if p is None: print(f"| `{key}` | N/A | {t:.5f}s | — | 🆕 New |") continue - delta = (t - p) / p * 100 + if p == 0: + delta = float('inf') if t > 0 else 0.0 + else: + delta = (t - p) / p * 100 if delta >= fail: status = "🔴 Regression" has_regression = True diff --git a/.github/workflows/apisec-scan.yml b/.github/workflows/apisec-scan.yml new file mode 100644 index 0000000000000..e716760284792 --- /dev/null +++ b/.github/workflows/apisec-scan.yml @@ -0,0 +1,29 @@ +name: APIsec +permissions: + contents: read + +on: + pull_request: + branches: + - main + +jobs: + scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run APIsec scan + uses: apisec-inc/apisec-run-scan@025432089674a28ba8fb55f8ab06c10215e772ea + with: + apisec-username: ${{ secrets.APISEC_USERNAME }} + apisec-password: ${{ secrets.APISEC_PASSWORD }} + apisec-project: VAmPI + apisec-profile: Master + apisec-region: us-east-1 + sarif-result-file: apisec-results.sarif + apisec-email-report: true + apisec-fail-on-vuln-severity: critical + apisec-oas: false + apisec-openapi-spec-url: "https://example.com/openapi.json" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000000000..5bf742c565e0f --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,92 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL Advanced" + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + schedule: + - cron: '25 9 * * 3' + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + # Runner size impacts CodeQL analysis time. To learn more, please see: + # - https://gh.io/recommended-hardware-resources-for-running-codeql + # - https://gh.io/supported-runners-and-hardware-resources + # - https://gh.io/using-larger-runners (GitHub.com only) + # Consider using larger runners or machines with greater resources for possible analysis time improvements. + runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + permissions: + # required for all workflows + security-events: write + + # required to fetch internal or private CodeQL packs + packages: read + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + include: + - language: python + build-mode: none + # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' + # Use `c-cpp` to analyze code written in C, C++ or both + # Use 'java-kotlin' to analyze code written in Java, Kotlin or both + # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both + # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, + # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. + # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how + # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # If the analyze step fails for one of the languages you are analyzing with + # "We were unable to automatically build your code", modify the matrix above + # to set the build mode to "manual" for that language. Then modify this step + # to build your code. + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + - if: matrix.build-mode == 'manual' + shell: bash + run: | + echo 'If you are using a "manual" build mode for one or more of the' \ + 'languages you are analyzing, replace this with the commands to build' \ + 'your code, for example:' + echo ' make bootstrap' + echo ' make release' + exit 1 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000000000..1ab3e63e39815 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,27 @@ +name: Foundry Build & Deploy +permissions: + contents: read +on: + push: + branches: [main, master] +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + + - name: Build project + run: cargo build --release + + - name: Run tests + run: cargo test + + - name: Docker build + run: docker build -t foundryg-rs diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000000000..e0c9c518e8748 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,21 @@ +name: Docker Image CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:${{ github.sha }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000000000..e994f94e7085c --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,100 @@ +name: Docker + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +on: + schedule: + - cron: '21 12 * * *' + push: + branches: [ "master" ] + # Publish semver tags as releases. + tags: [ 'v*.*.*' ] + pull_request: + branches: [ "master" ] + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + + +jobs: + build: + - name: Build the Docker image + run: docker build . --file path/to/Dockerfile --tag my-image-name:$(date +%s) + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + # This is used to complete the identity challenge + # with sigstore/fulcio when running outside of PRs. + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Install the cosign tool except on PR + # https://github.com/sigstore/cosign-installer + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@59acb6260d9c0ba8f4a2f9d9b48431a222b68e20 #v3.5.0 + with: + cosign-release: 'v2.2.4' + + # Set up BuildKit Docker container builder to be able to build + # multi-platform images and export cache + # https://github.com/docker/setup-buildx-action + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + # Login against a Docker registry except on PR + # https://github.com/docker/login-action + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + # Build and push Docker image with Buildx (don't push on PR) + # https://github.com/docker/build-push-action + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@v5.0.0 + with: + context: ./ + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + # Sign the resulting Docker image digest except on PRs. + # This will only write to the public Rekor transparency log when the Docker + # repository is public to avoid leaking data. If you would like to publish + # transparency data even for private images, pass --force to cosign below. + # https://github.com/sigstore/cosign + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + # This step uses the identity token to provision an ephemeral certificate + # against the sigstore community Fulcio instance. + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} diff --git a/.github/workflows/google.yml b/.github/workflows/google.yml new file mode 100644 index 0000000000000..1295e430ca96a --- /dev/null +++ b/.github/workflows/google.yml @@ -0,0 +1,117 @@ +# This workflow will build a docker container, publish it to Google Container +# Registry, and deploy it to GKE when there is a push to the "main" +# branch. +# +# To configure this workflow: +# +# 1. Enable the following Google Cloud APIs: +# +# - Artifact Registry (artifactregistry.googleapis.com) +# - Google Kubernetes Engine (container.googleapis.com) +# - IAM Credentials API (iamcredentials.googleapis.com) +# +# You can learn more about enabling APIs at +# https://support.google.com/googleapi/answer/6158841. +# +# 2. Ensure that your repository contains the necessary configuration for your +# Google Kubernetes Engine cluster, including deployment.yml, +# kustomization.yml, service.yml, etc. +# +# 3. Create and configure a Workload Identity Provider for GitHub: +# https://github.com/google-github-actions/auth#preferred-direct-workload-identity-federation. +# +# Depending on how you authenticate, you will need to grant an IAM principal +# permissions on Google Cloud: +# +# - Artifact Registry Administrator (roles/artifactregistry.admin) +# - Kubernetes Engine Developer (roles/container.developer) +# +# You can learn more about setting IAM permissions at +# https://cloud.google.com/iam/docs/manage-access-other-resources +# +# 5. Change the values in the "env" block to match your values. + +name: 'Build and Deploy to GKE' + +on: + push: + branches: + - '"main"' + - '"master"' + +env: + PROJECT_ID: 'my-project' # TODO: update to your Google Cloud project ID + GAR_LOCATION: 'us-central1' # TODO: update to your region + GKE_CLUSTER: 'cluster-1' # TODO: update to your cluster name + GKE_ZONE: 'us-central1-c' # TODO: update to your cluster zone + DEPLOYMENT_NAME: 'gke-test' # TODO: update to your deployment name + REPOSITORY: 'samples' # TODO: update to your Artifact Registry docker repository name + IMAGE: 'static-site' + WORKLOAD_IDENTITY_PROVIDER: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' # TODO: update to your workload identity provider + +jobs: + setup-build-publish-deploy: + name: 'Setup, Build, Publish, and Deploy' + runs-on: 'ubuntu-latest' + environment: 'production' + + permissions: + contents: 'read' + id-token: 'write' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332' # actions/checkout@v4 + + # Configure Workload Identity Federation and generate an access token. + # + # See https://github.com/google-github-actions/auth for more options, + # including authenticating via a JSON credentials file. + - id: 'auth' + name: 'Authenticate to Google Cloud' + uses: 'google-github-actions/auth@f112390a2df9932162083945e46d439060d66ec2' # google-github-actions/auth@v2 + with: + workload_identity_provider: '${{ env.WORKLOAD_IDENTITY_PROVIDER }}' + + # Authenticate Docker to Google Cloud Artifact Registry + - name: 'Docker Auth' + uses: 'docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567' # docker/login-action@v3 + with: + username: 'oauth2accesstoken' + password: '${{ steps.auth.outputs.auth_token }}' + registry: '${{ env.GAR_LOCATION }}-docker.pkg.dev' + + # Get the GKE credentials so we can deploy to the cluster + - name: 'Set up GKE credentials' + uses: 'google-github-actions/get-gke-credentials@6051de21ad50fbb1767bc93c11357a49082ad116' # google-github-actions/get-gke-credentials@v2 + with: + cluster_name: '${{ env.GKE_CLUSTER }}' + location: '${{ env.GKE_ZONE }}' + + # Build the Docker image + - name: 'Build and push Docker container' + run: |- + DOCKER_TAG="${GAR_LOCATION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${IMAGE}:${GITHUB_SHA}" + + docker build \ + --tag "${DOCKER_TAG}" \ + --build-arg GITHUB_SHA="${GITHUB_SHA}" \ + --build-arg GITHUB_REF="${GITHUB_REF}" \ + . + + docker push "${DOCKER_TAG}" + + # Set up kustomize + - name: 'Set up Kustomize' + run: |- + curl -sfLo kustomize https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv5.4.3/kustomize_v5.4.3_linux_amd64.tar.gz + chmod u+x ./kustomize + + # Deploy the Docker image to the GKE cluster + - name: 'Deploy to GKE' + run: |- + # replacing the image name in the k8s template + ./kustomize edit set image LOCATION-docker.pkg.dev/PROJECT_ID/REPOSITORY/IMAGE:TAG=$GAR_LOCATION-docker.pkg.dev/$PROJECT_ID/$REPOSITORY/$IMAGE:$GITHUB_SHA + ./kustomize build . | kubectl apply -f - + kubectl rollout status deployment/$DEPLOYMENT_NAME + kubectl get services -o wide diff --git a/.github/workflows/snyk-container.yml b/.github/workflows/snyk-container.yml new file mode 100644 index 0000000000000..f07df9c75c8d1 --- /dev/null +++ b/.github/workflows/snyk-container.yml @@ -0,0 +1,55 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# +# A sample workflow which checks out the code, builds a container +# image using Docker and scans that image for vulnerabilities using +# Snyk. The results are then uploaded to GitHub Security Code Scanning +# +# For more examples, including how to limit scans to only high-severity +# issues, monitor images for newly disclosed vulnerabilities in Snyk and +# fail PR checks for new vulnerabilities, see https://github.com/snyk/actions/ + +name: Snyk Container + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '30 10 * * 1' + +permissions: + contents: read + +jobs: + snyk: + permissions: + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/upload-sarif to upload SARIF results + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v5 + - name: Build a Docker image + run: docker build -t your/image-to-test . + - name: Run Snyk to check Docker image for vulnerabilities + # Snyk can be used to break the build when it detects vulnerabilities. + # In this case we want to upload the issues to GitHub Code Scanning + continue-on-error: true + uses: snyk/actions/docker@9adf32b1121593767fc3c057af55b55db032dc04 + env: + # In order to use the Snyk Action you will need to have a Snyk API token. + # More details in https://github.com/snyk/actions#getting-your-snyk-token + # or you can signup for free at https://snyk.io/login + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + image: your/image-to-test + args: --file=Dockerfile + - name: Upload result to GitHub Code Scanning + uses: github/codeql-action/upload-sarif@v4 + with: + sarif_file: snyk.sarif diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000000..b1269653d9c6f --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "counter/lib/forge-std"] + path = counter/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "counter/lib/openzeppelin-contracts"] + path = counter/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts diff --git a/benches/src/lib.rs b/benches/src/lib.rs index 50c7afae2ddec..7ed8807cbf0f5 100644 --- a/benches/src/lib.rs +++ b/benches/src/lib.rs @@ -141,10 +141,20 @@ impl BenchmarkProject { for entry in std::fs::read_dir(&root_path)? { let entry = entry?; let path = entry.path(); - if path.is_dir() { - std::fs::remove_dir_all(&path).ok(); + // Canonicalize the entry to prevent directory traversal + let canon = match path.canonicalize() { + Ok(p) => p, + Err(_) => continue, // Skip if unable to canonicalize + }; + // Ensure canonicalized path stays strictly within root_path (TempProject root) + if !canon.starts_with(&root_path) { + sh_eprintln!("âš ī¸ Skipping suspicious path during cleanup: {:?}", canon); + continue; + } + if canon.is_dir() { + std::fs::remove_dir_all(&canon).ok(); } else { - std::fs::remove_file(&path).ok(); + std::fs::remove_file(&canon).ok(); } } diff --git a/counter/.github/workflows/test.yml b/counter/.github/workflows/test.yml new file mode 100644 index 0000000000000..34a4a527be6f9 --- /dev/null +++ b/counter/.github/workflows/test.yml @@ -0,0 +1,43 @@ +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +env: + FOUNDRY_PROFILE: ci + +jobs: + check: + strategy: + fail-fast: true + + name: Foundry project + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Install Foundry + uses: foundry-rs/foundry-toolchain@v1 + + - name: Show Forge version + run: | + forge --version + + - name: Run Forge fmt + run: | + forge fmt --check + id: fmt + + - name: Run Forge build + run: | + forge build --sizes + id: build + + - name: Run Forge tests + run: | + forge test -vvv + id: test diff --git a/counter/.gitignore b/counter/.gitignore new file mode 100644 index 0000000000000..85198aaa55b84 --- /dev/null +++ b/counter/.gitignore @@ -0,0 +1,14 @@ +# Compiler files +cache/ +out/ + +# Ignores development broadcast logs +!/broadcast +/broadcast/*/31337/ +/broadcast/**/dry-run/ + +# Docs +docs/ + +# Dotenv file +.env diff --git a/counter/README.md b/counter/README.md new file mode 100644 index 0000000000000..679a7f4518035 --- /dev/null +++ b/counter/README.md @@ -0,0 +1,66 @@ +## Foundry + +**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** + +Foundry consists of: + +- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). +- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. +- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. +- **Chisel**: Fast, utilitarian, and verbose Solidity REPL. + +## Documentation + +https://book.getfoundry.sh/ + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +```shell +$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/counter/foundry.toml b/counter/foundry.toml new file mode 100644 index 0000000000000..25b918f9c9a96 --- /dev/null +++ b/counter/foundry.toml @@ -0,0 +1,6 @@ +[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 diff --git a/counter/lib/forge-std b/counter/lib/forge-std new file mode 160000 index 0000000000000..3b20d60d14b34 --- /dev/null +++ b/counter/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 3b20d60d14b343ee4f908cb8079495c07f5e8981 diff --git a/counter/lib/openzeppelin-contracts b/counter/lib/openzeppelin-contracts new file mode 160000 index 0000000000000..ca7a4e39de086 --- /dev/null +++ b/counter/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit ca7a4e39de0860bbaadf95824207886e6de9fa64 diff --git a/counter/script/Counter.s.sol b/counter/script/Counter.s.sol new file mode 100644 index 0000000000000..cdc1fe9a1ba25 --- /dev/null +++ b/counter/script/Counter.s.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Script, console} from "forge-std/Script.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterScript is Script { + Counter public counter; + + function setUp() public {} + + function run() public { + vm.startBroadcast(); + + counter = new Counter(); + + vm.stopBroadcast(); + } +} diff --git a/counter/src/Counter.sol b/counter/src/Counter.sol new file mode 100644 index 0000000000000..aded7997b0c35 --- /dev/null +++ b/counter/src/Counter.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +contract Counter { + uint256 public number; + + function setNumber(uint256 newNumber) public { + number = newNumber; + } + + function increment() public { + number++; + } +} diff --git a/counter/test/Counter.t.sol b/counter/test/Counter.t.sol new file mode 100644 index 0000000000000..54b724f7ae766 --- /dev/null +++ b/counter/test/Counter.t.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.13; + +import {Test, console} from "forge-std/Test.sol"; +import {Counter} from "../src/Counter.sol"; + +contract CounterTest is Test { + Counter public counter; + + function setUp() public { + counter = new Counter(); + counter.setNumber(0); + } + + function test_Increment() public { + counter.increment(); + assertEq(counter.number(), 1); + } + + function testFuzz_SetNumber(uint256 x) public { + counter.setNumber(x); + assertEq(counter.number(), x); + } +} diff --git a/crates/anvil/server/src/handler.rs b/crates/anvil/server/src/handler.rs index 250c486986240..95659d9eefbf6 100644 --- a/crates/anvil/server/src/handler.rs +++ b/crates/anvil/server/src/handler.rs @@ -49,7 +49,9 @@ pub async fn handle_request( Request::Single(call) => handle_call(call, handler).await.map(Response::Single), Request::Batch(calls) => { if calls.is_empty() { - return Some(Response::error(RpcError::invalid_request())); + return Some(Response::Batch(vec![anvil_rpc::response::RpcResponse::from( + RpcError::invalid_request(), + )])); } future::join_all(calls.into_iter().map(move |call| handle_call(call, handler.clone()))) .map(responses_as_batch) diff --git a/crates/cast/tests/cli/main.rs b/crates/cast/tests/cli/main.rs index 2f744efe4d4f0..9071614e3a7a9 100644 --- a/crates/cast/tests/cli/main.rs +++ b/crates/cast/tests/cli/main.rs @@ -3158,6 +3158,34 @@ Traces: "#]]); }); +// tests that displays a sample beacon block traces in Cancun +// https://github.com/foundry-rs/foundry/issues/12435 +casttest!(test_beacon_block_root_in_cancun, |prj, cmd| { + prj.clear(); + let eth_rpc_url = next_http_rpc_endpoint(); + cmd.args([ + "run", + "0xae290fe8c89c3e83dff20eeb2b8e3261bcdce0d66441c7056918dfb5fafe6d96", + "--rpc-url", + eth_rpc_url.as_str(), + ]) + .assert_success() + .stdout_eq(str![[r#" +Executing previous transactions from the block. +Traces: + [45054] 0xB731392c0EB5BF2092f9f7B520DA551f70Ea9131::Claim{value: 46698476594582387}() + ├─ [4320] 0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02::00000000(00000000000000000000000000000000000000000000000069091d4b) [staticcall] + │ └─ ← [Return] 0x70c7855161ec07af782df915fb3e81702df40f34972da3d740cdfc132ac926f6 + ├─ emit NvStuck(param0: 0x6e6C36B970f8862bA3F148DEdAB8F98f5ed8b426, param1: 46698476594582387 [4.669e16], param2: 1762205003 [1.762e9]) + └─ ← [Stop] + + +Transaction successfully executed. +[GAS] + +"#]]); +}); + // tests that displays a sample contract artifact // casttest!(flaky_fetch_artifact_from_etherscan, |_prj, cmd| { diff --git a/crates/cli/src/utils/suggestions.rs b/crates/cli/src/utils/suggestions.rs index a675ccae963c9..82a14a3b24beb 100644 --- a/crates/cli/src/utils/suggestions.rs +++ b/crates/cli/src/utils/suggestions.rs @@ -1,4 +1,5 @@ //! Helper functions for suggesting alternative values for a possibly erroneous user input. +use std::cmp::Ordering; /// Filters multiple strings from a given list of possible values which are similar /// to the passed in value `v` within a certain confidence by least confidence. diff --git a/crates/common/src/tempo/keystore.rs b/crates/common/src/tempo/keystore.rs index b4f9527d1b106..1da08ec5676ad 100644 --- a/crates/common/src/tempo/keystore.rs +++ b/crates/common/src/tempo/keystore.rs @@ -108,10 +108,29 @@ pub(crate) fn test_env_mutex() -> &'static tokio::sync::Mutex<()> { /// Resolve the Tempo home directory. /// -/// Uses `TEMPO_HOME` env var if set, otherwise `~/.tempo`. +/// Uses `TEMPO_HOME` env var if set (as a single relative directory name under +/// the user's home), otherwise `~/.tempo`. pub fn tempo_home() -> Option { if let Ok(home) = env::var(TEMPO_HOME_ENV) { - return Some(PathBuf::from(home)); + let candidate = home.trim(); + let p = PathBuf::from(candidate); + + // Accept only a single safe path component to avoid traversal/absolute paths. + if !candidate.is_empty() + && !candidate.contains("..") + && !candidate.contains('/') + && !candidate.contains('\\') + && !p.is_absolute() + && p.components().count() == 1 + { + return dirs::home_dir().map(|h| h.join(p)); + } + + tracing::warn!( + %candidate, + "{} is invalid; falling back to default", + TEMPO_HOME_ENV + ); } dirs::home_dir().map(|h| h.join(DEFAULT_TEMPO_HOME)) } diff --git a/crates/doc/src/parser/comment.rs b/crates/doc/src/parser/comment.rs index 42b91ccc366aa..e70f47e174a81 100644 --- a/crates/doc/src/parser/comment.rs +++ b/crates/doc/src/parser/comment.rs @@ -205,6 +205,12 @@ impl Comments { } } +impl From> for Comments { + fn from(value: Vec) -> Self { + Self(value) + } +} + /// The collection of references to natspec [Comment] items. #[derive(Debug, Default, PartialEq, Eq, Deref)] pub struct CommentsRef<'a>(Vec<&'a Comment>); diff --git a/crates/forge/Cargo.toml b/crates/forge/Cargo.toml index 667da6b442ca1..4e83eb168abca 100644 --- a/crates/forge/Cargo.toml +++ b/crates/forge/Cargo.toml @@ -62,6 +62,7 @@ alloy-primitives = { workspace = true, features = ["serde"] } alloy-provider = { workspace = true, features = ["reqwest", "ws", "ipc"] } alloy-signer.workspace = true alloy-transport.workspace = true +alloy-hardforks.workspace = true tempo-alloy.workspace = true diff --git a/crates/forge/tests/cli/test_optimizer.rs b/crates/forge/tests/cli/test_optimizer.rs index c744ff6457596..223549b08b048 100644 --- a/crates/forge/tests/cli/test_optimizer.rs +++ b/crates/forge/tests/cli/test_optimizer.rs @@ -1374,7 +1374,6 @@ Compiling 21 files with [..] }); // Test preprocessed contracts with decode internal fns. -#[cfg(not(feature = "isolate-by-default"))] forgetest_init!(preprocess_contract_with_decode_internal, |prj, cmd| { prj.initialize_default_contracts(); prj.update_config(|config| { diff --git a/crates/lint/src/linter.rs b/crates/lint/src/linter.rs new file mode 100644 index 0000000000000..2c11e0222a286 --- /dev/null +++ b/crates/lint/src/linter.rs @@ -0,0 +1,129 @@ +use foundry_compilers::Language; +use foundry_config::lint::Severity; +use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition}; +use solar_interface::{ + data_structures::Never, + diagnostics::{DiagBuilder, DiagId, MultiSpan}, + Session, Span, +}; +use std::{ops::ControlFlow, path::PathBuf}; + +/// Trait representing a generic linter for analyzing and reporting issues in smart contract source +/// code files. A linter can be implemented for any smart contract language supported by Foundry. +/// +/// # Type Parameters +/// +/// - `Language`: Represents the target programming language. Must implement the [`Language`] trait. +/// - `Lint`: Represents the types of lints performed by the linter. Must implement the [`Lint`] +/// trait. +/// +/// # Required Methods +/// +/// - `lint`: Scans the provided source files emitting a daignostic for lints found. +pub trait Linter: Send + Sync + Clone { + type Language: Language; + type Lint: Lint; + + fn lint(&self, input: &[PathBuf]); +} + +pub trait Lint { + fn id(&self) -> &'static str; + fn severity(&self) -> Severity; + fn description(&self) -> &'static str; + fn help(&self) -> &'static str; +} + +pub struct LintContext<'s> { + sess: &'s Session, + desc: bool, +} + +impl<'s> LintContext<'s> { + pub fn new(sess: &'s Session, with_description: bool) -> Self { + Self { sess, desc: with_description } + } + + // Helper method to emit diagnostics easily from passes + pub fn emit(&self, lint: &'static L, span: Span) { + let desc = if self.desc { lint.description() } else { "" }; + let diag: DiagBuilder<'_, ()> = self + .sess + .dcx + .diag(lint.severity().into(), desc) + .code(DiagId::new_str(lint.id())) + .span(MultiSpan::from_span(span)) + .help(lint.help()); + + diag.emit(); + } +} + +/// Trait for lints that operate directly on the AST. +/// Its methods mirror `solar_ast::visit::Visit`, with the addition of `LintCotext`. +pub trait EarlyLintPass<'ast>: Send + Sync { + fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {} + fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {} + fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {} + fn check_variable_definition( + &mut self, + _ctx: &LintContext<'_>, + _var: &'ast VariableDefinition<'ast>, + ) { + } + + // TODO: Add methods for each required AST node type +} + +/// Visitor struct for `EarlyLintPass`es +pub struct EarlyLintVisitor<'a, 's, 'ast> { + pub ctx: &'a LintContext<'s>, + pub passes: &'a mut [Box + 's>], +} + +impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast> +where + 's: 'ast, +{ + type BreakValue = Never; + + fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_expr(self.ctx, expr) + } + self.walk_expr(expr) + } + + fn visit_variable_definition( + &mut self, + var: &'ast VariableDefinition<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_variable_definition(self.ctx, var) + } + self.walk_variable_definition(var) + } + + fn visit_item_struct( + &mut self, + strukt: &'ast ItemStruct<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_struct(self.ctx, strukt) + } + self.walk_item_struct(strukt) + } + + fn visit_item_function( + &mut self, + func: &'ast ItemFunction<'ast>, + ) -> ControlFlow { + for pass in self.passes.iter_mut() { + pass.check_item_function(self.ctx, func) + } + self.walk_item_function(func) + } + + // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method + // sigs + adding `LintContext` +} diff --git a/crates/test-utils/src/script.rs b/crates/test-utils/src/script.rs index 06f83886d0fd2..4e318b7fc5569 100644 --- a/crates/test-utils/src/script.rs +++ b/crates/test-utils/src/script.rs @@ -123,6 +123,18 @@ impl ScriptTester { for entry in fs::read_dir(&from_dir)? { let file = &entry?.path(); let name = file.file_name().unwrap(); + // Validate file name to avoid path traversal and absolute paths + let name_str = name.to_string_lossy(); + if name_str.contains("..") || name_str.contains("/") || name_str.contains("\\") { + // Skip invalid (potentially dangerous) file names + continue; + } + // Optionally verify canonicalized file is in from_dir to avoid symlink traversal + if let Ok(canonical_file) = file.canonicalize() { + if !canonical_file.starts_with(&from_dir) { + continue; + } + } fs::copy(file, to_dir.join(name))?; } Ok(()) diff --git a/npm/package.json b/npm/package.json index bac357038a1ea..d596a7930b609 100644 --- a/npm/package.json +++ b/npm/package.json @@ -8,9 +8,9 @@ }, "dependencies": { "@types/bun": "^1.3.1", - "@types/node": "^25.5.2", + "@types/node": "^25.0.2", "bun": "^1.3.1", - "typescript": "^6.0.2" + "typescript": "^5.9.3" }, "license": "MIT OR Apache-2.0", "$schema": "https://json.schemastore.org/package.json"