Skip to content

Enforce READ_ONLY write blocking and add smoke coverage#1549

Merged
TheLastCicada merged 11 commits intov2-rc2from
observer-readonly-hardening
Mar 25, 2026
Merged

Enforce READ_ONLY write blocking and add smoke coverage#1549
TheLastCicada merged 11 commits intov2-rc2from
observer-readonly-hardening

Conversation

@TheLastCicada
Copy link
Copy Markdown
Contributor

@TheLastCicada TheLastCicada commented Mar 24, 2026

Summary

  • enforce READ_ONLY behavior at middleware level for non-GET/HEAD/OPTIONS requests and keep explicit controller guards for GET sync endpoints that can still write
  • standardize read-only failures to a canonical 403 response payload and apply missing V1/V2 controller checks for organization, governance, offer, and filestore paths
  • remove broken/unsafe route contracts by deleting V1 governance subscribe and removing POST /v2/filestore/get_file in favor of GET path-param usage, with updated validations and integration tests
  • add dedicated READ_ONLY test coverage (test:v2:readonly, new integration spec, live-api smoke spec) plus a gated live smoke workflow for self-hosted runners

Test plan

  • npm run test:v2:readonly
  • bash tests/run-tests.sh npx cross-env NODE_ENV=test USE_SIMULATOR=true CW_PORT=31311 mocha --loader node_modules/extensionless/src/register.js tests/v2/integration/filestore-v2.spec.js --reporter spec --exit --timeout 300000
  • npm run test:live:readonly:smoke (requires live environment with READ_ONLY=true)

Note

Medium Risk
Changes request handling and API contracts (middleware-level write blocking, new 403 payload, and removal of routes), which can break existing clients or alter behavior in production. Risk is mitigated by added integration and live smoke tests but still touches core request paths.

Overview
Enforces READ_ONLY mode across the API by blocking non-GET/HEAD/OPTIONS requests in src/middleware.js and standardizing failures to a canonical 403 JSON payload via new src/utils/read-only-response.js.

Adds missing read-only guards + consistent error handling to multiple V1/V2 controllers (governance, organizations, offers, filestore) and updates org read responses to omit wallet-derived fields (xchAddress/balance) when read-only.

Tightens API contracts by removing V1 governance POST /subscribe, and changing V2 filestore get_file to GET /v2/filestore/get_file?fileId=... (dropping the legacy POST/body behavior), with corresponding validation + integration test updates.

Adds new test entrypoints (test:v2:readonly, test:live:readonly:smoke), new read-only enforcement and live smoke specs, and a new GitHub Actions job to run the live read-only smoke flow against a real Chia environment.

Written by Cursor Bugbot for commit 77c536a. This will update automatically on new commits. Configure here.

Harden observer-node behavior by blocking write methods in middleware,
closing known controller gaps, and unifying read-only failures to a
canonical 403 payload. This prevents write side effects in read-only
mode while preserving explicit controller guards for GET endpoints that
can still trigger writes.
Avoid mapping unrelated 403 errors to the canonical read-only response by
matching only explicit READ_ONLY-coded errors, and ensure V1 filestore
delete maps read-only assertion failures to the shared 403 payload.
Avoid path-segment parsing issues for base64 file IDs by accepting
`fileId` on the GET query string instead of as a URL path parameter.
This preserves the POST removal while keeping lookups reliable through
proxy layers that normalize encoded slashes.
Drop unused V2 filestore path-param validation and remove an unreachable
READ_ONLY error branch from V1 getFileList to keep read-only mapping
focused on actual write-guarded handlers.
Ensure write requests in READ_ONLY mode always return the canonical 403
by checking read-only state before wallet availability assertions in the
common middleware chain. Also add explicit workflow token permissions for
the read-only live smoke job to satisfy code scanning requirements.
Keep write blocking in the common-assertions middleware and retain the
header-only READ_ONLY middleware behavior to avoid duplicated config
checks and unreachable code paths.
Run read-only live smoke in the shared tests pipeline so it follows the
same trigger and execution pattern as other live-api jobs, while keeping
its lightweight setup by skipping faucet funding and governance/mirror
bootstrap dependencies.
Delete the unused V1 governance subscribe validation schema left behind
after removing the deprecated subscribe route so the validation surface
matches active endpoints.
Map READ_ONLY assertion errors to the shared 403 payload in V1 and V2
GET offer generation handlers so read-only mode is enforced consistently
for GET endpoints that can trigger write-side behavior.
Broaden the live read-only smoke suite with representative write and
write-like GET endpoint canaries across v1 and v2.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Align the successful unsubscribe response text with the 200 status to
avoid client confusion when parsing operation results.
@TheLastCicada TheLastCicada merged commit 4012d52 into v2-rc2 Mar 25, 2026
27 checks passed
@TheLastCicada TheLastCicada deleted the observer-readonly-hardening branch March 25, 2026 16:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants