From fa3bf5b889fea58de931d753e2f9f96eeb61f26b Mon Sep 17 00:00:00 2001 From: Pyronewbic Date: Tue, 19 May 2026 00:06:51 +0530 Subject: [PATCH 1/2] fix: disable ZAP issue creation (403 on GITHUB_TOKEN) --- .github/workflows/deploy.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bd4cebd..85e2ae5 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -147,6 +147,7 @@ jobs: target: https://api.casecomp.xyz/docs/spec.json format: openapi fail_action: false + allow_issue_writing: false cmd_options: '-a' - name: Upload DAST report if: always() From 5347f12eb9bfd65220bc3243c9075436cd740560 Mon Sep 17 00:00:00 2001 From: Pyronewbic Date: Wed, 20 May 2026 08:44:16 +0530 Subject: [PATCH 2/2] fix: add global JSON 404/error handlers, update docs to match current state ZAP DAST flagged 500s on unmatched routes (cloud metadata probes) and HTML Content-Type on non-API paths. Add catch-all JSON 404 handler and global error handler with safeErrorMessage(). Update README, internals, and env-vars docs: correct lib/ structure (auth/, cards/ split), fix test counts (434 total), add missing env vars (JWT_SECRET, ADMIN_SUB, TOGETHER_API_KEY, GOOGLE_OAUTH), fix Binary Auth from audit to enforced, remove stale v1/drops curl example. --- .superset/config.json | 8 ++++++++ README.md | 13 +++++-------- api.js | 9 +++++++++ docs/env-vars.md | 5 ++++- docs/internals.md | 29 +++++++++++++++-------------- 5 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 .superset/config.json diff --git a/.superset/config.json b/.superset/config.json new file mode 100644 index 0000000..16ccac3 --- /dev/null +++ b/.superset/config.json @@ -0,0 +1,8 @@ +{ + "setup": [ + "yarn install", + "[ ! -f .env ] && cp .env.example .env" + ], + "teardown": [], + "run": [] +} \ No newline at end of file diff --git a/README.md b/README.md index 045c9c3..d794d37 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,6 @@ curl "https://api.casecomp.xyz/api/portfolio/set/sv8a?demo=true" curl -H "Authorization: Bearer $CASECOMP_KEY" \ "https://api.casecomp.xyz/api/search?q=Pikachu+ex+SAR&source=magi&format=slab&slab_provider=PSA&slab_grade=10" -curl -H "Authorization: Bearer $CASECOMP_KEY" \ - "https://api.casecomp.xyz/v1/drops" - curl -X POST -H "Authorization: Bearer $CASECOMP_KEY" \ -H "Content-Type: application/json" \ -d '{"email":"you@email.com","query":"Umbreon ex SAR 217/187","type":"arbitrage","spreadThreshold":10}' \ @@ -128,7 +125,7 @@ curl -X POST -H "Authorization: Bearer $CASECOMP_KEY" \ - **SBOM** - Syft SPDX JSON generated per deploy, uploaded as build artifact - **Vulnerability scanning** - Grype scans SBOM for CVEs, SARIF uploaded to GitHub Security tab - **SAST** - CodeQL static analysis on every PR + weekly schedule -- **Binary Authorization** - GCP policy on both Cloud Run services (audit mode) +- **Binary Authorization** - GCP policy enforced on both Cloud Run services - **Reproducible builds** - Kaniko `--reproducible` flag, pinned version - **Multi-region** - Cloud Run in asia-south1 + us-central1, global LB auto geo-routes to nearest - **Custom base image** - Wolfi + apko Node 24 image, manual rebuild, zero CVEs by design @@ -214,13 +211,13 @@ Load unpacked from `extension/` in `chrome://extensions`. ## Tests -329 tests across three layers. CI required checks: unit + codeql. Smoke is non-blocking. +434 tests across three layers. CI required checks: unit + codeql. Smoke is non-blocking. | Suite | Count | Command | Covers | |-------|------:|---------|--------| -| **Unit** | 151 | `yarn test:unit` | Filters, grading, query builder, card identity, condition detection, image preprocessing, email alerts, portfolio ROI, CSV export, autocomplete, JWT auth, price trends | -| **API** | 104 | `yarn test:api` | Search, sold, PSA, grade, auth, admin keys, arbitrage, price history, alerts, share pages, portfolio CRUD, card view, upload-url, analytics, collection tracking | -| **Smoke** | 74 | `yarn test:smoke` | API root page, detail panel, tabs, PSA stats, arbitrage, mobile viewport, portfolio, autocomplete, search filters | +| **Unit** | 266 | `yarn test:unit` | Filters, grading, query builder, card identity, condition detection, image preprocessing, email alerts, portfolio ROI, CSV export, autocomplete, JWT auth, price trends | +| **API** | 97 | `yarn test:api` | Search, sold, PSA, grade, auth, admin keys, arbitrage, price history, alerts, share pages, portfolio CRUD, card view, upload-url, analytics, collection tracking | +| **Smoke** | 71 | `yarn test:smoke` | API root page, detail panel, tabs, PSA stats, arbitrage, mobile viewport, portfolio, autocomplete, search filters | ## Contributing diff --git a/api.js b/api.js index 63490b5..1bde09c 100644 --- a/api.js +++ b/api.js @@ -2369,6 +2369,15 @@ app.post("/api/keys/rotate", async (req, res) => { } }); +app.use((req, res) => { + res.status(404).json({ error: "Not found" }); +}); + +app.use((err, req, res, _next) => { + logError("unhandled", err.message, req.originalUrl, req.requestId); + res.status(500).json({ error: safeErrorMessage(err), requestId: req.requestId }); +}); + const PORT = process.env.API_PORT || 3000; app.listen(PORT, async () => { console.log(`Casecomp API listening on http://localhost:${PORT}`); diff --git a/docs/env-vars.md b/docs/env-vars.md index 888592c..fc67f1f 100644 --- a/docs/env-vars.md +++ b/docs/env-vars.md @@ -51,7 +51,10 @@ Copy **`.env.example`** to **`.env`** and fill in the required values. | `API_URL` | `http://localhost:3000` | API base URL (used in responses) | | `CASECOMP_API_KEY` | *(none)* | Owner API key (`CC_LIVE_` prefix, 60 req/min) | | `CASECOMP_SANDBOX_KEY` | *(none)* | Public sandbox key (`CC_LIVE_SANDBOX_` prefix, 5 req/min) | -| `GOOGLE_OAUTH_CLIENT_ID` | *(none)* | Google OAuth client ID (configured but not yet implemented) | +| `CASECOMP_JWT_SECRET` | *(none)* | Secret for signing/verifying JWT tokens (HS256) | +| `CASECOMP_ADMIN_SUB` | *(none)* | Google account `sub` claim for admin access | +| `GOOGLE_OAUTH_CLIENT_ID` | *(none)* | Google OAuth client ID for sign-in (popup flow) | +| `TOGETHER_API_KEY` | *(none)* | Together AI key for card detection (GLM-4.6V, falls back to Claude Sonnet) | ## Email notifications diff --git a/docs/internals.md b/docs/internals.md index 635a61d..b847cd7 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -19,21 +19,22 @@ lib/ preprocessing.js Card detection, corner crops, SSRF-safe image fetch psa.js PSA pop reports, cert lookup, grading signal psaTiers.js PSA submission tier data - data/ - firestore.js Firestore: grade logs, drops, webhooks, cache + auth/ + auth.js Google OAuth token verification, JWT (HS256) api-keys.js Developer key management - card-identity.js Canonical IDs, set resolution, SET_TOTAL_MAP + cards/ card-database.js TCGdex card DB (29K EN+JP cards), set browser, rarity - price-history.js Sold comp tracking + TCGPlayer seeding + card-identity.js Canonical IDs, set resolution, SET_TOTAL_MAP demo.js Sample data (3 multi-source cards) + grading-dataset.js ML slab image collection from eBay sold listings + price-history.js Sold comp tracking + TCGPlayer seeding + data/ + analytics.js Request analytics (Firestore, 30d TTL) cache.js File-based cache (legacy CLI) - redis-cache.js Redis cache (optional) - email.js Alert emails via Resend csv.js CSV export helpers - portfolio.js Portfolio CRUD (Firestore subcollection) - analytics.js Request analytics (Firestore, 30d TTL) - auth.js Google OAuth token verification, JWT (HS256) - grading-dataset.js ML slab image collection from eBay sold listings + email.js Alert emails via Resend + firestore.js Firestore: grade logs, drops, webhooks, cache + redis-cache.js Redis cache (optional) search/ filters.js Language, relevance, condition detection, outlier flagging listingQuery.js eBay search query builder (raw vs slab) @@ -47,9 +48,9 @@ public/admin/ Admin panel (keys, stats, errors) extension/ Chrome extension: queue auto-join, drop intel terraform/ GCP infra (Cloud Run, Firestore, LB, CDN, Scheduler) test/ - unit-test.js 172 unit tests - api-test.js ~130 API integration tests - smoke-test.js 74 Playwright UI smoke tests + unit-test.js 266 unit tests + api-test.js 97 API integration tests + smoke-test.js 71 Playwright UI smoke tests ``` ## API server @@ -58,7 +59,7 @@ test/ - **Auth middleware**: owner key (`CC_LIVE_`) → sandbox → JWT (Google OAuth) → Firestore developer keys (30s cache). `apiAuthMiddleware` adds demo bypass. - **Rate limiting**: 60/min authenticated, 360/min demo, 5/min sandbox, 10/min auth endpoint. -- **Security**: Helmet headers, trust proxy = 1, request IDs, compression, `safeErrorMessage()` on all errors. +- **Security**: Helmet headers, trust proxy = 1, request IDs, compression, `safeErrorMessage()` on all errors. Global JSON 404 catch-all + error handler at bottom of file. - **CORS**: wildcard `*` — API key is the access control layer. - **Dashboard**: static files from `public/` served at `/` and `/admin`. - **Docs**: Swagger UI at `/docs`, spec at `/docs/spec.json`.