Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
b071072
chore: enforce formatting in CI
sepaseh Jul 30, 2026
bf8f541
chore: declare Node.js version
sepaseh Jul 30, 2026
df9381a
docs: add repository contribution policies
sepaseh Jul 30, 2026
48cf147
refactor: centralize API error messages
sepaseh Jul 30, 2026
4e71996
build: validate production environment
sepaseh Jul 30, 2026
779452c
chore: enforce dependency security audit
sepaseh Jul 30, 2026
9036ee0
chore: update dependencies and Node runtime
sepaseh Jul 30, 2026
65e4486
chore: configure automated dependency updates
sepaseh Jul 30, 2026
976f01b
ci: pin GitHub Actions to immutable revisions
sepaseh Jul 30, 2026
70d7e22
ci: add CodeQL security scanning
sepaseh Jul 30, 2026
73e3249
ci: integrate SonarQube analysis
sepaseh Jul 30, 2026
48854d1
test: cover API authentication lifecycle
sepaseh Jul 30, 2026
2a6412d
test: cover login journey end to end
sepaseh Jul 30, 2026
97bbe62
docs: define SonarQube quality gate
sepaseh Jul 30, 2026
7c609e7
docs: define main branch protection
sepaseh Jul 30, 2026
f0fd67e
test token handling
sepaseh Jul 30, 2026
1715711
test storage utilities
sepaseh Jul 30, 2026
138ce17
test routing permissions
sepaseh Jul 30, 2026
d0ed5c7
test API error handling
sepaseh Jul 30, 2026
ab7df50
test authentication refresh failures
sepaseh Jul 30, 2026
0f94910
test priority forms and pages
sepaseh Jul 30, 2026
67f533b
test authentication journeys
sepaseh Jul 30, 2026
2af82cf
test role and user management
sepaseh Jul 30, 2026
d384755
test application accessibility
sepaseh Jul 30, 2026
a6198b5
test localized critical journeys
sepaseh Jul 30, 2026
bf20247
test cross-browser coverage
sepaseh Jul 30, 2026
897579b
test add visual regression coverage
sepaseh Jul 30, 2026
63b0a07
perf split application bundle
sepaseh Jul 30, 2026
b1a6492
perf enforce performance budgets
sepaseh Jul 30, 2026
42889e9
security enforce production headers
sepaseh Jul 30, 2026
aa73007
test add deployment smoke checks
sepaseh Jul 30, 2026
8c1d023
feat add structured observability
sepaseh Jul 30, 2026
c719881
chore: establish automated releases
sepaseh Jul 30, 2026
a6605ea
docs: define release rollback procedures
sepaseh Jul 30, 2026
b1ab9e0
test: add staging validation gate
sepaseh Jul 30, 2026
c8aa8a6
security: add threat modeling and DAST
sepaseh Jul 30, 2026
7d94379
test: add mutation and API contract gates
sepaseh Jul 30, 2026
cb50735
style: bundle local Vazirmatn font
sepaseh Jul 30, 2026
fc68a9d
ci: require SonarQube quality gate configuration
sepaseh Jul 31, 2026
d5e2f99
test: isolate Vitest environment configuration
sepaseh Jul 31, 2026
5193ab8
test: raise production coverage baseline
sepaseh Jul 31, 2026
783adc6
fix: prevent email redaction regex backtracking
sepaseh Jul 31, 2026
f474eea
fix: prevent query redaction regex backtracking
sepaseh Jul 31, 2026
faa13a9
fix: validate untrusted API error responses
sepaseh Jul 31, 2026
e058e3a
refactor: reduce API interceptor complexity
sepaseh Jul 31, 2026
f536c9f
refactor: normalize error boundary render type
sepaseh Jul 31, 2026
f3c31e5
refactor: sanitize OTP input props
sepaseh Jul 31, 2026
5e7063a
refactor: normalize route wrapper return type
sepaseh Jul 31, 2026
546d197
refactor: remove redundant permission type alias
sepaseh Jul 31, 2026
0b1f086
test: centralize synthetic E2E credentials
sepaseh Jul 31, 2026
deab43e
fix: configure SonarQube organization
sepaseh Jul 31, 2026
7bb1207
fix: satisfy Sonar quality analysis
sepaseh Jul 31, 2026
abb2e93
test: raise coverage for routing and observability
sepaseh Jul 31, 2026
1f4ec49
test: fix performance entry mock type
sepaseh Jul 31, 2026
ec3d23b
ci: stabilize Lighthouse first paint budget
sepaseh Jul 31, 2026
6db617e
fix(ci): skip SonarQube for fork pull requests
sepaseh Jul 31, 2026
e0055f0
fix(ci): pin ZAP image digest
sepaseh Jul 31, 2026
547f67b
ci: account for Lighthouse runner variance
sepaseh Jul 31, 2026
86660f8
fix(ci): disable staging checkout credentials
sepaseh Jul 31, 2026
fcdbf62
test(contract): require account authorization
sepaseh Jul 31, 2026
2f456fd
docs: correct required check contexts
sepaseh Jul 31, 2026
e1f60fc
docs: align referrer policy description
sepaseh Jul 31, 2026
d851308
fix: align Node.js engine support
sepaseh Jul 31, 2026
bf730f1
docs(security): complete authentication review record
sepaseh Jul 31, 2026
cc7e47f
fix(security): restrict DAST to staging
sepaseh Jul 31, 2026
ea29331
fix(performance): respect application base path
sepaseh Jul 31, 2026
e490cb0
fix(a11y): preserve required OTP semantics
sepaseh Jul 31, 2026
fb15b22
fix(styles): unquote Vazirmatn family name
sepaseh Jul 31, 2026
9d5a36b
test: document ResizeObserver no-ops
sepaseh Jul 31, 2026
31787e5
fix(types): include staging and contract configs
sepaseh Jul 31, 2026
05c2a8d
fix(ci): disable checkout credential persistence
sepaseh Jul 31, 2026
fcfe9d3
fix: resolve SonarCloud maintainability issues
sepaseh Jul 31, 2026
92c59b2
refactor source modules into colocated directories
sepaseh Jul 31, 2026
0d8599b
fix: satisfy Sonar quality gate
sepaseh Jul 31, 2026
65a02b1
test: stabilize account flow coverage
sepaseh Jul 31, 2026
57efb16
test: cover management actions
sepaseh Jul 31, 2026
0545854
test: allow CI time for user management flow
sepaseh Jul 31, 2026
07331aa
test: align management fixtures with API types
sepaseh Jul 31, 2026
42cb9bd
fix: remove invalid OTP aria attribute
sepaseh Jul 31, 2026
6fd17ab
fix: remove redundant password fields fragment
sepaseh Jul 31, 2026
b2255f7
fix remaining coderabbit findings
sepaseh Jul 31, 2026
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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
VITE_API_BASE_URL=https://localhost:8080
VITE_APP_BASE_URL=/
VITE_OBSERVABILITY_URL=https://observability.example.com/events
VITE_RELEASE_ID=local
2 changes: 2 additions & 0 deletions .env.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
VITE_API_BASE_URL=http://localhost
VITE_APP_BASE_URL=/
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* text=auto

*.css text eol=lf
*.html text eol=lf
*.js text eol=lf
*.json text eol=lf
*.md text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
32 changes: 32 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Default reviewer for all repository changes.
* @sepaseh

# Require explicit review for automation and dependency policy changes.
/.github/ @sepaseh
/package.json @sepaseh
/package-lock.json @sepaseh

# Release policy, version state, and generated release notes.
/.release-please-manifest.json @sepaseh
/CHANGELOG.md @sepaseh
/docs/release-operations.md @sepaseh
/docs/releasing.md @sepaseh
/release-please-config.json @sepaseh

# Production-like staging gate.
/.github/workflows/staging.yml @sepaseh
/docs/staging.md @sepaseh
/staging.config.ts @sepaseh
/staging/ @sepaseh

# Security model, review evidence, and active scanning.
/.github/workflows/dast.yml @sepaseh
/docs/security/ @sepaseh
/SECURITY.md @sepaseh

# Test-effectiveness and frontend/backend compatibility gates.
/.github/workflows/mutation.yml @sepaseh
/contract/ @sepaseh
/docs/contract-testing.md @sepaseh
/stryker.config.json @sepaseh
/vitest.contract.config.ts @sepaseh
48 changes: 48 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
version: 2

updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
time: "09:00"
timezone: Asia/Tehran
open-pull-requests-limit: 10
reviewers:
- sepaseh
commit-message:
prefix: chore
include: scope
groups:
production-minor-and-patch:
dependency-type: production
update-types:
- minor
- patch
development-minor-and-patch:
dependency-type: development
update-types:
- minor
- patch

- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
time: "09:00"
timezone: Asia/Tehran
open-pull-requests-limit: 5
reviewers:
- sepaseh
commit-message:
prefix: chore
include: scope
groups:
actions-minor-and-patch:
patterns:
- "*"
update-types:
- minor
- patch
90 changes: 79 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,71 +17,139 @@ permissions:
contents: read

env:
NODE_VERSION: 22
NODE_VERSION: 24.18.0

jobs:
check:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
fetch-depth: 0
Comment thread
coderabbitai[bot] marked this conversation as resolved.
persist-credentials: false

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- run: npm ci

- run: npm run audit

- run: npm run typecheck

- run: npm run lint

- run: npm run format:check

- run: npm run test:coverage

- run: npm run test:contract

- if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: consumer-contracts-${{ github.sha }}
path: pacts
retention-days: 14

