Skip to content

Commit cc1d2d9

Browse files
author
AI Assistant
committed
merge: resolve conflicts with main for PR #1031
2 parents 2068644 + 9d890a0 commit cc1d2d9

574 files changed

Lines changed: 42503 additions & 41460 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ CERTIFICATE_CONTRACT_ID=
7979
# API Base URL
8080
API_BASE_URL=http://localhost:8080
8181

82+
# Token buyback indexer adapter URL (server-only). It must implement the
83+
# response documented in docs/api/tokenomics-buybacks.md. Do not point the
84+
# frontend directly at this URL, especially when it needs an API key.
85+
BUYBACK_INDEXER_URL=
86+
8287
# Certificate Metadata Base URL
8388
CERT_METADATA_BASE_URL=http://localhost:8080
8489

@@ -94,6 +99,11 @@ ISSUER_DID=did:stellar:GBRPYHIL2CI3FYQMWVUGE62KMGOBQKLCYJ3HLKBUBIW5VZH4S4MNOWT
9499
# Issuer Name
95100
ISSUER_NAME=Web3 Student Lab
96101

102+
# Comma-separated list of issuer DIDs authorized to revoke/reissue
103+
# certificates. Revoke/reissue requests from any other DID are rejected
104+
# with 403 and recorded in the audit log. Defaults to ISSUER_DID if unset.
105+
AUTHORIZED_ISSUER_DIDS=did:stellar:GBRPYHIL2CI3FYQMWVUGE62KMGOBQKLCYJ3HLKBUBIW5VZH4S4MNOWT
106+
97107
# ============================================
98108
# Logging Configuration
99109
# ============================================
@@ -140,9 +150,14 @@ DEBUG_CACHE=false
140150
# Enable debug logging for RPC calls
141151
DEBUG_RPC=false
142152

143-
# Enable CORS for development
144-
CORS_ENABLED=true
145-
CORS_ORIGIN=http://localhost:3000
153+
# ============================================
154+
# CORS Configuration
155+
# ============================================
156+
# Comma-separated list of allowed origins.
157+
# In production, set this explicitly to your deployed frontend URL(s).
158+
CORS_ORIGIN=http://localhost:3000,http://localhost:5173,http://localhost:8080
159+
# Optional extra dev origins; used when CORS_ORIGIN is unset (non-production only).
160+
# CORS_ALLOWED_DEV_ORIGINS=http://localhost:3000,http://localhost:5173
146161

147162
# ============================================
148163
# Webhook Delivery
@@ -159,3 +174,21 @@ WEBHOOK_MAX_ATTEMPTS=5
159174
WEBHOOK_BACKOFF_DELAY_MS=1000
160175
WEBHOOK_REQUEST_TIMEOUT_MS=10000
161176
WEBHOOK_WORKER_CONCURRENCY=25
177+
178+
# ============================================
179+
# Security Headers Configuration
180+
# ============================================
181+
182+
# Frontend URL for CORS and CSP
183+
FRONTEND_URL=http://localhost:3000
184+
185+
# Content Security Policy Configuration
186+
# Set to 'true' to enable report-only mode for testing CSP violations without blocking
187+
CSP_REPORT_ONLY=false
188+
189+
# CSP Report URI (optional) - endpoint to receive CSP violation reports
190+
# CSP_REPORT_URI=https://your-csp-report-endpoint.com/api/csp-reports
191+
192+
# Backend CSP Configuration
193+
# Set to 'true' to enable report-only mode for backend CSP
194+
BACKEND_CSP_REPORT_ONLY=false

.github/#72

Whitespace-only changes.

.github/#726.md

Whitespace-only changes.

.github/workflows/ci.yml

Lines changed: 131 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,45 @@ jobs:
4848
- name: Build
4949
working-directory: ./backend
5050
run: npm run build
51+
- name: Validate curriculum content
52+
working-directory: ./backend
53+
run: npm run validate:curriculum
54+
- name: Create shadow database
55+
# Rollback verification and drift detection (below) both need a
56+
# scratch "shadow" database distinct from test_db, on the same
57+
# Postgres service.
58+
run: |
59+
PGPASSWORD=test psql -h localhost -U test -d test_db \
60+
-c "CREATE DATABASE shadow_db;"
5161
- name: Prisma Generate & Migrate
5262
working-directory: ./backend
5363
env:
5464
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
5565
run: |
5666
npx prisma generate
57-
npx prisma migrate deploy || true
67+
npx prisma migrate deploy
5868
- name: Test Migration Rollbacks
5969
working-directory: ./backend
6070
env:
6171
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
72+
SHADOW_DATABASE_URL: postgresql://test:test@localhost:5432/shadow_db
6273
run: npm run test:migrations
74+
- name: Detect schema/migration drift
75+
# Reporting only, not yet a merge gate: schema.prisma has
76+
# substantial pre-existing drift from the migration history
77+
# (see PR description) that a single PR shouldn't be blocked on
78+
# resolving. Once that backlog is cleared with a dedicated
79+
# migration, remove `continue-on-error` so future drift fails CI.
80+
working-directory: ./backend
81+
continue-on-error: true
82+
env:
83+
DATABASE_URL: postgresql://test:test@localhost:5432/test_db
84+
SHADOW_DATABASE_URL: postgresql://test:test@localhost:5432/shadow_db
85+
run: |
86+
npx prisma migrate diff \
87+
--from-config-datasource \
88+
--to-schema prisma/schema.prisma \
89+
--exit-code
6390
- name: Run tests
6491
working-directory: ./backend
6592
env:
@@ -71,7 +98,23 @@ jobs:
7198
GITHUB_CLIENT_SECRET: test-github-client-secret
7299
GITHUB_REDIRECT_URI: http://localhost:8080/api/v1/oauth/github/callback
73100
FRONTEND_URL: http://localhost:3000
101+
# Pre-existing test failures unrelated to migrations (12 failing
102+
# suites as of this PR) — out of scope here; `|| true` intentionally
103+
# left in place rather than silently making this bypass narrower
104+
# without fixing what it's bypassing.
74105
run: npm run test:coverage || true
106+
- name: Dependency audit (backend)
107+
working-directory: ./backend
108+
run: npm audit --audit-level=high
109+
- name: Generate SBOM (backend)
110+
working-directory: ./backend
111+
run: npm sbom --output sbom.backend.json
112+
- name: Upload backend SBOM
113+
uses: actions/upload-artifact@v4
114+
with:
115+
name: sbom-backend
116+
path: backend/sbom.backend.json
117+
retention-days: 90
75118

76119
frontend:
77120
name: Frontend Build & Test
@@ -91,9 +134,66 @@ jobs:
91134
- name: Build
92135
working-directory: ./frontend
93136
run: npm run build
137+
- name: Dependency audit (frontend)
138+
working-directory: ./frontend
139+
run: npm audit --audit-level=high
140+
- name: Generate SBOM (frontend)
141+
working-directory: ./frontend
142+
run: npm sbom --output sbom.frontend.json
143+
- name: Upload frontend SBOM
144+
uses: actions/upload-artifact@v4
145+
with:
146+
name: sbom-frontend
147+
path: frontend/sbom.frontend.json
148+
retention-days: 90
149+
150+
frontend-e2e:
151+
name: Frontend E2E (Playwright)
152+
runs-on: ubuntu-latest
153+
steps:
154+
- name: Checkout code
155+
uses: actions/checkout@v4
156+
- name: Setup Node.js
157+
uses: actions/setup-node@v4
158+
with:
159+
node-version: '20'
160+
cache: 'npm'
161+
cache-dependency-path: frontend/package-lock.json
162+
- run: npm install
163+
- run: npm test
164+
- run: npm run build
165+
- run: npm run lint
166+
- name: Install dependencies
167+
working-directory: ./frontend
168+
run: npm ci
169+
- name: Install Playwright browsers
170+
working-directory: ./frontend
171+
run: npx playwright install --with-deps chromium
172+
- name: Run Playwright E2E tests
173+
working-directory: ./frontend
174+
# Scoped to chromium: the mobile-chrome project has pre-existing
175+
# responsive-layout gaps unrelated to this suite (e.g. the desktop-
176+
# only nav hides "Sign out" behind a hamburger menu on mobile
177+
# viewports) that predate this CI job and are a separate concern
178+
# from wallet-auth/protected-route coverage.
179+
run: npx playwright test --project=chromium
180+
- name: Upload Playwright HTML report
181+
if: always()
182+
uses: actions/upload-artifact@v4
183+
with:
184+
name: playwright-report
185+
path: frontend/playwright-report/
186+
retention-days: 14
187+
- name: Upload Playwright traces/screenshots/videos
188+
if: always()
189+
uses: actions/upload-artifact@v4
190+
with:
191+
name: playwright-test-results
192+
path: frontend/test-results/
193+
retention-days: 14
94194

95195
contracts:
96-
name: Contracts Build
196+
name: Contracts Build & Test
97197
runs-on: ubuntu-latest
98198
steps:
99199
- name: Checkout code
@@ -113,3 +213,32 @@ jobs:
113213
- name: Build
114214
working-directory: ./contracts
115215
run: cargo build
216+
- name: Test
217+
working-directory: ./contracts
218+
env:
219+
RUST_BACKTRACE: 1
220+
CARGO_TERM_COLOR: always
221+
run: cargo test -- --nocapture
222+
- name: Test (unit + property/fuzz)
223+
working-directory: ./contracts
224+
# Property tests use a fixed, bounded case count (see
225+
# ProptestConfig::with_cases in payment_gateway.rs) so CI runtime
226+
# stays short and predictable. A failing case is persisted by
227+
# proptest to contracts/proptest-regressions/*.txt, which must
228+
# be committed so the failure is deterministically reproducible;
229+
# see docs/contracts/FUZZING.md.
230+
run: cargo test --lib
231+
- name: Install cargo-audit
232+
uses: taiki-e/cargo-audit@main
233+
- name: Dependency audit (contracts)
234+
working-directory: ./contracts
235+
run: cargo audit --deny warnings
236+
- name: Generate SBOM (contracts)
237+
working-directory: ./contracts
238+
run: cargo sbom --output sbom.contracts.json 2>/dev/null || echo "SBOM generation skipped (install cargo-sbom via 'cargo install cargo-sbom')"
239+
- name: Upload contracts SBOM
240+
uses: actions/upload-artifact@v4
241+
with:
242+
name: sbom-contracts
243+
path: contracts/sbom.contracts.json
244+
retention-days: 90
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
name: Disaster Recovery Test (Automated Backup + Restore Verification)
2+
3+
on:
4+
schedule:
5+
- cron: '0 2 * * 0' # Weekly Sunday 02:00 UTC
6+
workflow_dispatch:
7+
pull_request:
8+
paths:
9+
- 'backend/src/jobs/backup*.ts'
10+
- '.github/workflows/disaster-recovery.yml'
11+
12+
jobs:
13+
disaster-recovery:
14+
name: Seed → Backup → Restore → Verify
15+
runs-on: ubuntu-latest
16+
services:
17+
postgres-primary:
18+
image: postgres:15
19+
env:
20+
POSTGRES_USER: test
21+
POSTGRES_PASSWORD: test
22+
POSTGRES_DB: primary_db
23+
options: >-
24+
--health-cmd pg_isready
25+
--health-interval 10s
26+
--health-timeout 5s
27+
--health-retries 5
28+
ports:
29+
- 5432:5432
30+
31+
postgres-restore:
32+
image: postgres:15
33+
env:
34+
POSTGRES_USER: test
35+
POSTGRES_PASSWORD: test
36+
POSTGRES_DB: restore_db
37+
options: >-
38+
--health-cmd pg_isready
39+
--health-interval 10s
40+
--health-timeout 5s
41+
--health-retries 5
42+
ports:
43+
- 5433:5432
44+
45+
steps:
46+
- name: Checkout
47+
uses: actions/checkout@v4
48+
49+
- name: Setup Node.js
50+
uses: actions/setup-node@v4
51+
with:
52+
node-version: '20'
53+
cache: 'npm'
54+
cache-dependency-path: backend/package-lock.json
55+
56+
- name: Install backend deps
57+
working-directory: ./backend
58+
run: npm ci
59+
60+
- name: Generate Prisma client
61+
working-directory: ./backend
62+
env:
63+
DATABASE_URL: postgresql://test:test@localhost:5432/primary_db
64+
run: npx prisma generate
65+
66+
- name: Migrate & Seed primary database
67+
working-directory: ./backend
68+
env:
69+
DATABASE_URL: postgresql://test:test@localhost:5432/primary_db
70+
run: |
71+
npx prisma migrate deploy
72+
npm run db:seed || npx tsx prisma/seed.ts || echo "Seed completed"
73+
74+
- name: Capture pre-backup counts
75+
run: |
76+
PGPASSWORD=test psql -h localhost -U test -d primary_db -c "
77+
SELECT 'users' AS entity, COUNT(*) FROM \"User\"
78+
UNION ALL SELECT 'courses', COUNT(*) FROM \"Course\"
79+
UNION ALL SELECT 'enrollments', COUNT(*) FROM \"Enrollment\"
80+
ORDER BY entity;
81+
" > /tmp/pre-backup-counts.txt
82+
cat /tmp/pre-backup-counts.txt
83+
84+
- name: Create backup using pg_dump (simulates backup.worker)
85+
run: |
86+
mkdir -p /tmp/backups
87+
PGPASSWORD=test pg_dump \
88+
--no-owner --no-acl --format=custom \
89+
-h localhost -U test -d primary_db \
90+
> /tmp/backups/dr-test-backup.dump
91+
ls -lh /tmp/backups/dr-test-backup.dump
92+
93+
- name: Restore into clean database
94+
run: |
95+
PGPASSWORD=test pg_restore \
96+
--no-owner --no-acl --clean --if-exists \
97+
-h localhost -p 5433 -U test -d restore_db \
98+
/tmp/backups/dr-test-backup.dump || true
99+
echo "Restore completed (exit code may be non-zero due to --clean)"
100+
101+
- name: Verify data integrity post-restore
102+
run: |
103+
PGPASSWORD=test psql -h localhost -p 5433 -U test -d restore_db -c "
104+
SELECT 'users' AS entity, COUNT(*) FROM \"User\"
105+
UNION ALL SELECT 'courses', COUNT(*) FROM \"Course\"
106+
UNION ALL SELECT 'enrollments', COUNT(*) FROM \"Enrollment\"
107+
ORDER BY entity;
108+
" > /tmp/post-restore-counts.txt
109+
cat /tmp/post-restore-counts.txt
110+
111+
echo "=== INTEGRITY CHECK ==="
112+
if diff /tmp/pre-backup-counts.txt /tmp/post-restore-counts.txt; then
113+
echo "✅ SUCCESS: All counts match post-restore"
114+
else
115+
echo "❌ FAILURE: Data mismatch detected"
116+
exit 1
117+
fi
118+
119+
- name: Cleanup temporary artifacts

.github/workflows/secret-scan.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
name: Secret Scanning
2+
3+
on:
4+
push:
5+
branches: [ "main", "master" ]
6+
pull_request:
7+
branches: [ "main", "master" ]
8+
9+
jobs:
10+
gitleaks:
11+
name: Gitleaks Secret Scan
12+
runs-on: ubuntu-latest
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v4
16+
with:
17+
fetch-depth: 0
18+
- name: Run gitleaks
19+
uses: gitleaks/gitleaks-action@v2
20+
env:
21+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,16 @@ frontend/tsc_errors.log
5454
frontend/tsc_errors_new.log
5555
frontend/tsc_errors_new2.log
5656
contracts/test_output_activity.txt
57+
contracts/test_output*.txt
58+
contracts/check_output*.json
59+
contracts/check_errors.txt
60+
contracts/errors*.json
61+
62+
# test snapshots
63+
**/__snapshots__/
64+
*.snap
65+
66+
# editor/OS junk
67+
.vscode/
68+
.idea/
69+
*.swp

0 commit comments

Comments
 (0)