Skip to content

Commit 43a5282

Browse files
Merge branch 'main' into feature/issue-34-admin-issuer-endpoints
2 parents 3826ed2 + 5ce5521 commit 43a5282

83 files changed

Lines changed: 5323 additions & 104 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 & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ ISSUER_SECRET_KEY=
3939
# JWT signing secret — min 32 chars recommended; rotate to invalidate sessions
4040
JWT_SECRET=
4141

42+
# kid (key ID) for the current JWT signing key — increment on each rotation
43+
JWT_KEY_ID=1
44+
45+
# Comma-separated list of previous JWT secrets still valid for verification
46+
# during the transition window after a rotation. Remove entries once all
47+
# tokens signed with those secrets have expired.
48+
# Example: JWT_PREVIOUS_KEYS=oldSecret1,oldSecret2
49+
JWT_PREVIOUS_KEYS=
50+
4251
# ── Backend server ────────────────────────────────────────────────────────────
4352
# TCP port for the Express backend (default: 4000)
4453
PORT=4000
@@ -53,6 +62,16 @@ RATE_LIMIT_SEP10=10
5362
# Max public verify requests per IP per minute (default: 60)
5463
RATE_LIMIT_VERIFY=60
5564

65+
# ── Brute-force protection ────────────────────────────────────────────────────
66+
# Max failed /auth/verify attempts before blocking (default: 5)
67+
BRUTE_FORCE_MAX_ATTEMPTS=5
68+
69+
# Sliding window for counting failures in milliseconds (default: 600000 = 10 min)
70+
BRUTE_FORCE_WINDOW_MS=600000
71+
72+
# How long a blocked IP/wallet stays blocked in milliseconds (default: 900000 = 15 min)
73+
BRUTE_FORCE_BLOCK_MS=900000
74+
5675
# ── Audit log ─────────────────────────────────────────────────────────────────
5776
# Path to append-only NDJSON audit log (default: ./audit.log)
5877
AUDIT_LOG_PATH=./audit.log
@@ -64,6 +83,10 @@ ANALYTICS_PORT=8001
6483
# Base URL the analytics service uses to reach the backend (default set by Compose)
6584
BACKEND_URL=http://backend:4000
6685

86+
# API key required to access protected analytics endpoints (rates, issuers, anomalies)
87+
# Generate with: openssl rand -hex 32
88+
ANALYTICS_API_KEY=
89+
6790
# ── Backup service ────────────────────────────────────────────────────────────
6891
# S3 Bucket for analytics DB backup
6992
S3_BUCKET_NAME=
@@ -87,7 +110,19 @@ AWS_SECRET_NAME=
87110
# AWS region for Secrets Manager (default: us-east-1)
88111
AWS_REGION=us-east-1
89112

90-
# ── Patient consent ───────────────────────────────────────────────────────────
113+
# ── Admin multi-signature ─────────────────────────────────────────────────────
114+
# Number of approvals required for critical admin operations (default: 2)
115+
MULTISIG_THRESHOLD=2
116+
117+
# Comma-separated list of wallet addresses authorised to approve proposals.
118+
# Leave empty to allow any admin-role JWT holder to approve.
119+
# Example: MULTISIG_KEY_HOLDERS=GABC...,GDEF...,GHIJ...
120+
MULTISIG_KEY_HOLDERS=
121+
122+
# How long a pending proposal stays valid in milliseconds (default: 3600000 = 1 hour)
123+
MULTISIG_PROPOSAL_TTL_MS=3600000
124+
125+
91126
# Set to 'false' to waive consent requirement (e.g. jurisdiction config).
92127
# Default: true (consent required before minting)
93128
REQUIRE_PATIENT_CONSENT=true

.github/workflows/ci.yml

Lines changed: 122 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,32 @@ jobs:
3333
working-directory: contracts
3434
run: make build
3535

36-
- name: Run contract tests
36+
- name: Run contract tests with coverage
3737
working-directory: contracts
38-
run: make test
38+
run: |
39+
cargo install cargo-tarpaulin
40+
make test-coverage
41+
42+
- name: Upload Contract Coverage to Codecov
43+
uses: codecov/codecov-action@v4
44+
with:
45+
files: contracts/tarpaulin-report.xml
46+
flags: contracts
47+
token: ${{ secrets.CODECOV_TOKEN }}
48+
49+
- name: Install cargo-audit
50+
run: cargo install cargo-audit
51+
52+
- name: Dependency audit
53+
working-directory: contracts
54+
run: cargo audit | tee audit-report.txt
55+
56+
- name: Upload audit report
57+
if: always()
58+
uses: actions/upload-artifact@v4
59+
with:
60+
name: contract-audit-report
61+
path: contracts/audit-report.txt
3962

4063
backend:
4164
name: Node.js backend
@@ -54,9 +77,83 @@ jobs:
5477
working-directory: backend
5578
run: npm ci
5679

57-
- name: Run tests
80+
- name: Run tests with coverage
81+
working-directory: backend
82+
run: npm run test:coverage
83+
84+
- name: Upload Backend Coverage to Codecov
85+
uses: codecov/codecov-action@v4
86+
with:
87+
files: backend/coverage/lcov.info
88+
flags: backend
89+
token: ${{ secrets.CODECOV_TOKEN }}
90+
91+
frontend:
92+
name: Node.js frontend
93+
runs-on: ubuntu-latest
94+
steps:
95+
- uses: actions/checkout@v4
96+
97+
- name: Setup Node.js
98+
uses: actions/setup-node@v4
99+
with:
100+
node-version: 18
101+
cache: npm
102+
cache-dependency-path: frontend/package-lock.json
103+
104+
- name: Install dependencies
105+
working-directory: frontend
106+
run: npm install --legacy-peer-deps
107+
108+
- name: Run tests with coverage
109+
working-directory: frontend
110+
run: npm run test:coverage
111+
112+
- name: Upload Frontend Coverage to Codecov
113+
uses: codecov/codecov-action@v4
114+
with:
115+
files: frontend/coverage/lcov.info
116+
flags: frontend
117+
token: ${{ secrets.CODECOV_TOKEN }}
118+
119+
- name: Dependency audit
58120
working-directory: backend
59-
run: npm test
121+
run: npm audit --audit-level=high | tee audit-report.txt
122+
123+
- name: Upload audit report
124+
if: always()
125+
uses: actions/upload-artifact@v4
126+
with:
127+
name: backend-audit-report
128+
path: backend/audit-report.txt
129+
130+
frontend:
131+
name: Node.js frontend
132+
runs-on: ubuntu-latest
133+
steps:
134+
- uses: actions/checkout@v4
135+
136+
- name: Setup Node.js
137+
uses: actions/setup-node@v4
138+
with:
139+
node-version: 18
140+
cache: npm
141+
cache-dependency-path: frontend/package-lock.json
142+
143+
- name: Install dependencies
144+
working-directory: frontend
145+
run: npm ci
146+
147+
- name: Dependency audit
148+
working-directory: frontend
149+
run: npm audit --audit-level=high | tee audit-report.txt
150+
151+
- name: Upload audit report
152+
if: always()
153+
uses: actions/upload-artifact@v4
154+
with:
155+
name: frontend-audit-report
156+
path: frontend/audit-report.txt
60157

61158
python:
62159
name: Python analytics service
@@ -75,10 +172,30 @@ jobs:
75172
working-directory: python-service
76173
run: pip install -r requirements.txt
77174

78-
- name: Run tests
175+
- name: Run tests with coverage
79176
working-directory: python-service
80177
run: pytest
81178

179+
- name: Upload Python Coverage to Codecov
180+
uses: codecov/codecov-action@v4
181+
with:
182+
files: python-service/coverage.xml
183+
flags: python
184+
token: ${{ secrets.CODECOV_TOKEN }}
185+
- name: Install pip-audit
186+
run: pip install pip-audit
187+
188+
- name: Dependency audit
189+
working-directory: python-service
190+
run: pip-audit -r requirements.txt | tee audit-report.txt
191+
192+
- name: Upload audit report
193+
if: always()
194+
uses: actions/upload-artifact@v4
195+
with:
196+
name: python-audit-report
197+
path: python-service/audit-report.txt
198+
82199
docker:
83200
name: Docker build validation
84201
runs-on: ubuntu-latest

.github/workflows/e2e.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,35 @@ jobs:
8989
name: test-videos
9090
path: frontend/test-results/
9191
retention-days: 7
92+
93+
visual:
94+
name: Visual regression tests
95+
timeout-minutes: 30
96+
runs-on: ubuntu-latest
97+
steps:
98+
- uses: actions/checkout@v4
99+
100+
- uses: actions/setup-node@v4
101+
with:
102+
node-version: '18'
103+
104+
- name: Install frontend dependencies
105+
run: cd frontend && npm install
106+
107+
- name: Install Playwright Chromium
108+
run: cd frontend && npx playwright install chromium --with-deps
109+
110+
- name: Run visual regression tests
111+
run: cd frontend && npx playwright test e2e/visual.spec.js --project=chromium
112+
env:
113+
CI: true
114+
115+
- name: Upload visual diff artifacts
116+
if: failure()
117+
uses: actions/upload-artifact@v4
118+
with:
119+
name: visual-diff-results
120+
path: |
121+
frontend/test-results/
122+
frontend/playwright-report/
123+
retention-days: 14

.github/workflows/gitleaks.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Secret Scanning with Gitleaks
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- develop
8+
pull_request:
9+
branches:
10+
- main
11+
- develop
12+
13+
jobs:
14+
gitleaks:
15+
name: Scan for Secrets
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0 # Fetch all history for all branches and tags
23+
24+
- name: Run Gitleaks
25+
uses: gitleaks/gitleaks-action@v2
26+
env:
27+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28+
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }} # Optional: for Gitleaks Pro
29+
30+
- name: Upload Gitleaks Report
31+
if: failure()
32+
uses: actions/upload-artifact@v4
33+
with:
34+
name: gitleaks-report
35+
path: gitleaks-report.json
36+
retention-days: 30

.gitleaks.toml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
title = "VacciChain Gitleaks Configuration"
2+
3+
[extend]
4+
# Use default gitleaks rules as base
5+
useDefault = true
6+
7+
# Custom rules for VacciChain-specific secrets
8+
[[rules]]
9+
id = "stellar-secret-key"
10+
description = "Stellar Secret Key"
11+
regex = '''S[A-Z0-9]{55}'''
12+
tags = ["stellar", "secret", "key"]
13+
14+
[[rules]]
15+
id = "stellar-private-key"
16+
description = "Stellar Private Key (alternative format)"
17+
regex = '''(?i)(stellar[_-]?secret|stellar[_-]?private)[_-]?key["\s:=]+[A-Z0-9]{56}'''
18+
tags = ["stellar", "secret", "key"]
19+
20+
[[rules]]
21+
id = "jwt-secret"
22+
description = "JWT Secret"
23+
regex = '''(?i)(jwt[_-]?secret|jwt[_-]?key)["\s:=]+[A-Za-z0-9+/=]{32,}'''
24+
tags = ["jwt", "secret"]
25+
26+
[[rules]]
27+
id = "soroban-secret"
28+
description = "Soroban Secret Key"
29+
regex = '''(?i)(soroban[_-]?secret|soroban[_-]?key)["\s:=]+[A-Za-z0-9+/=]{32,}'''
30+
tags = ["soroban", "secret"]
31+
32+
[[rules]]
33+
id = "api-key-generic"
34+
description = "Generic API Key"
35+
regex = '''(?i)(api[_-]?key|apikey)["\s:=]+[A-Za-z0-9_\-]{20,}'''
36+
tags = ["api", "key"]
37+
38+
# Allowlist for false positives
39+
[allowlist]
40+
description = "Allowlist for known false positives"
41+
paths = [
42+
'''\.env\.example$''',
43+
'''env\.example$''',
44+
'''\.md$''',
45+
'''test_.*\.js$''',
46+
'''.*\.test\.js$''',
47+
'''.*\.spec\.js$''',
48+
'''__mocks__/.*''',
49+
]
50+
51+
# Allowlist specific patterns that are not real secrets
52+
regexes = [
53+
'''EXAMPLE_.*''',
54+
'''TEST_.*''',
55+
'''DEMO_.*''',
56+
'''your-.*-here''',
57+
'''replace-with-.*''',
58+
'''<.*>''',
59+
'''SXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX''',
60+
]
61+
62+
# Stop words that indicate example/placeholder values
63+
stopwords = [
64+
"example",
65+
"sample",
66+
"test",
67+
"demo",
68+
"placeholder",
69+
"your-secret-here",
70+
"replace-me",
71+
]

.pre-commit-config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
repos:
2+
- repo: https://github.com/gitleaks/gitleaks
3+
rev: v8.18.4
4+
hooks:
5+
- id: gitleaks
6+
name: Detect hardcoded secrets
7+
description: Detect hardcoded secrets using Gitleaks
8+
entry: gitleaks protect --verbose --redact --staged
9+
language: golang
10+
pass_filenames: false

0 commit comments

Comments
 (0)