diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..9c7500c --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,3 @@ +reviews: + path_filters: + - "!audit/**" diff --git a/.gitignore b/.gitignore index d38617e..4310328 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ result -.env \ No newline at end of file +.env +.fixes \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0cbe161 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,71 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What is Rainix + +Rainix is a Nix flake that provides development environments and build tasks for the Rain Protocol ecosystem. It's a shared infrastructure flake consumed by other Rain repos — the actual project code lives in downstream consumers. The `test/fixture/` directory contains example contracts/crates used for CI validation of the flake itself. + +## Development Environment + +Requires Nix with flakes enabled. Enter the dev shell before working: + +``` +nix develop # default shell (Solidity + Rust + Node + subgraph tools) +nix develop .#tauri-shell # Tauri desktop app development (macOS-specific quirks) +``` + +The shell auto-sources `.env` if present and runs `npm ci --ignore-scripts` if `package.json` exists. + +## Build Tasks + +All tasks are Nix packages run via `nix run`. From a consuming repo (or `test/fixture/`): + +### Solidity +- `nix run ..#rainix-sol-prelude` — `forge install && forge build` +- `nix run ..#rainix-sol-test` — `forge test -vvv` +- `nix run ..#rainix-sol-static` — `slither . && forge fmt --check` +- `nix run ..#rainix-sol-legal` — `reuse lint` (REUSE/DCL-1.0 license compliance) +- `nix run ..#rainix-sol-artifacts` — deploy to testnet via `script/Deploy.sol` + +### Rust +- `nix run ..#rainix-rs-prelude` — (currently no-op, placeholder for env prep) +- `nix run ..#rainix-rs-test` — `cargo test` +- `nix run ..#rainix-rs-static` — `cargo fmt --all -- --check && cargo clippy --all-targets --all-features -- -D clippy::all` +- `nix run ..#rainix-rs-artifacts` — `cargo build --release` + +### Subgraph +- `subgraph-build` — forge build + npm ci + graph codegen/build +- `subgraph-test` — `docker compose up` in `./subgraph` +- `subgraph-deploy` — requires `GOLDSKY_TOKEN` and `GOLDSKY_NAME_AND_VERSION` + +## Pinned Versions + +- Rust: 1.94.0 (with `wasm32-unknown-unknown` target) +- Solidity: solc 0.8.19 +- Foundry: via foundry.nix +- Graph CLI: 0.69.2 +- Goldsky CLI: 8.6.6 +- wasm-bindgen-cli: 0.2.100 + +## Architecture + +The flake exports: +- **`packages`**: All `rainix-*` task derivations plus `tauri-release-env` +- **`devShells`**: `default` (full toolchain) and `tauri-shell` (Tauri + macOS workarounds) +- **Reusable outputs**: `pkgs`, `rust-toolchain`, `rust-build-inputs`, `sol-build-inputs`, `node-build-inputs`, `mkTask`, `network-list` — consumed by downstream Rain flakes to compose their own tasks/shells + +`mkTask` is the core abstraction: it creates self-contained Nix derivations wrapping shell scripts with their dependencies on `PATH`. + +## CI + +Defined in `.github/workflows/`: +- **test.yml** — runs sol and rs tasks against `test/fixture/` on Ubuntu + macOS (ARM only; sol tasks Ubuntu-only) +- **check-shell.yml** — verifies dev shell tools are available +- **pr-assessment.yaml** — PR size assessment + +## Code Style + +- Rust: `cargo fmt` and `cargo clippy` with all warnings denied (`-D clippy::all`) +- Solidity: `forge fmt` and `slither` static analysis +- License: DecentraLicense 1.0, enforced via `reuse lint` diff --git a/REUSE.toml b/REUSE.toml index 6a47568..5ad18c7 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -7,9 +7,12 @@ path = [ ".gitignore", ".gitmodules", "REUSE.toml", + "CLAUDE.md", + ".coderabbit.yaml", ".github/**/", ".vscode/**/", - "test/**/" + "test/**/", + "audit/**/" ] SPDX-FileCopyrightText = "Copyright (c) 2020 thedavidmeister" SPDX-License-Identifier = "LicenseRef-DCL-1.0" diff --git a/audit/2026-03-12-01/pass0/process.md b/audit/2026-03-12-01/pass0/process.md new file mode 100644 index 0000000..b2052b8 --- /dev/null +++ b/audit/2026-03-12-01/pass0/process.md @@ -0,0 +1,42 @@ +# Pass 0: Process Review + +## Evidence of thorough reading + +### CLAUDE.md +- Sections: What is Rainix, Development Environment, Build Tasks (Solidity, Rust, Subgraph), Pinned Versions, Architecture, CI, Code Style +- Line 7: project description +- Lines 14-15: dev shell commands +- Lines 18: shell auto-source behavior +- Lines 25-29: Solidity tasks +- Lines 32-35: Rust tasks +- Lines 38-40: Subgraph tasks +- Lines 44-49: Pinned versions +- Lines 53-58: Architecture description +- Lines 62-65: CI workflows +- Lines 69-71: Code style + +## Findings + +### A01-1 [LOW] Inaccurate CI platform description + +**File:** CLAUDE.md:63 + +The CLAUDE.md states test.yml runs on "Ubuntu + macOS (Intel & ARM)". However, test.yml only uses `ubuntu-latest` and `macos-latest` — it does not include `macos-13` (Intel). The Intel + Apple Silicon matrix is only in `check-shell.yml`. A future session relying on this could incorrectly assume Rust tests run on Intel macOS. + +### A01-2 [LOW] Task path prefix varies by working directory but documentation uses single prefix + +**File:** CLAUDE.md:22-29 + +Tasks are documented as `nix run ..#rainix-sol-test` (single `..`), which is correct from a direct consumer repo. But from `test/fixture/` (the only runnable location in this repo), the actual invocation is `nix run ../..#rainix-sol-test` (double `../..`), as confirmed in `.github/workflows/test.yml:50`. The note "From a consuming repo (or `test/fixture/`)" doesn't clarify that the path prefix differs. + +### A01-3 [LOW] Subgraph tasks not runnable via `nix run` + +**File:** CLAUDE.md:38-40 + +Subgraph tasks (`subgraph-build`, `subgraph-test`, `subgraph-deploy`) are listed under the "Build Tasks" section that opens with "All tasks are Nix packages run via `nix run`." However, these tasks are not exported in `packages` — they're only available on `PATH` inside the dev shell. A future session could attempt `nix run ..#subgraph-build` and fail. + +### A01-4 [INFO] Required environment variables for artifact deployment not documented + +**File:** CLAUDE.md:29 + +`rainix-sol-artifacts` requires `DEPLOYMENT_KEY`, `ETH_RPC_URL`, and optionally `ETHERSCAN_API_KEY`, `DEPLOY_BROADCAST`, `DEPLOY_VERIFY`, `DEPLOY_VERIFIER`, `DEPLOY_VERIFIER_URL`, `DEPLOY_LEGACY`. None are mentioned. Similarly, subgraph-deploy requires `GOLDSKY_TOKEN` and `GOLDSKY_NAME_AND_VERSION` (this one is documented). A future session attempting deployment would need to discover these from the flake source. diff --git a/audit/2026-03-12-01/pass1/Counter.md b/audit/2026-03-12-01/pass1/Counter.md new file mode 100644 index 0000000..1790c70 --- /dev/null +++ b/audit/2026-03-12-01/pass1/Counter.md @@ -0,0 +1,17 @@ +# Pass 1: Security — Counter.sol + +## Evidence of thorough reading + +**File:** test/fixture/src/Counter.sol (15 lines) + +### Contract: Counter (line 5) +- State variable: `number` (uint256, public) — line 6 +- Function: `setNumber(uint256 newNumber)` — line 8 +- Function: `increment()` — line 12 + +### Types/errors/constants +- None + +## Findings + +No security findings. This is a test fixture contract. `setNumber` has no access control, which is intentional for a CI validation fixture. Arithmetic overflow in `increment()` is handled by Solidity 0.8+ built-in checks. diff --git a/audit/2026-03-12-01/pass1/Counter.t.md b/audit/2026-03-12-01/pass1/Counter.t.md new file mode 100644 index 0000000..e0de139 --- /dev/null +++ b/audit/2026-03-12-01/pass1/Counter.t.md @@ -0,0 +1,18 @@ +# Pass 1: Security — Counter.t.sol + +## Evidence of thorough reading + +**File:** test/fixture/test/Counter.t.sol (25 lines) + +### Contract: CounterTest is Test (line 8) +- State variable: `counter` (Counter, public) — line 9 +- Function: `setUp()` — line 11 +- Function: `test_Increment()` — line 16 +- Function: `testFuzz_SetNumber(uint256 x)` — line 21 + +### Types/errors/constants +- None + +## Findings + +No security findings. Standard Foundry test contract. diff --git a/audit/2026-03-12-01/pass1/Deploy.md b/audit/2026-03-12-01/pass1/Deploy.md new file mode 100644 index 0000000..5fa7cef --- /dev/null +++ b/audit/2026-03-12-01/pass1/Deploy.md @@ -0,0 +1,16 @@ +# Pass 1: Security — Deploy.sol + +## Evidence of thorough reading + +**File:** test/fixture/script/Deploy.sol (13 lines) + +### Contract: Deploy is Script (line 7) +- Function: `setUp()` — line 8 (empty) +- Function: `run()` — line 10 (calls vm.broadcast() with no deployment actions) + +### Types/errors/constants +- None + +## Findings + +No security findings. Empty deployment script fixture — `vm.broadcast()` with no subsequent calls is a no-op. diff --git a/audit/2026-03-12-01/pass1/flake.md b/audit/2026-03-12-01/pass1/flake.md new file mode 100644 index 0000000..651d812 --- /dev/null +++ b/audit/2026-03-12-01/pass1/flake.md @@ -0,0 +1,54 @@ +# Pass 1: Security — flake.nix + +## Evidence of thorough reading + +**File:** flake.nix (414 lines) + +### Inputs (lines 4-12) +- nixpkgs, flake-utils, rust-overlay, foundry, solc, nixpkgs-old + +### Let bindings (lines 17-163) +- `wasm-bindgen-overlay` (line 17): overlay pinning wasm-bindgen-cli to 0.2.100 +- `overlays` (line 20): combined overlay list +- `pkgs` (line 21): nixpkgs with overlays +- `old-pkgs` (line 22): pinned old nixpkgs +- `rust-version` (line 24): "1.94.0" +- `rust-toolchain` (line 25): stable rust with wasm32 target and extensions +- `rust-build-inputs` (line 31): list of rust build dependencies +- `sol-build-inputs` (line 48): list of solidity build dependencies +- `node-build-inputs` (line 56): nodejs_22 +- `network-list` (line 57): ["base" "flare"] +- `the-graph` (line 58): mkDerivation for graph CLI 0.69.2, fetched by URL+SHA per system +- `goldsky` (line 90): mkDerivation for goldsky CLI 8.6.6, fetched by URL+SHA per system +- `tauri-build-inputs` (line 123): tauri dependencies including old-pkgs +- `tauri-release-env` (line 141): buildEnv for tauri releases +- `mkTask` (line 151): helper to create wrapped script derivations +- `rainix-sol-prelude` (line 165): forge install + build +- `rainix-sol-static` (line 180): slither + forge fmt --check +- `rainix-sol-legal` (line 190): reuse lint +- `rainix-sol-test` (line 199): forge test -vvv +- `rainix-sol-artifacts` (line 208): deploy with retry loop (up to 10 attempts) +- `rainix-rs-prelude` (line 253): no-op +- `rainix-rs-static` (line 261): cargo fmt + clippy +- `rainix-rs-test` (line 271): cargo test +- `rainix-rs-artifacts` (line 280): cargo build --release +- `rainix-tasks` (line 289): aggregated task list +- `subgraph-build` (line 302): forge build + npm ci + graph codegen/build +- `subgraph-test` (line 315): docker compose up +- `subgraph-deploy` (line 323): subgraph-build + goldsky deploy +- `subgraph-tasks` (line 333): aggregated subgraph task list +- `source-dotenv` (line 335): sources .env if present +- `tauri-shellhook-test` (line 343): BATS test runner (Darwin-only) + +### Outputs (lines 354-413) +- `packages` (line 364): all rainix-* tasks + tauri-release-env +- `devShells.default` (line 371): full dev shell +- `devShells.tauri-shell` (line 385): tauri-specific dev shell with macOS workarounds + +### Types/constants +- `network-list`: ["base" "flare"] +- `rust-version`: "1.94.0" + +## Findings + +No security findings. External binaries (graph CLI, goldsky) are fetched with SHA256 pins preventing MITM. Shell scripts use `set -euxo pipefail`. Environment variable interpolation uses Nix `''${}` escaping. `.env` sourcing is guarded by file existence check and `.env` is gitignored. diff --git a/audit/2026-03-12-01/pass1/main.md b/audit/2026-03-12-01/pass1/main.md new file mode 100644 index 0000000..eb8504e --- /dev/null +++ b/audit/2026-03-12-01/pass1/main.md @@ -0,0 +1,15 @@ +# Pass 1: Security — main.rs + +## Evidence of thorough reading + +**File:** test/fixture/crates/test-rs/src/main.rs (3 lines) + +### Module: (crate root) +- Function: `main()` — line 1 + +### Types/errors/constants +- None + +## Findings + +No security findings. Hello world program. diff --git a/audit/2026-03-12-01/pass1/shellhook.test.md b/audit/2026-03-12-01/pass1/shellhook.test.md new file mode 100644 index 0000000..2b3b07f --- /dev/null +++ b/audit/2026-03-12-01/pass1/shellhook.test.md @@ -0,0 +1,20 @@ +# Pass 1: Security — shellhook.test.bats + +## Evidence of thorough reading + +**File:** test/bats/devshell/tauri/shellhook.test.bats (32 lines) + +### Tests +- `/usr/bin should be in PATH` — line 1 +- `nixpkgs apple_sdk xcrun should NOT be in PATH` — line 5 +- `should have access to native macos xcrun` — line 9 +- `should have access to native macos SetFile bin through native macos xcrun` — line 18 +- `should have access to native macos SetFile bin through /usr/bin in PATH` — line 24 +- `DEVELOPER_DIR should be unset` — line 30 + +### Types/errors/constants +- None + +## Findings + +No security findings. Shell environment validation tests for macOS Tauri dev shell. diff --git a/audit/2026-03-12-01/pass2/Counter.md b/audit/2026-03-12-01/pass2/Counter.md new file mode 100644 index 0000000..a0b1e8b --- /dev/null +++ b/audit/2026-03-12-01/pass2/Counter.md @@ -0,0 +1,29 @@ +# Pass 2: Test Coverage — Counter.sol + +## Evidence of thorough reading + +### Source: test/fixture/src/Counter.sol (15 lines) +- Contract: Counter (line 5) +- State variable: `number` (uint256, public) — line 6 +- Function: `setNumber(uint256 newNumber)` — line 8 +- Function: `increment()` — line 12 + +### Test: test/fixture/test/Counter.t.sol (25 lines) +- Contract: CounterTest is Test (line 8) +- Function: `setUp()` — line 11: creates Counter, sets number to 0 +- Function: `test_Increment()` — line 16: increments once, asserts == 1 +- Function: `testFuzz_SetNumber(uint256 x)` — line 21: fuzz sets number, asserts + +## Findings + +### A01-1 [LOW] No test for increment overflow behavior + +**File:** test/fixture/test/Counter.t.sol + +`test_Increment` only tests incrementing from 0 to 1. There is no test verifying that `increment()` reverts when `number` is `type(uint256).max`. While Solidity 0.8+ guarantees overflow reverts, an explicit test documents this expected behavior and would catch regressions if the contract were modified to use `unchecked`. + +### A01-2 [LOW] No test for consecutive increments + +**File:** test/fixture/test/Counter.t.sol + +`test_Increment` calls `increment()` once from 0. There is no test verifying multiple consecutive increments (e.g., increment from 0 to N) to exercise accumulation behavior. diff --git a/audit/2026-03-12-01/pass2/Deploy.md b/audit/2026-03-12-01/pass2/Deploy.md new file mode 100644 index 0000000..66ca930 --- /dev/null +++ b/audit/2026-03-12-01/pass2/Deploy.md @@ -0,0 +1,15 @@ +# Pass 2: Test Coverage — Deploy.sol + +## Evidence of thorough reading + +### Source: test/fixture/script/Deploy.sol (13 lines) +- Contract: Deploy is Script (line 7) +- Function: `setUp()` — line 8 (empty) +- Function: `run()` — line 10 (calls vm.broadcast() only) + +### Test files: None found +- Grepped for "Deploy" across test/ directory — only referenced in CI workflow, not tested directly + +## Findings + +No findings. This is an intentionally empty deployment script fixture used only to validate the CI deploy pipeline. Testing an empty `vm.broadcast()` call adds no value. diff --git a/audit/2026-03-12-01/pass2/flake.md b/audit/2026-03-12-01/pass2/flake.md new file mode 100644 index 0000000..9e15b48 --- /dev/null +++ b/audit/2026-03-12-01/pass2/flake.md @@ -0,0 +1,25 @@ +# Pass 2: Test Coverage — flake.nix + +## Evidence of thorough reading + +### Source: flake.nix (414 lines) +- See Pass 1 flake.md for complete function/binding inventory + +### Test files: +- `.github/workflows/test.yml` — runs rainix-sol-*, rainix-rs-* tasks against test/fixture/ +- `.github/workflows/check-shell.yml` — verifies dev shell tool availability +- `test/bats/devshell/tauri/shellhook.test.bats` — validates tauri shell environment + +## Findings + +### A02-1 [LOW] No CI coverage for subgraph tasks + +**File:** .github/workflows/ + +`subgraph-build`, `subgraph-test`, and `subgraph-deploy` are defined in the flake but have no CI workflow exercising them. While subgraph-test requires Docker and subgraph-deploy requires tokens, subgraph-build could be validated in CI (it just runs forge build + npm ci + graph codegen/build). + +### A02-2 [LOW] Default dev shell not tested in check-shell.yml + +**File:** .github/workflows/check-shell.yml + +`check-shell.yml` tests specific tool availability (`cargo release`, `flamegraph`, `graph`, `goldsky`) and the tauri shell hook, but does not test basic dev shell entry (`nix develop --command true`) or verify that the default shell's `shellHook` (which runs `npm ci`) succeeds. If a nixpkgs update breaks the shell derivation, there's no direct CI signal. diff --git a/audit/2026-03-12-01/pass2/main.md b/audit/2026-03-12-01/pass2/main.md new file mode 100644 index 0000000..1ac9f26 --- /dev/null +++ b/audit/2026-03-12-01/pass2/main.md @@ -0,0 +1,13 @@ +# Pass 2: Test Coverage — main.rs + +## Evidence of thorough reading + +### Source: test/fixture/crates/test-rs/src/main.rs (3 lines) +- Function: `main()` — line 1 (prints "Hello, world!") + +### Test files: None found +- No `tests/` directory, no `#[cfg(test)]` module, no `*_test.rs` files in the crate + +## Findings + +No findings. This is a hello-world fixture crate used only to validate that `cargo test` and `cargo build --release` work in CI. The crate has no logic to test. diff --git a/audit/2026-03-12-01/pass3/Counter.md b/audit/2026-03-12-01/pass3/Counter.md new file mode 100644 index 0000000..0b4480f --- /dev/null +++ b/audit/2026-03-12-01/pass3/Counter.md @@ -0,0 +1,13 @@ +# Pass 3: Documentation — Counter.sol + +## Evidence of thorough reading + +### Source: test/fixture/src/Counter.sol (15 lines) +- Contract: Counter (line 5) +- State variable: `number` (uint256, public) — line 6 +- Function: `setNumber(uint256 newNumber)` — line 8 +- Function: `increment()` — line 12 + +## Findings + +No findings. This is a minimal test fixture. NatSpec documentation on a CI validation fixture would add no value. diff --git a/audit/2026-03-12-01/pass3/Counter.t.md b/audit/2026-03-12-01/pass3/Counter.t.md new file mode 100644 index 0000000..b6bbdb8 --- /dev/null +++ b/audit/2026-03-12-01/pass3/Counter.t.md @@ -0,0 +1,13 @@ +# Pass 3: Documentation — Counter.t.sol + +## Evidence of thorough reading + +### Source: test/fixture/test/Counter.t.sol (25 lines) +- Contract: CounterTest is Test (line 8) +- Function: `setUp()` — line 11 +- Function: `test_Increment()` — line 16 +- Function: `testFuzz_SetNumber(uint256 x)` — line 21 + +## Findings + +No findings. Test names are self-documenting. diff --git a/audit/2026-03-12-01/pass3/Deploy.md b/audit/2026-03-12-01/pass3/Deploy.md new file mode 100644 index 0000000..dac6f81 --- /dev/null +++ b/audit/2026-03-12-01/pass3/Deploy.md @@ -0,0 +1,12 @@ +# Pass 3: Documentation — Deploy.sol + +## Evidence of thorough reading + +### Source: test/fixture/script/Deploy.sol (13 lines) +- Contract: Deploy is Script (line 7) +- Function: `setUp()` — line 8 (empty) +- Function: `run()` — line 10 + +## Findings + +No findings. Empty deployment fixture — documentation would describe nothing. diff --git a/audit/2026-03-12-01/pass3/flake.md b/audit/2026-03-12-01/pass3/flake.md new file mode 100644 index 0000000..a94a465 --- /dev/null +++ b/audit/2026-03-12-01/pass3/flake.md @@ -0,0 +1,19 @@ +# Pass 3: Documentation — flake.nix + +## Evidence of thorough reading + +See Pass 1 flake.md for complete function/binding inventory. + +## Findings + +### A01-1 [LOW] No README.md exists + +**File:** (missing) + +The project has no README.md. While CLAUDE.md provides context for AI tools, there is no human-readable documentation explaining what Rainix is, how to use it as a flake input, or what outputs it provides. Downstream consumers discovering this repo have no entry point other than reading flake.nix directly. + +### A01-2 [LOW] flake.nix has no comments on exported reusable outputs + +**File:** flake.nix:354-357 + +The outputs block exports `pkgs`, `old-pkgs`, `rust-toolchain`, `rust-build-inputs`, `sol-build-inputs`, `node-build-inputs`, `mkTask`, and `network-list` without any comments explaining that these are intended for downstream consumption or how they should be used. The `mkTask` helper in particular has a non-obvious API (name, body, additionalBuildInputs) that would benefit from a comment. diff --git a/audit/2026-03-12-01/pass3/main.md b/audit/2026-03-12-01/pass3/main.md new file mode 100644 index 0000000..dc42b85 --- /dev/null +++ b/audit/2026-03-12-01/pass3/main.md @@ -0,0 +1,10 @@ +# Pass 3: Documentation — main.rs + +## Evidence of thorough reading + +### Source: test/fixture/crates/test-rs/src/main.rs (3 lines) +- Function: `main()` — line 1 + +## Findings + +No findings. Hello world fixture. diff --git a/audit/2026-03-12-01/pass3/shellhook.test.md b/audit/2026-03-12-01/pass3/shellhook.test.md new file mode 100644 index 0000000..b1cc88e --- /dev/null +++ b/audit/2026-03-12-01/pass3/shellhook.test.md @@ -0,0 +1,15 @@ +# Pass 3: Documentation — shellhook.test.bats + +## Evidence of thorough reading + +### Source: test/bats/devshell/tauri/shellhook.test.bats (32 lines) +- Test: `/usr/bin should be in PATH` — line 1 +- Test: `nixpkgs apple_sdk xcrun should NOT be in PATH` — line 5 +- Test: `should have access to native macos xcrun` — line 9 +- Test: `should have access to native macos SetFile bin through native macos xcrun` — line 18 +- Test: `should have access to native macos SetFile bin through /usr/bin in PATH` — line 24 +- Test: `DEVELOPER_DIR should be unset` — line 30 + +## Findings + +No findings. Test names are descriptive and self-documenting. diff --git a/audit/2026-03-12-01/pass4/Counter.md b/audit/2026-03-12-01/pass4/Counter.md new file mode 100644 index 0000000..dbf6e60 --- /dev/null +++ b/audit/2026-03-12-01/pass4/Counter.md @@ -0,0 +1,17 @@ +# Pass 4: Code Quality — Counter.sol + +## Evidence of thorough reading + +### Source: test/fixture/src/Counter.sol (15 lines) +- Contract: Counter (line 5) +- State variable: `number` (uint256, public) — line 6 +- Function: `setNumber(uint256 newNumber)` — line 8 +- Function: `increment()` — line 12 + +## Findings + +### A01-1 [LOW] Pragma version mismatch between source and test + +**File:** test/fixture/src/Counter.sol:3 vs test/fixture/test/Counter.t.sol:3 + +`Counter.sol` uses `pragma solidity ^0.8.25` while `Counter.t.sol` uses `pragma solidity ^0.8.13`. The source contract requires a newer compiler than the test file. While Foundry resolves this by using the highest required version, the inconsistency suggests the test pragma was not updated when the source pragma was bumped. diff --git a/audit/2026-03-12-01/pass4/Counter.t.md b/audit/2026-03-12-01/pass4/Counter.t.md new file mode 100644 index 0000000..3bf8653 --- /dev/null +++ b/audit/2026-03-12-01/pass4/Counter.t.md @@ -0,0 +1,18 @@ +# Pass 4: Code Quality — Counter.t.sol + +## Evidence of thorough reading + +### Source: test/fixture/test/Counter.t.sol (25 lines) +- Contract: CounterTest is Test (line 8) +- Function: `setUp()` — line 11 +- Function: `test_Increment()` — line 16 +- Function: `testFuzz_SetNumber(uint256 x)` — line 21 +- Import: `console2` from forge-std (line 5) — unused + +## Findings + +### A02-1 [LOW] Unused import: console2 + +**File:** test/fixture/test/Counter.t.sol:5 + +`console2` is imported from `forge-std/Test.sol` but never used in the test contract. This is dead code. diff --git a/audit/2026-03-12-01/pass4/Deploy.md b/audit/2026-03-12-01/pass4/Deploy.md new file mode 100644 index 0000000..7a175bf --- /dev/null +++ b/audit/2026-03-12-01/pass4/Deploy.md @@ -0,0 +1,16 @@ +# Pass 4: Code Quality — Deploy.sol + +## Evidence of thorough reading + +### Source: test/fixture/script/Deploy.sol (13 lines) +- Contract: Deploy is Script (line 7) +- Function: `setUp()` — line 8 (empty) +- Function: `run()` — line 10 + +## Findings + +### A03-1 [LOW] Pragma version mismatch with Counter.sol + +**File:** test/fixture/script/Deploy.sol:3 + +`Deploy.sol` uses `pragma solidity ^0.8.13` while `Counter.sol` uses `^0.8.25`. Same inconsistency as noted in Counter.t.sol. diff --git a/audit/2026-03-12-01/pass4/flake.md b/audit/2026-03-12-01/pass4/flake.md new file mode 100644 index 0000000..e41925b --- /dev/null +++ b/audit/2026-03-12-01/pass4/flake.md @@ -0,0 +1,19 @@ +# Pass 4: Code Quality — flake.nix + +## Evidence of thorough reading + +See Pass 1 flake.md for complete function/binding inventory. + +## Findings + +### A04-1 [LOW] goldsky SHA256 is identical for x86_64-darwin and aarch64-darwin + +**File:** flake.nix:103-106 + +Both `x86_64-darwin` and `aarch64-darwin` map to the same SHA256 hash (`0yznf81yxc3a9vnfjdmmzdb59mh9bwrpxw87lrlhlchfr0jmnjk4`) and the same URL path (`macos`). This suggests a universal binary, which is fine — but if Goldsky ever ships separate arm64/x64 binaries, this mapping would silently download the wrong architecture. The `the-graph` derivation correctly distinguishes `darwin-x64` from `darwin-arm64`. + +### A04-2 [INFO] `network-list` is defined but only used as an output + +**File:** flake.nix:57 + +`network-list = [ "base" "flare" ]` is defined in the let block and exported as an output, but is not consumed anywhere within this flake. Its usage is presumably in downstream flakes. diff --git a/audit/2026-03-12-01/pass4/main.md b/audit/2026-03-12-01/pass4/main.md new file mode 100644 index 0000000..44295b4 --- /dev/null +++ b/audit/2026-03-12-01/pass4/main.md @@ -0,0 +1,10 @@ +# Pass 4: Code Quality — main.rs + +## Evidence of thorough reading + +### Source: test/fixture/crates/test-rs/src/main.rs (3 lines) +- Function: `main()` — line 1 + +## Findings + +No findings. Minimal hello-world fixture. diff --git a/audit/2026-03-12-01/pass4/shellhook.test.md b/audit/2026-03-12-01/pass4/shellhook.test.md new file mode 100644 index 0000000..a95dc98 --- /dev/null +++ b/audit/2026-03-12-01/pass4/shellhook.test.md @@ -0,0 +1,15 @@ +# Pass 4: Code Quality — shellhook.test.bats + +## Evidence of thorough reading + +### Source: test/bats/devshell/tauri/shellhook.test.bats (32 lines) +- Test: `/usr/bin should be in PATH` — line 1 +- Test: `nixpkgs apple_sdk xcrun should NOT be in PATH` — line 5 +- Test: `should have access to native macos xcrun` — line 9 +- Test: `should have access to native macos SetFile bin through native macos xcrun` — line 18 +- Test: `should have access to native macos SetFile bin through /usr/bin in PATH` — line 24 +- Test: `DEVELOPER_DIR should be unset` — line 30 + +## Findings + +No findings. Tests are consistent in style and structure. diff --git a/audit/2026-03-12-01/pass5/Counter.md b/audit/2026-03-12-01/pass5/Counter.md new file mode 100644 index 0000000..283c711 --- /dev/null +++ b/audit/2026-03-12-01/pass5/Counter.md @@ -0,0 +1,13 @@ +# Pass 5: Correctness / Intent Verification — Counter.sol + +## Evidence of thorough reading + +### Source: test/fixture/src/Counter.sol (15 lines) +- Contract: Counter (line 5) +- State variable: `number` (uint256, public) — line 6 +- Function: `setNumber(uint256 newNumber)` — line 8: sets number to newNumber +- Function: `increment()` — line 12: increments number by 1 + +## Findings + +No findings. `setNumber` sets and `increment` increments — behavior matches names. diff --git a/audit/2026-03-12-01/pass5/Counter.t.md b/audit/2026-03-12-01/pass5/Counter.t.md new file mode 100644 index 0000000..d1f2f1c --- /dev/null +++ b/audit/2026-03-12-01/pass5/Counter.t.md @@ -0,0 +1,13 @@ +# Pass 5: Correctness / Intent Verification — Counter.t.sol + +## Evidence of thorough reading + +### Source: test/fixture/test/Counter.t.sol (25 lines) +- Contract: CounterTest is Test (line 8) +- Function: `setUp()` — line 11: deploys Counter, sets number to 0 +- Function: `test_Increment()` — line 16: increments, asserts number == 1 +- Function: `testFuzz_SetNumber(uint256 x)` — line 21: sets number to x, asserts number == x + +## Findings + +No findings. `test_Increment` tests incrementing from 0 to 1 — name matches behavior. `testFuzz_SetNumber` fuzzes setNumber — name matches behavior. diff --git a/audit/2026-03-12-01/pass5/Deploy.md b/audit/2026-03-12-01/pass5/Deploy.md new file mode 100644 index 0000000..980723e --- /dev/null +++ b/audit/2026-03-12-01/pass5/Deploy.md @@ -0,0 +1,16 @@ +# Pass 5: Correctness / Intent Verification — Deploy.sol + +## Evidence of thorough reading + +### Source: test/fixture/script/Deploy.sol (13 lines) +- Contract: Deploy is Script (line 7) +- Function: `setUp()` — line 8: empty +- Function: `run()` — line 10: calls vm.broadcast() + +## Findings + +### A03-1 [LOW] Deploy.run() broadcasts nothing + +**File:** test/fixture/script/Deploy.sol:10-12 + +The contract is named `Deploy` and its `run()` function calls `vm.broadcast()` but deploys nothing after it. `vm.broadcast()` makes the next call a broadcast, but there is no next call. The `rainix-sol-artifacts` task in flake.nix runs this script with `--broadcast`, which will succeed but deploy nothing. If the intent is a no-op fixture, the `vm.broadcast()` call is misleading — it suggests deployment was intended but forgotten. diff --git a/audit/2026-03-12-01/pass5/flake.md b/audit/2026-03-12-01/pass5/flake.md new file mode 100644 index 0000000..4da0c9d --- /dev/null +++ b/audit/2026-03-12-01/pass5/flake.md @@ -0,0 +1,19 @@ +# Pass 5: Correctness / Intent Verification — flake.nix + +## Evidence of thorough reading + +See Pass 1 flake.md for complete function/binding inventory. + +## Findings + +### A04-1 [MEDIUM] rainix-rs-prelude is a no-op but CI runs it before every task + +**File:** flake.nix:253-259, .github/workflows/test.yml:51 + +`rainix-rs-prelude` is defined with an empty body (`set -euxo pipefail` and nothing else). The CI workflow runs `nix run ../..#rainix-rs-prelude` before every matrix task (line 51). This is harmless but costs CI time building and running a no-op derivation. More importantly, if a downstream consumer expects `rainix-rs-prelude` to prepare the Rust environment (by analogy with `rainix-sol-prelude` which does `forge install && forge build`), they'll get no preparation. + +### A04-2 [LOW] CLAUDE.md documents rainix-rs-prelude as "currently no-op, placeholder for env prep" but flake has no TODO/comment + +**File:** flake.nix:253-259 + +CLAUDE.md (line 32) notes this is a "placeholder for env prep" but the flake.nix source has no corresponding comment or TODO indicating this is intentionally incomplete. Future maintainers reading only the flake won't know whether this is done or pending. diff --git a/audit/2026-03-12-01/pass5/main.md b/audit/2026-03-12-01/pass5/main.md new file mode 100644 index 0000000..70a1ce9 --- /dev/null +++ b/audit/2026-03-12-01/pass5/main.md @@ -0,0 +1,10 @@ +# Pass 5: Correctness / Intent Verification — main.rs + +## Evidence of thorough reading + +### Source: test/fixture/crates/test-rs/src/main.rs (3 lines) +- Function: `main()` — line 1: prints "Hello, world!" + +## Findings + +No findings. Hello world does what it says. diff --git a/audit/2026-03-12-01/pass5/shellhook.test.md b/audit/2026-03-12-01/pass5/shellhook.test.md new file mode 100644 index 0000000..fcb3466 --- /dev/null +++ b/audit/2026-03-12-01/pass5/shellhook.test.md @@ -0,0 +1,15 @@ +# Pass 5: Correctness / Intent Verification — shellhook.test.bats + +## Evidence of thorough reading + +### Source: test/bats/devshell/tauri/shellhook.test.bats (32 lines) +- Test: `/usr/bin should be in PATH` — line 1: checks PATH contains /usr/bin +- Test: `nixpkgs apple_sdk xcrun should NOT be in PATH` — line 5: checks PATH doesn't contain xcrun +- Test: `should have access to native macos xcrun` — line 9: runs xcrun --version, verifies /usr/bin/xcrun +- Test: `should have access to native macos SetFile bin through native macos xcrun` — line 18: runs xcrun --find SetFile +- Test: `should have access to native macos SetFile bin through /usr/bin in PATH` — line 24: checks which SetFile == /usr/bin/SetFile +- Test: `DEVELOPER_DIR should be unset` — line 30: checks DEVELOPER_DIR is unset + +## Findings + +No findings. Each test name accurately describes the assertion it makes. Tests correspond to the workarounds documented in flake.nix lines 399-411. diff --git a/audit/2026-03-12-01/triage.md b/audit/2026-03-12-01/triage.md new file mode 100644 index 0000000..0e22fe0 --- /dev/null +++ b/audit/2026-03-12-01/triage.md @@ -0,0 +1,40 @@ +# Audit Triage — 2026-03-12-01 + +## Pass 0: Process Review +| ID | Severity | Title | Status | +|----|----------|-------|--------| +| A01-1-p0 | LOW | Inaccurate CI platform description in CLAUDE.md | FIXED | +| A01-2-p0 | LOW | Task path prefix varies by working directory | PENDING | +| A01-3-p0 | LOW | Subgraph tasks not runnable via `nix run` | PENDING | + +## Pass 1: Security +No LOW+ findings. + +## Pass 2: Test Coverage +| ID | Severity | Title | Status | +|----|----------|-------|--------| +| A01-1-p2 | LOW | No test for increment overflow behavior | PENDING | +| A01-2-p2 | LOW | No test for consecutive increments | PENDING | +| A02-1-p2 | LOW | No CI coverage for subgraph tasks | PENDING | +| A02-2-p2 | LOW | Default dev shell not tested in check-shell.yml | PENDING | + +## Pass 3: Documentation +| ID | Severity | Title | Status | +|----|----------|-------|--------| +| A01-1-p3 | LOW | No README.md exists | PENDING | +| A01-2-p3 | LOW | No comments on exported reusable outputs in flake.nix | PENDING | + +## Pass 4: Code Quality +| ID | Severity | Title | Status | +|----|----------|-------|--------| +| A01-1-p4 | LOW | Pragma version mismatch between source and test | PENDING | +| A02-1-p4 | LOW | Unused import: console2 | PENDING | +| A03-1-p4 | LOW | Pragma version mismatch — Deploy.sol | PENDING | +| A04-1-p4 | LOW | goldsky SHA256 identical for x86_64-darwin and aarch64-darwin | PENDING | + +## Pass 5: Correctness +| ID | Severity | Title | Status | +|----|----------|-------|--------| +| A03-1-p5 | LOW | Deploy.run() broadcasts nothing | PENDING | +| A04-1-p5 | MEDIUM | rainix-rs-prelude is a no-op but CI runs it | DOCUMENTED | +| A04-2-p5 | LOW | CLAUDE.md documents no-op but flake has no comment | PENDING | diff --git a/flake.lock b/flake.lock index a69ab30..60e6a5f 100644 --- a/flake.lock +++ b/flake.lock @@ -57,11 +57,11 @@ "nixpkgs": "nixpkgs" }, "locked": { - "lastModified": 1769324704, - "narHash": "sha256-aef15vEgiMEls1hTMt46rJuKNSO2cIOfiP99patq9yc=", + "lastModified": 1773213477, + "narHash": "sha256-Pv1Z3QqGkSGEUV+9pM5vYIiI7VJo7Tfm6ZmR+JSp1zo=", "owner": "shazow", "repo": "foundry.nix", - "rev": "e830409ba1bdecdc5ef9a1ec92660fc2da9bc68d", + "rev": "3c73daa86c823d706824fd9bbcb85aa23fd0f668", "type": "github" }, "original": { @@ -102,11 +102,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1769364508, - "narHash": "sha256-Wy8EVYSLq5Fb/rYH3LRxAMCnW75f9hOg2562AXVFmPk=", + "lastModified": 1773260212, + "narHash": "sha256-lYbvufol78/ziIlZm043NT2byudb6NQpGHhYhVSlkVY=", "owner": "nixos", "repo": "nixpkgs", - "rev": "6077bc4fb29be43d525984f63b69d37b9b1e62fe", + "rev": "37e1923adb6229cea0d12a5921b75c976017ccfe", "type": "github" }, "original": { @@ -133,11 +133,11 @@ }, "nixpkgs_4": { "locked": { - "lastModified": 1766653575, - "narHash": "sha256-TPgxCS7+hWc4kPhzkU5dD2M5UuPhLuuaMNZ/IpwKQvI=", + "lastModified": 1771923393, + "narHash": "sha256-Fy0+UXELv9hOE8WjYhJt8fMDLYTU2Dqn3cX4BwoGBos=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3c1016e6acd16ad96053116d0d3043029c9e2649", + "rev": "ea7f1f06811ce7fcc81d6c6fd4213150c23edcf2", "type": "github" }, "original": { @@ -162,11 +162,11 @@ "nixpkgs": "nixpkgs_3" }, "locked": { - "lastModified": 1769309768, - "narHash": "sha256-AbOIlNO+JoqRJkK1VrnDXhxuX6CrdtIu2hSuy4pxi3g=", + "lastModified": 1773216618, + "narHash": "sha256-iZlowevS+xKLGOXtZwpIrz3SWe7PtoGUfEeVZNib+WE=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "140c9dc582cb73ada2d63a2180524fcaa744fad5", + "rev": "07d7dc6fcc5eae76b4fb0e19d4afd939437bec97", "type": "github" }, "original": { @@ -182,11 +182,11 @@ "solc-macos-amd64-list-json": "solc-macos-amd64-list-json" }, "locked": { - "lastModified": 1768831671, - "narHash": "sha256-0mmlYRtZK+eomevkQCCH7PL8QlSuALZQsjLroCWGE08=", + "lastModified": 1772085240, + "narHash": "sha256-+NEcuhT2A0QQumVx9Ze6g2iuNicyuW028Jq/HUJHGh4=", "owner": "hellwolf", "repo": "solc.nix", - "rev": "80ad871b93d15c7bccf71617f78f73c2d291a9c7", + "rev": "d3cc119973e484ea366f4b997b404bb00d7829ca", "type": "github" }, "original": { @@ -198,13 +198,13 @@ "solc-macos-amd64-list-json": { "flake": false, "locked": { - "narHash": "sha256-P+ZslplK4cQ/wnV/wykVKb+yTCviI0eylA3sk9uHmRo=", + "narHash": "sha256-oEiXc95EghuYCudzkPA9XBFOnMdgWFfTO2/4XUfSTpc=", "type": "file", - "url": "https://github.com/argotorg/solc-bin/raw/a11f1ad/macosx-amd64/list.json" + "url": "https://github.com/argotorg/solc-bin/raw/83cb756/macosx-amd64/list.json" }, "original": { "type": "file", - "url": "https://github.com/argotorg/solc-bin/raw/a11f1ad/macosx-amd64/list.json" + "url": "https://github.com/argotorg/solc-bin/raw/83cb756/macosx-amd64/list.json" } }, "systems": { diff --git a/flake.nix b/flake.nix index 1a33f7c..e44de47 100644 --- a/flake.nix +++ b/flake.nix @@ -21,7 +21,7 @@ pkgs = import nixpkgs { inherit system overlays; }; old-pkgs = import nixpkgs-old { inherit system; }; - rust-version = "1.89.0"; + rust-version = "1.94.0"; rust-toolchain = pkgs.rust-bin.stable.${rust-version}.default.override (previous: { targets = previous.targets ++ [ "wasm32-unknown-unknown" ]; @@ -252,6 +252,8 @@ rainix-rs-prelude = mkTask { name = "rainix-rs-prelude"; + # Intentionally empty — exists so downstream consumers can call + # rainix-rs-prelude unconditionally alongside rainix-sol-prelude. body = '' set -euxo pipefail '';