Skip to content

fix(subscription): close fake-Pro fall-through path on /api/subscription#182

Merged
operatoruplift merged 1 commit intomasterfrom
subscription-honesty
Apr 27, 2026
Merged

fix(subscription): close fake-Pro fall-through path on /api/subscription#182
operatoruplift merged 1 commit intomasterfrom
subscription-honesty

Conversation

@operatoruplift
Copy link
Copy Markdown
Owner

Real security gap closed

The legacy "confirm" fall-through path on POST /api/subscription was a real fake-Pro vulnerability. Any authenticated user posting:

POST /api/subscription
Authorization: Bearer <valid-privy-jwt>
Content-Type: application/json

{ "tx_signature": "anything-here" }

…would be marked tier=pro, status=active without any on-chain verification. The TODO at line 159 admitted it: "Verify the Solana tx on-chain before activating. For devnet/demo, we trust the client-provided signature."

Same fabrication class as the council PRs already cleaned up (#147, #155, #156) and the dashboard fabrication PRs (#164, #167, #173) — but worse, because this one let users claim a paid subscription without paying.

What about the real callers?

Zero calls in the codebase use this path:

grep -rn "tx_signature.*subscription" --include="*.ts" --include="*.tsx" | grep -v tests
# (no hits)
  • The paywall page (app/(auth)/paywall/page.tsx) uses action="create_invoice" exclusively.
  • Real on-chain settlement runs through /api/access/verify-payment, which does call verifyPayment(reference) against the Solana RPC.

What this PR does

Closes the path with HTTP 410 Gone + a nextAction pointing callers at /api/access/verify-payment, the route that actually verifies the on-chain tx.

The two known-good actions (create_invoice and dev_simulate) are unchanged.

Verified

pnpm exec tsc --noEmit
# exit 0

pnpm check
# 3 passed, 0 failed

grep -rn "tx_signature.*subscription" --include="*.ts" --include="*.tsx" app/ src/ \
  | grep -v "tests/\|//.*"
# 0 callers

Net diff

1 file changed, 18 insertions(+), 28 deletions(-)

Rollback

Single git revert. Note: rolling this back re-introduces the fake-Pro path; investigate the regression before reverting.

The legacy "confirm" fall-through path on POST /api/subscription was
a real security gap. Any authenticated user posting:

    POST /api/subscription
    Authorization: Bearer <valid-privy-jwt>
    Content-Type: application/json

    { "tx_signature": "anything-here" }

would be marked tier=pro, status=active without any on-chain
verification. The TODO at line 159 admitted it: "Verify the Solana
tx on-chain before activating. For devnet/demo, we trust the
client-provided signature." Same fabrication class as the council
PRs cleaned up (#147, #155, #156, #164, #167, #173).

No code path calls this fall-through (`grep -rn "tx_signature.*\
subscription"` returns zero in app/, src/). The paywall page uses
action="create_invoice" exclusively, and real settlement runs
through /api/access/verify-payment which DOES call verifyPayment()
against the Solana RPC.

Closed the path with HTTP 410 Gone + a nextAction pointing callers
at /api/access/verify-payment, the route that actually verifies the
on-chain tx.

The two known-good actions ("create_invoice" and "dev_simulate")
are unchanged.

## Verified

- pnpm exec tsc --noEmit: clean
- pnpm check: 3 passed, 0 failed
- grep -rn "tx_signature.*subscription" returns 0 callers

## Rollback

Single git revert. One file.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
website Ready Ready Preview, Comment Apr 27, 2026 4:14pm

@operatoruplift operatoruplift merged commit 6f9d98a into master Apr 27, 2026
4 checks passed
operatoruplift added a commit that referenced this pull request Apr 28, 2026
…#209)

Both routes were serving hardcoded "demo until API key configured"
data with zero internal callers:

- /api/risk -> lib/webacy-risk.ts returned overall: 87, grade: 'A',
  flagged: false, sanctions: false for any wallet/contract/transaction.
  A "your wallet is safe" answer regardless of input.

- /api/gold -> lib/oro-grail.ts returned balanceOz: 0.0847,
  balanceUsd: 278.24 — exactly the values that
  tests/e2e/dashboard-honesty.spec.ts bans (Gold Agent retired in #164).
  The dashboard widget was retired but the API + lib survived.

Both routes now return 410 Gone with a clear nextAction, mirroring the
pattern from #173 (x402 charge) and #182 (subscription confirm).

Both libs deleted (no callers).

DD.xyz integration entry on /integrations: status updated from
"available" to "coming_soon" since the lib backing it returned fake
data. Now matches the truth.

tests/e2e/request-id-runtime.spec.ts: /api/risk expectStatus updated
[400, 401] -> [401, 410]. Added /api/gold probe with [401, 410].
Hermetic spec count stays at 17 (just adds a probe to an existing spec).
operatoruplift added a commit that referenced this pull request Apr 28, 2026
Adds a fourth grep-guard alongside copy-check, capability-check, and
trust-gate. Each rule encodes a fabrication pattern we've explicitly
retired and points at the original cleanup PR so a future contributor
can read the prior agreement before reintroducing it.

10 rules covering:
- Gold Agent fake balances (0.0847 oz, $278.24, $3284.50/oz) retired in #209
- Webacy fake "wallet grade A, not flagged, not sanctioned" risk grades retired in #209
- Fabricated x402-devnet-${Date.now()} tx signature pattern retired in #173
- Random-vector fake (Math.floor(Math.random() * 2000) + 100) on /memory retired in #210
- Fake-Pro confirm fall-through tx_signature pattern retired in #182
- Pre-seeded fake "Operator Uplift Architecture" memory node retired in #210
- "API key generated (expires in 30 days)" toast lie retired in #212
- 2-second setTimeout fake-install alert on /store retired in #216

Comment-line heuristic skips lines starting with //, /*, *, or <!--
so the audit-trail comments documenting these retirements (e.g. the
new /api/gold route's comment block explaining why the lib was
deleted) don't trigger false positives.

Wired into pnpm check via scripts/check.mjs and added a top-level
script `pnpm fabrication-rot-check` for direct invocation.

Sanity-tested both directions:
- Clean state: 197 files scanned, 0 hits
- Plant a 'balanceOz: 0.0847' in lib/: guard fires with the rule name
  and the retiring-PR pointer, exit code 1.
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.

1 participant