Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CodeQL
on:
pull_request:
branches: [main]
schedule:
- cron: "0 6 * * 1"

permissions:
contents: read
security-events: write

jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5

- uses: github/codeql-action/init@v3
with:
languages: javascript-typescript

- uses: github/codeql-action/analyze@v3
3 changes: 3 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ jobs:
echo "digest=$DIGEST" >> "$GITHUB_OUTPUT"
echo "Image digest: $DIGEST"

- name: Configure Docker auth for GCR
run: gcloud auth configure-docker --quiet

- uses: sigstore/cosign-installer@v3

- name: Sign image (keyless)
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/terraform.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ jobs:
run: terraform fmt -check -recursive
working-directory: ${{ env.TF_DIR }}

- name: Write tfvars from secrets
run: |
cat > ${{ env.TF_DIR }}/ci.auto.tfvars <<EOF
alert_email = "${{ secrets.ALERT_EMAIL }}"
EOF

- name: Plan
id: plan
run: terraform plan -no-color -input=false -out=tfplan 2>&1 | tee plan.txt
Expand Down Expand Up @@ -93,6 +99,12 @@ jobs:
run: terraform init -input=false
working-directory: ${{ env.TF_DIR }}

- name: Write tfvars from secrets
run: |
cat > ${{ env.TF_DIR }}/ci.auto.tfvars <<EOF
alert_email = "${{ secrets.ALERT_EMAIL }}"
EOF

- name: Apply
run: terraform apply -auto-approve -input=false
working-directory: ${{ env.TF_DIR }}
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,17 @@
## Unreleased

### Added
- Set browser: GET /api/sets (238 sets with logos, era groups, card count breakdown) and GET /api/sets/:setCode (cards with rarity)
- Collection tracking: GET /api/portfolio/set/:setCode returns owned cardIds for progress bars and owned/missing indicators
- Set metadata from TCGdex: 236 named, 154 with logos, official/secret card count split
- Rarity data from TCGdex rarity-filter endpoints (~4K cards tagged: SAR, IR, UR, HR, SR, AR, RR, ACE, CR)
- Era classification: Scarlet & Violet, Sword & Shield, Sun & Moon, XY, Pokemon TCG Pocket, Diamond & Pearl, Black & White, Classic
- Sigstore container signing: cosign keyless signing via GitHub OIDC, deploy by image digest
- SBOM generation: Syft SPDX JSON uploaded as build artifact (90 day retention)
- Grype vulnerability scanning: SARIF report uploaded to GitHub Security tab
- Binary Authorization: DRYRUN audit policy on both Cloud Run services
- CodeQL SAST: static analysis on PRs + weekly schedule
- Terraform CI: plan posted as PR comment, auto-apply on merge to main
- Card autocomplete: GET /api/autocomplete with TCGdex EN+JP database (29K cards), card preview images, EN→JP name mapping
- Search filters: format (raw/slab), multi-select source pills, condition dropdown, slab provider+grade selectors
- Autocomplete dropdown on dashboard: card thumbnails, card preview panel on hover, keyboard navigation
Expand Down Expand Up @@ -77,7 +88,13 @@
- Card identity: cleaned up long names (strips pack names, condition text from titles)
- track-prices: now also tracks cards from active alerts, not just 3 hardcoded defaults
- Demo condition filter: checks detectedCondition in addition to raw condition field
- Tests: 290 total (128 unit + 88 API + 74 smoke), up from 183
- Tests: 300 total (130 unit + 96 API + 74 smoke), up from 183
- PR template: type, endpoints, checks, infrastructure, frontend impact, deploy notes
- Kaniko pinned to v1.23.2 with --reproducible builds, dual tags (latest + SHA)
- Deploy by image digest instead of :latest tag
- Health endpoint: eBay usage stats hidden from non-owner requests
- PSA endpoint: demo mode returns canned data (was making live API calls)
- All error responses use safeErrorMessage() (fixed 3 places leaking raw e.message)
- AI grading prompts: full PSA rubric (5-10), perspective correction, per-corner/edge detail, holo-specific surface guidance
- Demo grades re-evaluated with improved prompts (more conservative scores, honest confidence)
- Removed dead code: Redis import from api.js, updateCardField from card-identity.js
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,16 @@ curl "https://api.casecomp.xyz/api/portfolio/set/sv8a?demo=true"

`GET /api/health` | `GET /api/demo` | `GET /api/sitemap` | `GET /api/autocomplete` | `GET /api/sets` | `GET /api/sets/:setCode` | `GET /docs` | `GET /docs/spec.json` | `?demo=true` (sample data) on search/sold/arbitrage/price-history

## Security

- **Container signing** - Sigstore cosign keyless signing via GitHub OIDC, logged to Rekor transparency log
- **Deploy by digest** - immutable image SHA, not mutable `:latest` tag
- **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)
- **Reproducible builds** - Kaniko `--reproducible` flag, pinned version

## Claude Code Skills

**`/casecomp`** - search for cards in plain English. Claude parses intent and runs the CLI with the right flags.
Expand Down
15 changes: 15 additions & 0 deletions docs/internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,21 @@ Use `--refresh` to delete all cache files before a run.
5. Overall = minimum of all subgrades (matches PSA methodology).
6. Falls back to single combined prompt for non-Claude providers or missing back image.

## Security pipeline

Deploy workflow: Build (Kaniko) → Get digest → Sign (cosign keyless) → Deploy (by digest) → Scan (parallel).

| Tool | Stage | What |
|------|-------|------|
| Kaniko v1.23.2 | Build | Pinned version, `--reproducible`, dual tags (latest + SHA) |
| Cosign | Post-build | Keyless signing via GitHub OIDC → Sigstore Rekor |
| Syft | Post-deploy | SBOM generation (SPDX JSON), 90-day artifact retention |
| Grype | Post-deploy | CVE scan from SBOM, SARIF → GitHub Security tab |
| CodeQL | PR + weekly | SAST for JavaScript/TypeScript |
| Binary Auth | Cloud Run | GCP policy, DRYRUN audit mode (logs unsigned deploys) |

The scan job runs in parallel after deploy — adds zero time to the deploy critical path. CodeQL runs on PRs only (~60s).

## Scheduled tasks

Cloud Scheduler runs two jobs every 6 hours:
Expand Down
Loading