Skip to content

The metrics router exposes snapshot history over HTTP with no authentication — reads are unguarded by design #228

Closed

Description

@Jagadeeshftw

Priority: Medium  ·  Area: Observability / exposure  ·  Est. effort: 6–9 h

📌 Problem

src/routes/metrics.ts serves operational data — a MetricsSnapshot (metrics.ts:24) including counts such as "Total registered anchors, active or not" (:25) — plus a snapshot history endpoint returning { snapshots } (:116).

Both apiKeyAuth and rateLimiter guard only mutating methods; their shared MUTATING_METHODS set is POST, PUT, PATCH, DELETE. GET is therefore unauthenticated and unlimited on every route, including these.

Two consequences:

  1. Unauthenticated business intelligence. Anchor counts, liquidity totals and settlement volumes over time describe the operational state of the network. Whether that should be public is a decision nobody appears to have made explicitly — it is a side effect of the auth middleware only covering writes.
  2. Unbounded, unlimited history reads. A snapshot history endpoint with no rate limit is a cheap way to generate load, and depending on retention, a large response.

Establish the facts before proposing changes: what exactly the two endpoints return, whether history retention is bounded, and whether any field is sensitive.

🎯 Design decision required

State and defend:

  1. Public or protected? A metrics endpoint intended for a scraper inside the deployment boundary should not be internet-reachable unauthenticated; one intended as a public network-transparency feed legitimately is. Decide, and make the code reflect it.
  2. Granularity. If some fields are fine to expose and others are not, is the answer two endpoints, or field filtering by auth level? Argue it.
  3. Read-path limiting. Should rateLimiter cover reads — here specifically, or globally? Note this overlaps the rate-limiter issue; coordinate and say which PR owns the change.

🧩 Requirements and context

  • Do not change the metrics content without saying why; this is about exposure.
  • If you protect the endpoint, provide a documented path for a legitimate scraper — a separate credential or a bound interface — so monitoring does not silently break.
  • Any retention bound on snapshot history must be explicit and tested.
  • src/openapi.ts must reflect whatever auth requirement you land on.
  • All 42 test files must pass.

🛠️ Suggested execution

  1. Document exactly what both endpoints return and whether history is bounded.
  2. Classify each field as safe-to-expose or not.
  3. Implement your decision.
  4. Add tests for the auth behaviour and the retention bound.
  5. Update the OpenAPI spec and the operator documentation.

✅ Acceptance criteria

  • The PR documents the full response of both endpoints and whether retention is bounded.
  • Each field is classified as safe-to-expose or not, with reasoning.
  • The public-vs-protected decision is implemented and tested.
  • Snapshot history retention is explicitly bounded, with a test.
  • Monitoring has a documented path that still works.
  • src/openapi.ts reflects the auth requirement.
  • npm run lint, npm run build and npm test pass.

🚫 Out of scope

  • Adding new metrics.
  • Building a monitoring stack.
  • The general rate-limiter store work — separate issue.

🧪 Verification

npm ci
npm test src/routes
npm run lint && npm run build && npm test

📤 What your PR must include

  1. The endpoint response inventory and field classification.
  2. Your public-vs-protected decision.
  3. How a legitimate scraper still works.
  4. The retention-bound test.
  5. Closes #<n>.

🔒 Security notes

The underlying issue is that "authenticate mutating methods only" was applied uniformly without asking what each read endpoint discloses. Aggregate operational metrics over time reveal volume, timing and participant counts — useful to an operator and equally useful to someone probing the network before targeting it. The specific fix matters less than making the exposure decision deliberately rather than inheriting it from middleware defaults.

📋 Guidelines

  • Minimum 95% test coverage on changed lines
  • Clear documentation
  • Timeframe: 96 hours from assignment
  • One logical change per commit; no merge commits

💬 Join our community

Working on this, or want to sanity-check your approach before you start? Come and ask — the maintainers are there and happy to help.

Telegram: https://t.me/Grainlify

Activity

  1. added
    securitySecurity hardening
    Maybe RewardedGrantFox: potentially rewarded contribution
    GrantFox OSSGrantFox open-source program
    priority:mediumMedium difficulty / self-contained but non-trivial
    Third CampaignGrantFox third campaign issue
    on Aug 20, 2026
  2. jahswillb-dev commented on Aug 20, 2026

    @jahswillb-dev
    Contributor

    Hi Maintainer, i would want to hop on this issue. ETA 2hrs

  3. grantfox-oss commented on Aug 20, 2026

    @grantfox-oss

    🦊 GrantFox — @jahswillb-dev has been assigned to this issue as part of the Third Campaign campaign!

    Next steps:

    1. Open a Pull Request referencing this issue (e.g., Closes #228)
    2. Your PR will be reviewed by the AnchorNet-Org maintainers

    Good luck! Track your progress on GrantFox.

  4. grantfox-oss commented on Aug 29, 2026

    @grantfox-oss

    🎉 This issue has been marked as completed on GrantFox as part of the Third Campaign campaign!

    @jahswillb-dev's PR #235 was approved and merged by @Jagadeeshftw.

    🏆 @jahswillb-dev: You earned 35 FoxPoints for this contribution! Your current tier: Explorer (923 total points). Track your full progress on GrantFox.

    👏 Great work, @jahswillb-dev! Keep contributing to AnchorNet-Org.

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSGrantFox open-source programMaybe RewardedGrantFox: potentially rewarded contributionThird CampaignGrantFox third campaign issuepriority:mediumMedium difficulty / self-contained but non-trivialsecuritySecurity hardening

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions