Compare commits

...

22 Commits

Author SHA1 Message Date
cartsnitch-ceo[bot] 0d8ee5f386 feat(ci): add npm audit vulnerability check (#61)
feat(ci): add npm audit vulnerability check
2026-03-31 14:34:24 +00:00
cartsnitch-ceo[bot] 09864c1a96 Merge branch 'main' into feat/ci-npm-audit 2026-03-31 14:31:19 +00:00
cartsnitch-ceo[bot] 3621504c22 fix(ci): add Docker Hub login before build steps (#83)
fix(ci): add Docker Hub login before build steps
2026-03-31 14:30:42 +00:00
cartsnitch-ceo[bot] 24adc7e35b Merge branch 'main' into fix/dockerhub-login-cicd 2026-03-31 14:28:20 +00:00
cartsnitch-ci[bot] 99294ea46d fix(ci): add Docker Hub login before build steps in all 4 build jobs
- Adds docker/login-action@v3 step before each GHCR login in all 4
  build jobs (build-and-push, build-and-push-auth,
  build-and-push-receiptwitness, build-and-push-api)
- Uses DOCKERHUB_USERNAME and DOCKERHUB_TOKEN secrets
- Also fixes: removes duplicate API image tag from the receiptwitness
  kustomize update step (was causing the API image to be set twice)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-31 04:32:40 +00:00
cartsnitch-ci[bot] 6b32197ad2 chore: merge main into feat/ci-npm-audit to pick up CI updates
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-31 03:50:18 +00:00
cartsnitch-engineer[bot] 528887a4a2 fix(auth): add session table model mapping for plural table name
Better-Auth defaults to singular "session" table name, but our DB uses
the plural "sessions" table (created by migration 002). Add modelName and
snake_case field mappings to match the existing pattern for user,
account, and verification models.

Co-authored-by: Stockboy Steve <steve@cartsnitch.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: cartsnitch-ceo[bot] <269712056+cartsnitch-ceo[bot]@users.noreply.github.com>
2026-03-31 03:42:26 +00:00
cartsnitch-ci[bot] bca46bf68e chore(ci): merge main into fix/deploy-dev-resilient-v2, resolve ci.yml conflict
Resolved conflict in build-and-push-api and deploy-dev jobs:
- build-and-push-api: keep `if: push && main` guard to skip on PRs
- deploy-dev: keep `if: always() && !cancelled() && push && main` resilience guard

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-31 03:31:09 +00:00
cartsnitch-ci[bot] 5d3b8fc8c2 Merge stash - resolve conflict with v2 branch 2026-03-31 03:22:08 +00:00
cartsnitch-ceo[bot] 6e76222b81 Merge branch 'main' into feat/ci-npm-audit 2026-03-31 03:06:05 +00:00
cartsnitch-ceo[bot] 65e670a887 Merge pull request #80 from cartsnitch/fix/api-dockerfile-libpq
fix(api): add libpq5 to prod stage for psycopg2 runtime
2026-03-31 03:05:41 +00:00
cartsnitch-ci[bot] 63aae4f2eb fix(ci): make deploy-dev resilient to individual build failures 2026-03-31 02:55:28 +00:00
cartsnitch-engineer[bot] e9bc46121f fix(api): add libpq5 to prod stage for psycopg2 runtime
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-31 02:48:25 +00:00
cartsnitch-ceo[bot] 56d9d5ad2e feat(ci): add build-and-push-api job for ghcr.io/cartsnitch/api
Merges PR #75. QA-approved (Checkout Charlie) and CTO-approved (Savannah Savings). All CI checks pass. Resolves CAR-221, unblocks auth restoration (CAR-200).
2026-03-31 02:32:55 +00:00
cartsnitch-ci[bot] 69e1be1560 fix(deps): patch high-severity picomatch ReDoS vulnerability
Resolves GHSA-3v7f-55p6-f55p (picomatch ReDoS) and
GHSA-c2c7-rcm5-vvqj (picomatch method injection) flagged by the new
npm audit CI job. Also bump @vitejs/plugin-react to 4.7.0.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-31 01:32:02 +00:00
cartsnitch-ci[bot] 43673583c1 Merge main into feat/ci-npm-audit to resolve divergence 2026-03-31 01:23:53 +00:00
Flea Flicker aba26b9d2f Merge remote-tracking branch 'origin/main' into feat/ci-npm-audit
# Conflicts:
#	package-lock.json
2026-03-30 22:41:44 +00:00
Barcode Betty dfe7b42db3 fix: update stale package-lock.json to resolve npm ci failure
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-30 22:28:11 +00:00
Barcode Betty b6df3dc0cb fix(deps): patch 3 high-severity CVEs via overrides and vite-plugin-pwa downgrade 2026-03-30 22:28:11 +00:00
Barcode Betty 6c09db5478 fix(deps): force picomatch to 4.0.4 to patch high-severity ReDoS
Adds picomatch@^4.0.4 as a direct dependency to override the vulnerable
4.0.3 pinned in transitive deps (vitest). Resolves 2 high-severity CVEs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-30 22:28:11 +00:00
Barcode Betty 3f13cb1bf6 fix(deps): resolve 7 npm audit vulnerabilities
Ran npm audit fix to patch prototype pollution (flatted), glob matching
issues (picomatch), and RCE vectors (serialize-javascript).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-30 22:28:11 +00:00
Stockboy Steve d4f7194d3f feat(ci): add npm audit vulnerability check
Adds an audit job to the CI workflow that runs npm audit with
--audit-level=high, failing the job on critical or high severity
vulnerabilities. Runs in parallel with lint and test, and does
not gate the build-and-push jobs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-30 22:28:11 +00:00
5 changed files with 95 additions and 94 deletions
+62 -2
View File
@@ -48,6 +48,18 @@ jobs:
- name: Run tests
run: npx vitest run
audit:
runs-on: runners-cartsnitch
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: npm
- run: npm ci
- name: Check for vulnerabilities
run: npm audit --audit-level=high
e2e:
runs-on: runners-cartsnitch
steps:
@@ -62,6 +74,7 @@ jobs:
build-and-push:
runs-on: runners-cartsnitch
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [lint, test, e2e]
outputs:
calver_tag: ${{ steps.calver.outputs.version }}
@@ -87,6 +100,13 @@ jobs:
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "CalVer tag: $VERSION"
- name: Log in to Docker Hub
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
@@ -124,6 +144,7 @@ jobs:
build-and-push-auth:
runs-on: runners-cartsnitch
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [lint, test, e2e]
outputs:
calver_tag: ${{ steps.calver.outputs.version }}
@@ -148,6 +169,13 @@ jobs:
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Log in to Docker Hub
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
@@ -177,6 +205,7 @@ jobs:
build-and-push-receiptwitness:
runs-on: runners-cartsnitch
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [lint, test]
outputs:
calver_tag: ${{ steps.calver.outputs.version }}
@@ -196,6 +225,13 @@ jobs:
else BUILD_NUM=$(echo "$EXISTING" | sed "s/v${DATE_TAG}\.//"); VERSION="${DATE_TAG}.$((BUILD_NUM + 1))"; fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Log in to Docker Hub
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
@@ -225,6 +261,7 @@ jobs:
build-and-push-api:
runs-on: runners-cartsnitch
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [lint, test]
outputs:
calver_tag: ${{ steps.calver.outputs.version }}
@@ -244,6 +281,13 @@ jobs:
else BUILD_NUM=$(echo "$EXISTING" | sed "s/v${DATE_TAG}\.//"); VERSION="${DATE_TAG}.$((BUILD_NUM + 1))"; fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
- name: Log in to Docker Hub
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Log in to GHCR
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: docker/login-action@v3
@@ -274,7 +318,7 @@ jobs:
deploy-dev:
runs-on: runners-cartsnitch
needs: [build-and-push, build-and-push-auth, build-and-push-receiptwitness, build-and-push-api]
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
if: always() && !cancelled() && github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Generate GitHub App token
id: app-token
@@ -299,12 +343,28 @@ jobs:
- name: Install kustomize
uses: imranismail/setup-kustomize@v2
- name: Update dev overlay image tags
- name: Update frontend image tag
if: needs.build-and-push.result == 'success'
run: |
cd infra/apps/overlays/dev
kustomize edit set image ghcr.io/cartsnitch/cartsnitch:${{ needs.build-and-push.outputs.calver_tag }}
- name: Update auth image tag
if: needs.build-and-push-auth.result == 'success'
run: |
cd infra/apps/overlays/dev
kustomize edit set image ghcr.io/cartsnitch/auth:${{ needs.build-and-push-auth.outputs.calver_tag }}
- name: Update receiptwitness image tag
if: needs.build-and-push-receiptwitness.result == 'success'
run: |
cd infra/apps/overlays/dev
kustomize edit set image ghcr.io/cartsnitch/receiptwitness:${{ needs.build-and-push-receiptwitness.outputs.calver_tag }}
- name: Update api image tag
if: needs.build-and-push-api.result == 'success'
run: |
cd infra/apps/overlays/dev
kustomize edit set image ghcr.io/cartsnitch/api:${{ needs.build-and-push-api.outputs.calver_tag }}
- name: Commit and push to infra
+2
View File
@@ -15,6 +15,8 @@ RUN pip install --no-cache-dir --prefix=/install .
# Stage 2: Production image
FROM python:3.12-slim AS prod
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 && rm -rf /var/lib/apt/lists/*
WORKDIR /app
RUN adduser --system --group --uid 1000 app
COPY --from=build /install /usr/local
+9
View File
@@ -36,6 +36,15 @@ export const auth = betterAuth({
},
session: {
modelName: "sessions",
fields: {
userId: "user_id",
expiresAt: "expires_at",
ipAddress: "ip_address",
userAgent: "user_agent",
createdAt: "created_at",
updatedAt: "updated_at",
},
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // refresh after 1 day
cookieCache: {
+13 -89
View File
@@ -10,6 +10,7 @@
"dependencies": {
"@tanstack/react-query": "^5.0.0",
"better-auth": "^1.2.0",
"picomatch": "4.0.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.0.0",
@@ -25,7 +26,7 @@
"@types/node": "^24.12.0",
"@types/react": "^18.3.28",
"@types/react-dom": "^18.3.7",
"@vitejs/plugin-react": "^4.5.2",
"@vitejs/plugin-react": "^4.7.0",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
@@ -6052,9 +6053,9 @@
}
},
"node_modules/flatted": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz",
"integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
"dev": true,
"license": "ISC"
},
@@ -8177,10 +8178,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"devOptional": true,
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz",
"integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
"license": "MIT",
"engines": {
"node": ">=12"
@@ -8338,16 +8338,6 @@
"node": ">=6"
}
},
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
}
},
"node_modules/react": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
@@ -8754,27 +8744,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/feross"
},
{
"type": "patreon",
"url": "https://www.patreon.com/feross"
},
{
"type": "consulting",
"url": "https://feross.org/support"
}
],
"license": "MIT"
},
"node_modules/safe-push-apply": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
@@ -8850,13 +8819,13 @@
}
},
"node_modules/serialize-javascript": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
"integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"version": "7.0.5",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz",
"integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/set-cookie-parser": {
@@ -10446,31 +10415,6 @@
"rollup": "^1.20.0 || ^2.0.0"
}
},
"node_modules/workbox-build/node_modules/@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "0.0.39",
"estree-walker": "^1.0.1",
"picomatch": "^2.2.2"
},
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0"
}
},
"node_modules/workbox-build/node_modules/@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true,
"license": "MIT"
},
"node_modules/workbox-build/node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
@@ -10488,13 +10432,6 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/workbox-build/node_modules/estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
"dev": true,
"license": "MIT"
},
"node_modules/workbox-build/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
@@ -10512,19 +10449,6 @@
"sourcemap-codec": "^1.4.8"
}
},
"node_modules/workbox-build/node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/workbox-build/node_modules/pretty-bytes": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+9 -3
View File
@@ -15,6 +15,7 @@
"dependencies": {
"@tanstack/react-query": "^5.0.0",
"better-auth": "^1.2.0",
"picomatch": "4.0.4",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.0.0",
@@ -23,17 +24,17 @@
},
"devDependencies": {
"@eslint/js": "^9.39.4",
"@playwright/test": "^1.49.0",
"@tailwindcss/vite": "^4.0.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.3.2",
"@types/node": "^24.12.0",
"@types/react": "^18.3.28",
"@types/react-dom": "^18.3.7",
"@vitejs/plugin-react": "^4.5.2",
"@vitejs/plugin-react": "^4.7.0",
"eslint": "^9.39.4",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"@playwright/test": "^1.49.0",
"globals": "^17.4.0",
"jsdom": "^25.0.1",
"msw": "^2.12.14",
@@ -43,5 +44,10 @@
"vite": "^6.3.5",
"vite-plugin-pwa": "^0.21.2",
"vitest": "^3.2.4"
},
"overrides": {
"@rollup/pluginutils": "5.3.0",
"flatted": "^3.4.2",
"serialize-javascript": "7.0.5"
}
}
}