Skip to content

Install docs omit the rain-sol-codegen dependency, so a consumer following them cannot compile #48

Description

@thedavidmeister

Raised independently by the documentation and process dimensions against the same README section.


Finding D3-01

Audit finding D3-01 — dimension 3, severity LOW. Whole-repo audit pass 1 at 440e90b5.

README.md:323-339

Problem

The Install section states the ONLY external requirement is forge-std 1.16.1 ("You also need forge-std 1.16.1", and a [dependencies] block listing only forge-std and rain-deploy). That is false against the published tree. src/lib/LibRainDeploySnapshot.sol:6-7 imports rain-sol-codegen-0.1.6/src/lib/LibCodeGen.sol and .../LibFs.sol, and src/abstract/RainDeployVerifySnapshot.sol:8 imports LibRainDeploySnapshot — an abstract the README's own example tells consumers to inherit (contract MyDeploySnapshotTest is MyDeploySuites, RainDeployVerifySnapshot {}). .soldeerignore ships src/ and excludes remappings.txt/soldeer.lock/dependencies/, so the consumer must declare rain-sol-codegen = "0.1.6" AND remap it as rain-sol-codegen-0.1.6/ itself. Following the README as written yields an unresolved-import build failure on the first forge build. The paragraph's stated rationale ("the deployed contract imports nothing outside this package, but every abstract a consumer inherits pulls forge-std in") is the exact reasoning that also implies rain-sol-codegen, and it stops one dependency short.

Proposed fix

README.md, replace lines 323-335 with:

**You also need `forge-std` 1.16.1 and `rain-sol-codegen` 0.1.6**, remapped as
`forge-std-1.16.1/` and `rain-sol-codegen-0.1.6/`. The published package
deliberately ships `src/` and `script/` — no `remappings.txt`, no
`soldeer.lock`, no `dependencies/` — so a consumer resolves both itself. The
requirement is transitive rather than incidental: the deployed contracts import
nothing outside this package, but every abstract a consumer inherits pulls them
in — `Script` via `RainDeployBroadcast`, `Test` via `RainDeployVerifyBase`, `Vm`
via `LibRainDeploy` beneath both, and `LibCodeGen`/`LibFs` via
`LibRainDeploySnapshot`, which `RainDeployVerifySnapshot` imports:

```toml
[dependencies]
forge-std = "1.16.1"
rain-sol-codegen = "0.1.6"
rain-deploy = "<version>"
# remappings.txt
forge-std-1.16.1/=dependencies/forge-std-1.16.1/
rain-sol-codegen-0.1.6/=dependencies/rain-sol-codegen-0.1.6/

---

<details>
<summary>Verification — this finding survived an adversarial refutation pass</summary>

SURVIVES. Every load-bearing claim checks out against source.\n\nREADME.md:323-335 says "**You also need `forge-std` 1.16.1**" and gives a `[dependencies]` block containing only `forge-std = \"1.16.1\"` and `rain-deploy = \"<version>\"`. `rain-sol-codegen` appears nowhere in README.md (grep: zero hits).\n\nThe dependency is real and reachable from the documented consumer path:\n- src/lib/LibRainDeploySnapshot.sol:6-7 imports `rain-sol-codegen-0.1.6/src/lib/LibCodeGen.sol` and `.../LibFs.sol`.\n- src/abstract/RainDeployVerifySnapshot.sol:8 imports LibRainDeploySnapshot and USES it at line 265 (`LibRainDeploySnapshot.frozenSnapshotPaths(vm, LibRainDeploySnapshot.LIB_FS_ROOT)`), so it is not a dead import that could be argued away.\n- README.md:65 explicitly tells a consumer to write `contract MyDeploySnapshotTest is MyDeploySuites, RainDeployVerifySnapshot {}`. CLAUDE.md:253-258 independently confirms this is the intended consumer contract ("a downstream repo has to import all of this ... its test contracts inherit `RainDeployVerify*`").\n\nThe consumer genuinely must resolve it themselves: .soldeerignore ships `src/` and `script/` while excluding `/foundry.toml`, `/remappings.txt`, `/soldeer.lock`, `/dependencies`, so no dependency metadata is published; and `[soldeer] recursive_deps = false` in foundry.toml rules out transitive resolution. This repo's own remappings.txt carries `rain-sol-codegen-0.1.6/=dependencies/rain-sol-codegen-0.1.6/` and foundry.toml `[dependencies]` carries `rain-sol-codegen = \"0.1.6\"` — exactly the two facts stripped from the published package and omitted from the README. Following the README as written yields an unresolved-import failure on first `forge build`.\n\nRefutation attempts that failed: (a) no forge-std-only consumer path avoids it, since the snapshot abstract is documented consumer-facing in both README and CLAUDE.md; (b) the import is used, not vestigial; (c) no other README section mentions the package; (d) this is not a deliberate documented convention — the README's own stated rationale ("every abstract a consumer inherits pulls forge-std in") is the exact reasoning that implies rain-sol-codegen too, and it stops one dependency short.\n\nSEVERITY CORRECTED MEDIUM -> LOW. Value-at-risk in production is nil: no on-chain exposure, no key custody, no deployed-bytecode or pin correctness impact. The failure is a loud, self-diagnosing compile-time error that names the missing path verbatim, encountered once at consumer onboarding. Real documentation defect, but onboarding friction rather than risk.\n\nNit on the proposed fix: it silently drops "only" from "ships only `src/` and `script/`"; that word is accurate and should be kept.

