Compare commits

..

11 commits

Author SHA1 Message Date
9ccc9f4a9d Merge pull request 'fix(security): override 5 transitive dep vulnerabilities from npm audit' (#100) from issue-95-99-deps-overrides into master 2026-07-01 00:33:13 +00:00
le king fu
68abfb17f8 fix(security): override 5 transitive dep vulnerabilities from npm audit
Adds scoped overrides for shell-quote (GHSA-w7jw-789q-3m8p, critical),
undici (4 CVEs, high), ws (GHSA-96hv-2xvq-fx4p, high — distinct from the
CVE already fixed in #92/#94), js-yaml (GHSA-h67p-54hq-rp68, moderate,
two incompatible majors need separate scopes), and tar
(GHSA-vmf3-w455-68vh, moderate). All 5 are dev-tooling-only (Expo CLI,
Metro, react-devtools-core) with no runtime app code path.

Verified: npm audit clean on all 5 packages, smoke test passes, Metro
Bundler boots and serves /status normally after the ws bump (the only
scope touching Fast Refresh/dev-middleware runtime).

Fixes #95, Fixes #96, Fixes #97, Fixes #98, Fixes #99

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-06-30 20:20:46 -04:00
158204b938 Merge pull request 'fix(security): override @expo/cli ws to ^8.20.1 (GHSA-58qx-3vcg-4xpx) (#92)' (#94) from issue-92-ws-override into master 2026-05-31 19:21:22 +00:00
le king fu
483030081c state: sync after #90 + web deploy
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:46:07 -04:00
51429045e6 Merge pull request 'chore(web): document set-state-in-effect on ThemeToggle localStorage read (#90)' (#93) from issue-90-themetoggle-lint-disable into master 2026-05-30 19:25:20 +00:00
le king fu
9ee5372404 fix(security): override @expo/cli ws to ^8.20.1 (GHSA-58qx-3vcg-4xpx) (#92)
npm audit flagged ws 8.0.0-8.20.0 (moderate, uninitialized memory
disclosure) pulled in via expo > @expo/cli > ws@8.20.0. Scope the
override to @expo/cli so only the vulnerable 8.x instance is bumped
(resolved to 8.21.0); metro's ws@7.5.10 and react-native's ws@6.2.3
stay on their majors to avoid breaking the Metro dev server.

All ws here is dev/build tooling (Expo CLI, Metro, react-devtools),
absent from the shipped APK, so this was not exploitable in production
- but the override clears npm audit and stops the Defenseur re-flagging.

npm audit: 0 vulnerabilities. Root smoke green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 15:05:10 -04:00
le king fu
e9ddf27eab chore(web): document set-state-in-effect on ThemeToggle localStorage read (#90)
ThemeToggle's mount effect reads localStorage then setTheme, a pattern
react-hooks/set-state-in-effect flags. It is safe here: localStorage is
SSR-unavailable so the read must happen post-mount, and ThemeScript
already applies the dark class before hydration (no page FOUC). Add a
targeted eslint-disable-next-line with justification.

web/ lint is now green (0 errors).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 14:57:44 -04:00
le king fu
7750214c64 state: sync after #70
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 14:53:57 -04:00
97cf9ce0f7 Merge pull request 'chore(web): fix prefer-const and remove unused import' (#91) from chore/web-lint-cleanup into master 2026-05-30 18:52:34 +00:00
7343f993ee Merge pull request 'fix(web): resolve display name from userInfo, not just claims (#70)' (#89) from issue-70-harmonize-display-name into master 2026-05-30 18:52:22 +00:00
le king fu
3637879f90 fix(web): resolve display name from userInfo, not just claims (#70)
getAuthenticatedUser only read ID token claims, where `name` is often
absent, so the web app showed the user's email instead of their name
after SSO. Fetch the userInfo endpoint and resolve the display name with
the same fallback order as the vitrine (la-compagnie-maximus#80):
userInfo.name -> userInfo.username -> claims.name -> claims.username,
with the email fallback applied in the layout. Email now also prefers
userInfo over claims.

Single source of truth in auth.ts (only producer of `name`, consumed
solely by layout.tsx -> Header). layout.tsx and Header.tsx verified,
left unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 14:10:14 -04:00
5 changed files with 169 additions and 74 deletions

View file

@ -1,16 +1,23 @@
# STATE — simpl-liste
> Derniere MAJ : 2026-05-10 (par fix-issue #87)
> Derniere MAJ : 2026-05-30 (deploy web liste.lacompagnie + #90)
## Position actuelle
Version 1.6.1 (versionCode 13). Remediation vulnerabilites du defenseur en cours
Version 1.6.4 (versionCode 16). Remediation vulnerabilites du defenseur en cours
via overrides `@xmldom/xmldom@^0.8.13` et `uuid@^11.0.0` dans `package.json`
(overnight 2026-04-24). 4 CVE HIGH xmldom nettoyees ; 1 advisory uuid residuelle
non-exploitable en pratique (details dans `SECURITY.md`).
Companion web `liste.lacompagniemaximus.com` : deploye **manuellement** (docker-compose
standalone `/data/simpl-liste-web` sur le VPS, build depuis `source/` rsync — PAS une app
Coolify malgre la spec). Procedure en memoire `simpl-liste-web-deploy`.
## Decisions recentes
- 2026-05-30 : #90 merged (PR #93, ThemeToggle eslint-disable) + premier deploy web depuis 7 sem (rsync source/ + docker compose --build sur VPS) — #70 et #90 live sur liste.lacompagniemaximus.com.
- 2026-05-30 : PR #89 merged — display name web resolu via userInfo (ordre fallback de la vitrine), fix #70.
- 2026-05-30 : nettoyage lint web/ (PR #91, 2 prefer-const + import inutilise) ; #90 analyse (ThemeToggle set-state-in-effect, decision : eslint-disable documente).
- 2026-05-10 : Aligne 6 patches Expo SDK 54 via expo install --fix — expo-doctor 17/17 (ref #87)
- 2026-05-08 : Defenseur rerun confirme 0 findings — overrides existants suffisent, pas de PR necessaire (ref #81)
- 2026-04-24 : overrides xmldom + uuid (spec `spec-decisions-vuln-simpl-liste.md`) — PRs #77, #78, #79 (pending-human).

183
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "simpl-liste",
"version": "1.6.3",
"version": "1.6.4",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "simpl-liste",
"version": "1.6.3",
"version": "1.6.4",
"dependencies": {
"@expo-google-fonts/inter": "^0.4.2",
"@expo/ngrok": "^4.1.3",
@ -2684,9 +2684,19 @@
"license": "Python-2.0"
},
"node_modules/@expo/xcpretty/node_modules/js-yaml": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
"integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
@ -2747,6 +2757,19 @@
"node": ">=6"
}
},
"node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz",
"integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==",
"license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/@istanbuljs/schema": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
@ -3427,12 +3450,24 @@
}
},
"node_modules/@react-native/dev-middleware/node_modules/ws": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
"integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
"integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==",
"license": "MIT",
"dependencies": {
"async-limiter": "~1.0.0"
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/@react-native/gradle-plugin": {
@ -3990,12 +4025,6 @@
"util": "^0.12.5"
}
},
"node_modules/async-limiter": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
"license": "MIT"
},
"node_modules/available-typed-arrays": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
@ -6464,9 +6493,9 @@
}
},
"node_modules/expo/node_modules/ws": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
"integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
"version": "8.21.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz",
"integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==",
"license": "MIT",
"engines": {
"node": ">=10.0.0"
@ -7675,19 +7704,6 @@
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"license": "MIT",
"dependencies": {
"argparse": "^1.0.7",
"esprima": "^4.0.0"
},
"bin": {
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsc-safe-url": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz",
@ -8574,6 +8590,27 @@
"hermes-estree": "0.32.0"
}
},
"node_modules/metro/node_modules/ws": {
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
"integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==",
"license": "MIT",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@ -9680,6 +9717,27 @@
"ws": "^7"
}
},
"node_modules/react-devtools-core/node_modules/ws": {
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
"integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==",
"license": "MIT",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/react-dom": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
@ -10376,12 +10434,24 @@
}
},
"node_modules/react-native/node_modules/ws": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.2.3.tgz",
"integrity": "sha512-jmTjYU0j60B+vHey6TfR3Z7RD61z/hmxBS3VMSGIrroOWXQEneK1zNuotOUrGyBHQj0yrpsLHPWtigEFd13ndA==",
"version": "7.5.11",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz",
"integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==",
"license": "MIT",
"dependencies": {
"async-limiter": "~1.0.0"
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/react-refresh": {
@ -11026,9 +11096,9 @@
}
},
"node_modules/shell-quote": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz",
"integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz",
"integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
@ -11358,9 +11428,9 @@
}
},
"node_modules/tar": {
"version": "7.5.15",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.15.tgz",
"integrity": "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ==",
"version": "7.5.19",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.19.tgz",
"integrity": "sha512-4LeEWl96twnS2Q7Bz4MGqgazLqO+hJN63GZxXoIqh1T3VweYD997gbU1ItNsQafqqXTXd5WFyFdReLtwvRBNiw==",
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/fs-minipass": "^4.0.0",
@ -11664,9 +11734,9 @@
}
},
"node_modules/undici": {
"version": "6.25.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.25.0.tgz",
"integrity": "sha512-ZgpWDC5gmNiuY9CnLVXEH8rl50xhRCuLNA97fAUnKi8RRuV4E6KG31pDTsLVUKnohJE0I3XDrTeEydAXRw47xg==",
"version": "6.27.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz",
"integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==",
"license": "MIT",
"engines": {
"node": ">=18.17"
@ -12241,27 +12311,6 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
"node_modules/ws": {
"version": "7.5.10",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
"integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
"license": "MIT",
"engines": {
"node": ">=8.3.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": "^5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/xcode": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz",

View file

@ -68,7 +68,31 @@
"esbuild": "^0.25.0",
"@xmldom/xmldom": "^0.8.13",
"uuid": "^11.1.1",
"postcss": "^8.5.10"
"postcss": "^8.5.10",
"@expo/cli": {
"ws": "^8.20.1",
"undici": "^6.27.0",
"tar": "^7.5.19"
},
"react-devtools-core": {
"shell-quote": "^1.8.4",
"ws": "^7.5.11"
},
"react-native": {
"ws": "^7.5.11"
},
"@react-native/dev-middleware": {
"ws": "^7.5.11"
},
"metro": {
"ws": "^7.5.11"
},
"@expo/xcpretty": {
"js-yaml": "^4.2.0"
},
"@istanbuljs/load-nyc-config": {
"js-yaml": "^3.15.0"
}
},
"private": true
}

View file

@ -12,6 +12,11 @@ export function ThemeToggle() {
useEffect(() => {
const stored = localStorage.getItem("sl-theme") as Theme | null;
// localStorage is unavailable during SSR, so the stored theme can only be
// read post-mount. ThemeScript already applies the `dark` class before
// hydration (no page FOUC); only the toggle icon corrects on mount. The
// rule is a false positive for this hydration-from-localStorage read.
// eslint-disable-next-line react-hooks/set-state-in-effect
if (stored) setTheme(stored);
}, []);

View file

@ -4,16 +4,26 @@ import { logtoConfig } from './logto';
export const getAuthenticatedUser = cache(async () => {
try {
const context = await getLogtoContext(logtoConfig);
const context = await getLogtoContext(logtoConfig, { fetchUserInfo: true });
if (!context.isAuthenticated || !context.claims?.sub) {
return null;
}
const { claims, userInfo } = context;
// Mirror the vitrine's display-name resolution (la-compagnie-maximus#80):
// prefer the userInfo endpoint over ID token claims, falling back to email
// in the layout. The `name` claim is often absent from the ID token while
// present in userInfo, which is why the user saw their email instead of "Max".
return {
userId: context.claims.sub,
email: context.claims.email,
name: context.claims.name,
userId: claims.sub,
email: userInfo?.email || claims.email,
name:
userInfo?.name ||
userInfo?.username ||
claims.name ||
claims.username,
};
} catch (error) {
console.error('[auth] getLogtoContext error:', error);