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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ next-env.d.ts
# screenshots
ui-*.png
safelens-*.png

# Generated audit drift output
docs/audit/dependency-footprint.diff
137 changes: 58 additions & 79 deletions AUDIT.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ bun install
|------|-------------|
| `packages/core` | Shared crypto verification library (TypeScript) |
| `packages/cli` | CLI wrapper over core logic |
| `apps/generator` | Next.js web app creates evidence packages |
| `apps/desktop` | Tauri + Vite desktop app airgapped verification |
| `apps/generator` | Next.js web app, creates evidence packages |
| `apps/desktop` | Tauri + Vite desktop app, airgapped verification |

## Running Locally

Expand Down Expand Up @@ -54,6 +54,6 @@ SafeLens is a security tool. Changes to these areas require extra scrutiny:

## Architecture

- [`TRUST_ASSUMPTIONS.md`](TRUST_ASSUMPTIONS.md) full trust model
- [`AUDIT.md`](AUDIT.md) security architecture and attack surface
- [`docs/`](docs/) architecture contracts and runbooks
- [`TRUST_ASSUMPTIONS.md`](TRUST_ASSUMPTIONS.md), full trust model
- [`AUDIT.md`](AUDIT.md), security architecture and attack surface
- [`docs/`](docs/), architecture contracts and runbooks
22 changes: 19 additions & 3 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@

Why each dependency exists. Organized by package and split into verification-path (security-critical) vs UI/tooling (non-critical).

## Reviewer workflow

Generate a dependency snapshot and compare it with the committed baseline:

```bash
bash scripts/audit/deps.sh
```

Artifacts:

- Current snapshot: `docs/audit/dependency-footprint.md`
- Baseline snapshot: `docs/audit/dependency-footprint.baseline.md`
- Drift diff: `docs/audit/dependency-footprint.diff`

Any dependency drift should be reviewed together with rationale updates in this file.

## Verification Path (security-critical)

These dependencies are in the trust boundary they handle crypto, schema validation, or EVM execution.
These dependencies are in the trust boundary, they handle crypto, schema validation, or EVM execution.

### packages/core (TypeScript)

Expand Down Expand Up @@ -76,7 +92,7 @@ These dependencies are not in the verification trust boundary. They handle rende
| `next` | ^14.2 | React framework. Serves the generator web app. |
| `react` / `react-dom` | ^18.3 | UI rendering. |
| `lucide-react` | ^0.454 | Icons. |
| `class-variance-authority` / `clsx` / `tailwind-merge` | | CSS utilities (same as desktop). |
| `class-variance-authority` / `clsx` / `tailwind-merge` | n/a | CSS utilities (same as desktop). |

### apps/generator (dev)