</details>



---

# Finding `PROC-01`

**Audit finding `PROC-01`** — dimension 0, severity LOW. Whole-repo audit pass 1 at `440e90b5`.

`README.md:316-339`

## Problem

The Install section states the only extra dependency is forge-std ("**You also need `forge-std` 1.16.1**") and gives a copy-paste `[dependencies]` block containing only `forge-std` and `rain-deploy`. It justifies this with "the deployed contract imports nothing outside this package, but every abstract a consumer inherits pulls forge-std in". That is incomplete against the tree: `src/lib/LibRainDeploySnapshot.sol` (shipped — `.soldeerignore` excludes `test/` but not `src/`) imports `rain-sol-codegen-0.1.6/src/lib/LibCodeGen.sol` and `.../LibFs.sol`, and `src/abstract/RainDeployVerifySnapshot.sol:8` imports `LibRainDeploySnapshot`. README's own example on line 65 tells a consumer to write `contract MyDeploySnapshotTest is MyDeploySuites, RainDeployVerifySnapshot {}`, so every consumer following this document compiles that import chain. The misreading: a future session sets a consumer repo up exactly as documented, gets an unresolved-import compile failure on `rain-sol-codegen-0.1.6/`, and — because the doc says the package needs nothing else — hunts for a remapping/packaging bug in rain.deploy instead of adding the missing dependency. A session maintaining this repo may equally conclude the codegen import is accidental and try to remove it.

## Proposed fix

README.md, replace lines 323-335 with:

You also need forge-std 1.16.1 (remapped as forge-std-1.16.1/) and
rain-sol-codegen 0.1.6 (remapped as rain-sol-codegen-0.1.6/). The
published package deliberately ships only src/ and script/ — no
remappings.txt, no soldeer.lock, no dependencies/ — so a consumer resolves
both itself. Both requirements are transitive rather than incidental: the
deployed contract imports nothing outside this package, but every abstract a
consumer inherits pulls them in — Script via RainDeployBroadcast, Test via
RainDeployVerifyBase, Vm via LibRainDeploy beneath both, and LibCodeGen/
LibFs via LibRainDeploySnapshot, which RainDeployVerifySnapshot imports.

[dependencies]
forge-std = "1.16.1"
rain-sol-codegen = "0.1.6"
rain-deploy = "<version>"

---

<details>
<summary>Verification — this finding survived an adversarial refutation pass</summary>

Verified against source and could not refute.

1. The import chain is real and load-bearing. src/lib/LibRainDeploySnapshot.sol:6-7 imports rain-sol-codegen-0.1.6/src/lib/LibCodeGen.sol and .../LibFs.sol. src/abstract/RainDeployVerifySnapshot.sol:8 imports LibRainDeploySnapshot and actually uses it at line 265 (LibRainDeploySnapshot.frozenSnapshotPaths(vm, LibRainDeploySnapshot.LIB_FS_ROOT)), so it is not a removable/vestigial import. script/Build.sol:8 imports it as well.

2. It is shipped. .soldeerignore excludes .github, /test, /dependencies, /foundry.toml, /remappings.txt, /soldeer.lock, /out, /cache, /audit, /docs, CLAUDE.md — but not src/ or script/, which matches README's own claim that the package ships exactly those two. So the consumer receives the codegen-importing file and no dependency metadata (foundry.toml and remappings.txt are both excluded).

3. README line 65 prescribes `contract MyDeploySnapshotTest is MyDeploySuites, RainDeployVerifySnapshot {}` as the canonical consumer shape, so every consumer following the document compiles that chain.

4. The cited lines say what the finding claims. README:323-335 asserts "You also need `forge-std` 1.16.1", enumerates the transitive pull-ins as only Script/Test/Vm, and gives a copy-paste [dependencies] block containing only forge-std and rain-deploy. Following it verbatim produces an unresolved-import failure on rain-sol-codegen-0.1.6/.

5. Nothing elsewhere covers it: grep of test/, src/, script/ and .github/ finds no reference to README; no test or CI job validates the install block. This is not a documented org convention — it is a plain omission introduced when the repo picked up rain-sol-codegen (commit befb78d bumped it to 0.1.6) without the README following.

Minor overreach in the finding, not enough to refute: the sentence "the deployed contract imports nothing outside this package" remains TRUE — AddressRegistry and MigrationRegistry import only local interfaces. The defect is the incomplete enumeration of what the inherited abstracts pull in and the incomplete toml block, not that clause; the proposed fix correctly keeps it.

Severity corrected MEDIUM -> LOW. Value at risk in production is zero: this is documentation only, touches no deployed bytecode, no pin, no snapshot and no broadcast path. The failure mode is a compile-time unresolved import whose error message names `rain-sol-codegen-0.1.6/src/lib/LibCodeGen.sol` verbatim, so it is fail-loud and self-describing; the finding's escalation ("hunts for a packaging bug in rain.deploy") is speculative against an error that states the missing package. The secondary scenario — a maintainer concluding the codegen import is accidental and removing it — is refuted by line 265's real call site, which would break the build immediately.

</details>

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

auditAudit findingpass1Audit pass 1 (whole-repo, 2026-08-15)severity:lowAudit severity: LOW

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions