aere-docs/AERE-SECURITY-FIXES.md
Aere Network e4cead319d Initial public release
Aere Network public source. Everything here can be checked against the live
chain (chain id 2800, https://rpc.aere.network).

Scope note, stated up front rather than buried: consensus on chain 2800 is
classical secp256k1 ECDSA QBFT. The post-quantum work in this repository is at
the signature, precompile, account and transport layers. Nothing here makes the
consensus post-quantum, and no document in it should be read as claiming so.
2026-07-20 01:01:36 +03:00

52 lines
6.3 KiB
Markdown

# Aere Network: adversarial-review fixes (pre-deploy hardening)
Source review: `aerenew/docs/AERE-SECURITY-REVIEW-NEW-CONTRACTS.md`
Repo: `aerenew/contracts` (solc 0.8.23, OpenZeppelin v4.9.x). All contracts founder-gated, not deployed.
Guard pattern for L1 copied from `contracts/pqfinality/AereFinalityCertificateVerifier.sol` (`gateway.code.length == 0` revert).
## Per-finding status
### M1 (MEDIUM) FIXED, with proof
File: `contracts/erc8004/AereReputationRegistry8004.sol`
- Made the adapter `Ownable` (OZ v4, `@openzeppelin/contracts/access/Ownable.sol`, matching the repo).
- Added `mapping(address => bool) public isAuthorizedFeedbackAuthor`, owner functions `addFeedbackAuthor` / `removeFeedbackAuthor` (with `FeedbackAuthorSet` event and a zero-address guard on add), and a new error `NotAuthorizedFeedbackAuthor(address)`.
- Gated `giveFeedback`: first line now reverts `NotAuthorizedFeedbackAuthor` unless `isAuthorizedFeedbackAuthor[msg.sender]`. The rest of the flow (agent-registered, delta clamp, adapter-is-attestor) is unchanged.
- Design choice (documented in NatSpec): owner-curated author allowlist, mirroring the live AereAIReputation curated-attestor model. Scope is authorization-only: the owner cannot set a reputation value, cannot exceed the `{-1,0,+1}` delta clamp every author is held to, and moves no funds. No re-entrancy, no fund/reputation-value backdoor. Read path (`getScore`, `getStats`, `liveKeyOf`, `canWriteThrough`) untouched.
### L1 (LOW) FIXED (x3), with proof
Added the sibling's codeless-gateway guard to each verify path. New error `VerifierHasNoCode()` in each.
- `contracts/mpc/AerePQAggregateVerifier.sol` constructor: reverts if `gateway.code.length == 0` (after the existing `ZeroGateway` check).
- `contracts/depin/AereComputeMarketV3.sol` constructor: reverts if `zkVerifier.code.length == 0` (after the existing `ZeroAddress` check).
- `contracts/compliance/AereBitstringStatusList.sol` constructor: reverts if `nonRevocationVerifier_ != address(0) && nonRevocationVerifier_.code.length == 0`. The zero address is still allowed (keeps the zk non-revocation path fail-closed OFF until configured), which preserves existing behavior. Verifier is immutable, so the constructor is the correct place.
### L2 (LOW) FIXED, with proof
File: `contracts/agentic/AereVectorStore.sol`
- Bound each paid-query receipt to an intended provider. Added `address provider` to the `QueryReceipt` struct.
- `paidQuery` now takes `address provider` (validated non-zero via existing `ZeroAddress`) and records it in the receipt. Signature is now `paidQuery(storeId, queryHash, provider, agentId, amount, nonce, deadline, authSig)`. The settlement/forwarding flow (AERE402 rail, fee to sink, delta-measured forward to recipient) is unchanged; `receiptId` formula unchanged.
- `attestRetrieval` priced branch now reverts new error `NotReceiptProvider()` unless `msg.sender == receipt.provider`, so a third party (or the payer) cannot front-run the honest provider and burn the receipt with a junk resultRoot. `getReceipt` view extended to also return `provider`.
### L3 (LOW) DOCUMENTED (accepted risk), not code-changed
File: `contracts/erc8004/AereAP2MandateVerifier.sol`
- Chose DOCUMENT over CODE-CHANGE, per the task's guidance, because the two candidate code fixes are unsuitable: (a) a per-authorization nonce would break the intended standing-mandate ("sign once, spend repeatedly up to the cap") semantics that the design and tests rely on; (b) "bind consumption to the executor" is already the built-in `SETTLEMENT_EXECUTOR` gate.
- Strengthened the contract-level and `authorizeSpend` NatSpec: the open accounting path (`SETTLEMENT_EXECUTOR == address(0)`) is verification/test-only and MUST NOT be used in production; production MUST set `SETTLEMENT_EXECUTOR` to the AERE402 facilitator so a mandate's cap is only ever consumed alongside a real settlement. No funds at risk (this contract holds none); the vector is cap-exhaustion griefing only.
- The existing test "enforces the settlement-executor gate when one is configured" already proves the mitigation works (a non-executor is rejected, the configured executor succeeds). No new test needed.
## Tests added / changed
- `test/erc8004-adapters.test.js`: `deployRep` now authorizes `other` as a feedback author (both branches). Two pre-existing tests that relied on the ungated `giveFeedback` were updated to call as the authorized `other` (legitimate behavior change from the M1 gate). Added new test "gates giveFeedback to owner-authorized feedback authors (M1)": unauthorized caller reverts `NotAuthorizedFeedbackAuthor` and writes nothing, owner authorizes -> same call succeeds and moves the live score to 10, owner revokes -> re-blocked, and only the owner may manage the allowlist.
- `test/AerePQAggregate.test.js`: added "rejects a code-less SP1 gateway at construction (L1 fail-open guard)" (EOA gateway reverts `VerifierHasNoCode`, zero reverts `ZeroGateway`, real mock deploys).
- `test/AereComputeMarketV3.test.js`: added "construction guards" > "rejects a code-less ZK verifier at construction (L1)" (EOA reverts `VerifierHasNoCode`, zero reverts `ZeroAddress`, real mock deploys).
- `test/aere-pq-screen.test.js`: added "rejects a non-zero code-less non-revocation verifier at construction, but allows zero (L1)" (EOA reverts `VerifierHasNoCode`; zero deploys and `nonRevocationConfigured()==false`; real mock deploys).
- `test/aere-vector-store.test.js`: the two `paidQuery` calls updated to pass `provider.address`; the paid-query test extended to assert the receipt is bound to the provider and that a third party (`operator`) and even the payer (`consumer`) are rejected `NotReceiptProvider` with the receipt left unconsumed, then the bound provider consumes it successfully (L2 proof).
## Real re-run results (npx hardhat test <file>, from aerenew/contracts)
- test/erc8004-adapters.test.js .... 19 passing
- test/aere-vector-store.test.js ... 8 passing
- test/AerePQAggregate.test.js ..... 18 passing
- test/AereComputeMarketV3.test.js . 14 passing
- test/aere-pq-screen.test.js ...... 13 passing
`npx hardhat compile`: green (compiled successfully, then "Nothing to compile" on re-run).
## Notes
- No contracts outside the findings were touched. No existing guard was weakened. The L1 zero-address behavior for AereBitstringStatusList was intentionally preserved. Prose contains no em-dashes or "--".