Expand Down Expand Up @@ -111,7 +127,7 @@ helios-consensus-core = { git = "https://github.com/a16z/helios", rev = "582fda3

The pinned commit (`582fda3`, 2026-02-18) includes a fix for hex-encoded `blockNumber` and `chainId` fields in beacon API responses ([helios#776](https://github.com/a16z/helios/pull/776)). The latest tagged Helios release is `0.11.0` (2025-12-16), which does not include this fix. SafeLens needs it because beacon finality update responses from some clients return numeric fields as hex strings.

**Commit provenance:** The commit is on the `main` branch of `a16z/helios`, 17 commits ahead of the `0.11.0` tag. It will be included in the next Helios release.
**Commit provenance:** The commit is on the `main` branch of `a16z/helios`, 17 commits ahead of the `0.11.0` tag.

**Action item:** When Helios publishes a release that includes commit `582fda3`, migrate from `rev = "..."` to a version or tag pin.

Expand Down
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,32 @@
SafeLens generates and verifies evidence packages for Gnosis Safe multisig transactions. Paste a Safe transaction URL into the [generator](https://safelens.lfg.rs/), download the `evidence.json`, then verify signatures and hashes offline using the desktop app or CLI.

- **Generate** an `evidence.json` package from any Safe transaction URL
- **Verify** signatures, hashes, and enriched proofs locally with zero network access
- **Verify** signatures, hashes, enriched proofs, and local transaction replay checks with zero network access
- **Clear signing** via built-in and ERC-7730 interpreters for human-readable transaction details
- **Consensus checks** via embedded Helios verifier for beacon-mode consensus proofs

## Trust model

The desktop verifier ships with a CSP that restricts `connect-src` to Tauri IPC only (`ipc: http://ipc.localhost`) — no external network origins — and no shell-open capability. It cannot make network requests during verification. All crypto runs locally using bundled libraries. See [`TRUST_ASSUMPTIONS.md`](TRUST_ASSUMPTIONS.md) for the full model.
The desktop verifier ships with a CSP that restricts `connect-src` to Tauri IPC only (`ipc: http://ipc.localhost`), with no external network origins and no shell-open capability. It cannot make network requests during verification. All crypto runs locally using bundled libraries. See [`TRUST_ASSUMPTIONS.md`](TRUST_ASSUMPTIONS.md) for the full model.

## Independent verification stack

SafeLens is one verifier in a multi-tool workflow, not a single source of truth.

- Tool 1: Independent Safe hash verifiers (for example `safe-tx-hashes-util`) validate digest expectations.
- Tool 2: Hardware wallet screen validates the hash shown at signing time.
- Tool 3: Foundry or local node simulation validates execution expectations.
- Tool 4: SafeLens validates offline package integrity, signatures, and optional proofs.

For high-value operations, use at least two independent tools and compare outputs.

## Project docs

- Security policy: [`SECURITY.md`](SECURITY.md)
- Contributing guidelines: [`CONTRIBUTING.md`](CONTRIBUTING.md)
- Build verification: [`VERIFY.md`](VERIFY.md)
- Release integrity checks: [`RELEASE_INTEGRITY.md`](RELEASE_INTEGRITY.md)
- Auditor packet: [`docs/audit/AUDITOR_PACKET.md`](docs/audit/AUDITOR_PACKET.md)

## Architecture and runbooks

Expand Down
44 changes: 44 additions & 0 deletions RELEASE_INTEGRITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Release Integrity

This document defines the reviewer workflow for release integrity checks.

## Policy

- Release tags (`v*`) are treated as immutable once published.
- Release artifacts are tied to a specific git tag and commit.
- Release checksums in `SHA256SUMS.txt` are the canonical artifact integrity record.

## Verify Tag to Commit

```bash
git fetch --tags origin
git show --no-patch --pretty=fuller v0.4.0
git rev-list -n 1 v0.4.0
```

Expected result: the tag resolves to the intended audited commit.

## Verify Artifact Checksums

```bash
# Example on Linux/macOS
sha256sum SafeLens_0.4.0_aarch64.dmg
# Compare with matching line in SHA256SUMS.txt
```

```powershell
# Example on Windows
Get-FileHash SafeLens_0.4.0_x64-setup.exe -Algorithm SHA256
```

Expected result: local checksum equals the value in `SHA256SUMS.txt`.

## Verify CI Workflow Source

Release pipeline definition:
- `.github/workflows/release.yml`

Test/CI parity pipeline definition:
- `.github/workflows/test.yml`

Expected result: pinned action revisions, pinned Bun and Rust versions, and `bun install --frozen-lockfile` in CI.
20 changes: 20 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,23 @@ Out of scope:
## Trust Model

See [`TRUST_ASSUMPTIONS.md`](TRUST_ASSUMPTIONS.md) for the full trust model and [`AUDIT.md`](AUDIT.md) for the security architecture.

## Threat Model Summary

### Protects against

| Scenario | Condition |
|---|---|
| Malicious or incorrect Safe API payload | Offline verifier recomputes safeTxHash and checks signatures |
| Hash substitution in exported evidence | Claimed hash is never trusted, verifier recomputes from transaction fields |
| Malformed policy proof artifacts | MPT proofs are validated locally before trust upgrades |
| Desktop data exfiltration during verification | CSP and Tauri config block external network access |

### Does not protect against

| Scenario | Reason |
|---|---|
| Compromised airgapped machine | Local runtime and OS are in the trusted computing base |
| Compromised RPC used during generation | Generation is network-connected and treated as untrusted input collection |
| Compromised Foundry/local node simulation tooling | External simulation tools are out of SafeLens trust boundary |
| Unsupported signature schemes fully verified offline | Some signature modes require on-chain validation and are reported as warnings |
25 changes: 17 additions & 8 deletions TRUST_ASSUMPTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ This document lists every trust assumption SafeLens makes today.

SafeLens classifies each source as:

- `proof-verified`: validated against cryptographic Merkle/consensus proofs
- `consensus-verified` (legacy): backward-compatibility enum value for older exports; new reports use mode-specific consensus levels
- `consensus-verified-beacon`: state root verified with beacon light-client BLS checks
- `consensus-verified-opstack`: OP Stack envelope integrity verified (not beacon-equivalent)
- `consensus-verified-linea`: Linea envelope integrity verified (not beacon-equivalent)
- `proof-verified`: validated against cryptographic Merkle proofs
- `self-verified`: validated locally with deterministic code
- `rpc-sourced`: accepted from an RPC endpoint (e.g., generation-time simulation/witness inputs)
- `api-sourced`: accepted from remote API responses
- `user-provided`: accepted from local operator input or local files

Each section of the evidence package carries its own trust classification,
allowing progressive trust upgrades (e.g., `api-sourced` to `proof-verified`)
allowing progressive trust upgrades (e.g., `api-sourced` to `proof-verified`,
or `rpc-sourced` to `consensus-verified-beacon`)
as proof infrastructure is added.

## Evidence Generation (`apps/generator`, CLI `analyze`)
Expand All @@ -33,7 +38,7 @@ as proof infrastructure is added.
| Signature scheme coverage | api-sourced when unsupported signatures exist | Contract signatures / pre-approved hashes are not fully verified locally | Use on-chain or Safe-native validation when unsupported signatures appear |
| Safe owners and threshold | api-sourced (upgradable to proof-verified) | `confirmations` and `confirmationsRequired` in evidence reflect on-chain state | Include `onchainPolicyProof` to upgrade to proof-verified |
| On-chain policy proof | proof-verified when present, disabled otherwise | Merkle storage proofs for owners, threshold, nonce, modules, guard, fallback handler, singleton are valid against provided state root | Verify state root against finalized beacon chain consensus (Phase 4) |
| Transaction simulation | rpc-sourced by default, upgradeable to proof-verified | Generation-time RPC simulation/witness inputs may be wrong until witness and local replay checks pass | Include `simulationWitness` and run desktop replay verification to upgrade simulation trust |
| Transaction simulation | rpc-sourced | Generation-time RPC simulation/witness inputs may be wrong until witness and local replay checks pass | Include `simulationWitness` and run desktop replay verification to prove deterministic consistency against provided witness inputs. Trust remains `rpc-sourced` until replay world-state accounts are fully state-root proven |
| Decoded calldata metadata | api-sourced | Human-readable decode (`dataDecoded`) may be incorrect | Treat raw calldata + hash as canonical; decode independently when needed |
| Local settings labels | user-provided | Address/contract labels are accurate | Keep settings under change control and review diffs |

Expand All @@ -56,11 +61,15 @@ without these sections is fully supported and behaves identically to before.

From highest to lowest assurance:

1. **proof-verified**: Validated against cryptographic proofs (Merkle storage proofs, consensus proofs)
2. **self-verified**: Validated locally with deterministic code (EIP-712 hash, ECDSA recovery)
3. **rpc-sourced**: Accepted from an RPC endpoint (including generation-time simulation/witness inputs without full local replay confirmation)
4. **api-sourced**: Accepted from a remote API (Safe Transaction Service)
5. **user-provided**: Accepted from local operator input (URLs, settings, timestamps)
1. **consensus-verified-beacon**: State root verified with beacon BLS sync committee checks
2. **consensus-verified-opstack**: OP Stack envelope integrity checks (not beacon-equivalent)
3. **consensus-verified-linea**: Linea envelope integrity checks (not beacon-equivalent)
4. **consensus-verified** (legacy): Backward-compatibility label from older exports, superseded by mode-specific consensus labels in current output
5. **proof-verified**: Validated against cryptographic Merkle proofs
6. **self-verified**: Validated locally with deterministic code (EIP-712 hash, ECDSA recovery)
7. **rpc-sourced**: Accepted from an RPC endpoint (including generation-time simulation/witness inputs without full local replay confirmation)
8. **api-sourced**: Accepted from a remote API (Safe Transaction Service)
9. **user-provided**: Accepted from local operator input (URLs, settings, timestamps)

## Desktop Airgap Scope

Expand Down
16 changes: 15 additions & 1 deletion VERIFY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

How to confirm that a released binary matches the source code.

For release integrity policy and immutable tag checks, see [`RELEASE_INTEGRITY.md`](RELEASE_INTEGRITY.md).

## Checksum Verification

Every [GitHub release](https://github.com/Th0rgal/SafeLens/releases) includes a `SHA256SUMS.txt` file listing the SHA-256 hash of each artifact.
Expand Down Expand Up @@ -68,6 +70,18 @@ Release builds run in GitHub Actions ([`release.yml`](.github/workflows/release.

All CI runners use the same pinned toolchain versions (Bun 1.3.9, Rust 1.93.1) and `--frozen-lockfile` to ensure deterministic dependency resolution.

## Tag and Release Integrity

Before trusting a release artifact, verify the release tag points to the expected commit:

```bash
git fetch --tags origin
git rev-list -n 1 v0.4.0
git rev-parse origin/main
```

Then verify the published artifact checksum against `SHA256SUMS.txt`.

## Reproducibility Limitations

Tauri/Rust builds are not yet fully bit-for-bit reproducible across environments due to:
Expand All @@ -76,7 +90,7 @@ Tauri/Rust builds are not yet fully bit-for-bit reproducible across environments
- **Timestamps**: Some build tools embed timestamps in binaries
- **System libraries**: Linked system libraries (WebKit, OpenSSL) differ across OS versions

The `SHA256SUMS.txt` file in each release is the canonical reference. If you build locally and get a different hash, the above factors are the likely cause — not a supply-chain compromise. The source code itself can always be audited directly.
The `SHA256SUMS.txt` file in each release is the canonical reference. If local hashes differ, investigate code signing, timestamps, and system library differences before drawing conclusions.

## Reporting Discrepancies

Expand Down
Loading