- name: Validate SonarQube configuration
if: >-
github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository
env:
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
missing=()

[[ -z "$SONAR_HOST_URL" ]] && missing+=("SONAR_HOST_URL")
[[ -z "$SONAR_PROJECT_KEY" ]] && missing+=("SONAR_PROJECT_KEY")
[[ -z "$SONAR_TOKEN" ]] && missing+=("SONAR_TOKEN")

if (( ${#missing[@]} > 0 )); then
missing_list=$(IFS=,; echo "${missing[*]}")
echo "::error title=Missing SonarQube configuration::Configure these repository settings: $missing_list"
exit 1
fi

- name: SonarQube scan
if: >-
github.event_name != 'pull_request' ||
github.event.pull_request.head.repo.full_name == github.repository
uses: SonarSource/sonarqube-scan-action@7006c4492b2e0ee0f816d36501671557c97f5995 # v8.1.0
env:
SONAR_HOST_URL: ${{ vars.SONAR_HOST_URL }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY }}
with:
args: -Dsonar.projectKey=${{ env.SONAR_PROJECT_KEY }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

- run: npm run knip

build:
needs: check
runs-on: ubuntu-latest
env:
VITE_API_BASE_URL: https://api.example.com
VITE_APP_BASE_URL: /
VITE_RELEASE_ID: ${{ github.sha }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- run: npm ci

- run: npm run build
- run: npm run performance

- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: dist-${{ github.sha }}
path: dist

- if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: lighthouse-${{ github.sha }}
path: .lighthouseci
retention-days: 14

e2e:
needs: check
runs-on: ubuntu-latest
env:
VITE_API_BASE_URL: https://api.example.com
VITE_APP_BASE_URL: /
VITE_RELEASE_ID: ${{ github.sha }}

steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false

- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm

- run: npm ci

- run: npx playwright install --with-deps chromium
- run: npx playwright install --with-deps chromium firefox webkit

- run: npm run test:e2e

- if: ${{ !cancelled() }}
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: playwright-report-${{ github.sha }}
path: |
Expand Down
37 changes: 37 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: CodeQL

on:
push:
branches:
- main
pull_request:
branches:
- main
schedule:
- cron: "30 3 * * 1"
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

permissions:
contents: read

jobs:
analyze:
name: Analyze JavaScript and TypeScript
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write

steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- uses: github/codeql-action/init@f52b05f4acaaa234e44466e66d29050e135ea9ef # v4.36.0
with:
languages: javascript-typescript
queries: security-extended

- uses: github/codeql-action/analyze@f52b05f4acaaa234e44466e66d29050e135ea9ef # v4.36.0
63 changes: 63 additions & 0 deletions .github/workflows/dast.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: DAST

on:
schedule:
- cron: "0 3 1 * *"
workflow_dispatch:

concurrency:
cancel-in-progress: false
group: staging-dast

permissions:
contents: read

jobs:
zap:
environment: staging
runs-on: ubuntu-latest
timeout-minutes: 60
env:
DAST_ALLOWED_HOST: ${{ vars.STAGING_ALLOWED_HOST }}
DAST_PRODUCTION_HOST: ${{ vars.PRODUCTION_HOST }}
DAST_TARGET: ${{ vars.STAGING_BASE_URL }}

steps:
- name: Validate authorized target
shell: bash
run: |
node -e '
const value = process.env.DAST_TARGET;
const allowedHost = process.env.DAST_ALLOWED_HOST?.toLowerCase();
const productionHost = process.env.DAST_PRODUCTION_HOST?.toLowerCase();
if (!value) throw new Error("STAGING_BASE_URL is required");
if (!allowedHost) throw new Error("STAGING_ALLOWED_HOST is required");
if (!productionHost) throw new Error("PRODUCTION_HOST is required");
const url = new URL(value);
if (url.protocol !== "https:") {
throw new Error("STAGING_BASE_URL must use HTTPS");
}
if (url.username || url.password) {
throw new Error("STAGING_BASE_URL must not contain credentials");
}
if (url.hostname.toLowerCase() !== allowedHost) {
throw new Error("STAGING_BASE_URL host is not explicitly allowed");
}
if (url.hostname.toLowerCase() === productionHost) {
throw new Error("DAST must not target the production host");
}
'

- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
with:
persist-credentials: false

- name: Run authorized ZAP full scan
uses: zaproxy/action-full-scan@bee4e5be916301228d2b1b2dcad73aab6fa6a25b # v0.13.0
with:
allow_issue_writing: false
artifact_name: staging-dast-${{ github.run_id }}
cmd_options: "-a"
docker_name: ghcr.io/zaproxy/zaproxy:2.17.0@sha256:c558ee87358911ab17278c70991e856f57793e115d9cd0f88ca475cf82907a1a
fail_action: true
target: ${{ env.DAST_TARGET }}
Loading
Loading