# Security Checks

> Checklist sécurité et RGPD avant tout commit ou ajout de feature dans Fidely. Déclencher quand : commit, ajout formulaire, manipulation IndexedDB/localStorage, scan QR, ajout dépendance, accès caméra, affichage HTML dynamique. Mots-clés : secret, API key, UUID, RGPD, données personnelles, localStorage, IndexedDB, v-html, html5-qrcode, validation input, .env, gitignore, pnpm audit.

- Skill: `chawkitariq/security-checks` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add chawkitariq/security-checks`
- Raw SKILL.md: https://api.skillmd.com/api/skills/chawkitariq/security-checks/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: chawkitariq (https://skillmd.com/u/chawkitariq)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/chawkitariq/security-checks

---


# Skill : Vérifications sécurité

Avant tout commit ou ajout de fonctionnalité, passer en revue ces points.

## Ressources disponibles

- [`reference/storage-map.md`](reference/storage-map.md) — carte complète du stockage Fidely (IndexedDB + localStorage)

## Checklist rapide

```
[ ] Aucune clé API, token ou secret dans le code
[ ] Aucune donnée personnelle collectée (UUID anonymes uniquement)
[ ] localStorage/IndexedDB : utilisés correctement (client-only)
[ ] Inputs utilisateur : validés avant traitement
[ ] Dépendances tierces : versions non deprecated/vulnérables
[ ] Fichiers sensibles exclus du commit (.env, clés, etc.)
```

## 1. Données sensibles dans le code

**Interdit dans tout fichier commité :**
```
❌ const API_KEY = 'sk-abc123...'
❌ const WEBHOOK_SECRET = 'whsec_...'
❌ password: 'monmotdepasse'
❌ Authorization: 'Bearer hardcoded-token'
```

**Ce projet n'a pas de backend actuellement** — aucune clé API n'est nécessaire pour le MVP.

Si une API externe est ajoutée, les variables d'env doivent passer par `nuxt.config.ts` :
```ts
runtimeConfig: {
  // Côté serveur uniquement (jamais exposé au client)
  apiSecret: process.env.API_SECRET,
  public: {
    // Côté client (visible dans le bundle !)
    apiBase: process.env.NUXT_PUBLIC_API_BASE
  }
}
```

Et utilisées via :
```ts
const config = useRuntimeConfig()
// Serveur : config.apiSecret
// Client : config.public.apiBase
```

## 2. Données personnelles (RGPD)

Ce projet est conçu RGPD by design :
- Les clients sont identifiés par des **UUID anonymes** générés localement
- **Aucun email, nom, téléphone** n'est collecté ou stocké
- Toutes les données restent dans le navigateur de l'utilisateur

**Ne pas introduire** de champs de formulaire demandant des données personnelles sans discussion.

Si une fonctionnalité de compte utilisateur est ajoutée, prévoir une politique de confidentialité.

## 3. Sécurité du stockage local

**localStorage** — uniquement `fidely_mode` (préférence UI) :
- Donnée non sensible — préférence d'affichage uniquement
- Ne jamais y stocker de tokens, secrets, ou données métier
- ⚠️ Ne **pas** y stocker `client_id` — c'est dans IndexedDB

**IndexedDB** (`fidely` DB, version 2) :
| Object store | Contenu | Clé |
|---|---|---|
| `cards` | Cartes de fidélité (title, reward_points, reward) | `id` (UUID v7) |
| `clients` | Points par client (`{ cards: Record<string, number> }`) | UUID client |
| `config` | Paramètres app dont `client_id` | string key |

- `client_id` est dans IndexedDB `config` store — stable et immuable
- Ne jamais y stocker de données de paiement, mots de passe, ni PII

## 4. Validation des inputs utilisateur

Dans `merchant.vue`, les inputs du formulaire de carte sont validés côté client :

```ts
function validate(s: typeof state) {
  const errors = []
  if (!s.title.trim()) errors.push({ name: 'title', message: 'Champ requis' })
  if (!s.reward_points || s.reward_points < 1) errors.push({ name: 'reward_points', message: 'Minimum 1' })
  if (!s.reward.trim()) errors.push({ name: 'reward', message: 'Champ requis' })
  return errors
}
```

**Règles à toujours respecter pour les nouveaux inputs :**
- `.trim()` sur tous les champs texte avant de les stocker
- Valider les bornes numériques (min/max)
- Sanitiser avant d'afficher dans le DOM (Vue le fait automatiquement via `{{ }}`)
- **Ne jamais utiliser** `v-html` avec du contenu utilisateur

**Validation du QR code scanné :**
```ts
// Valider le format UUID avant de naviguer
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(text)) {
  navigateTo(`/client/${text}`)
}
```

## 5. Dépendances tierces

Vérifier avant d'ajouter une dépendance :
```bash
pnpm audit                    # Vulnérabilités connues
pnpm outdated                 # Versions obsolètes
```

Dépendances à surveiller (accès caméra / DOM bas niveau) :
- `html5-qrcode` 2.3.8 — accès caméra, maintenir à jour
- `qrcode` 1.5.4 — génération côté client, faible risque

## 6. Sécurité spécifique au scan QR

Le scanner `html5-qrcode` lit tout QR code — un QR code malveillant pourrait contenir :
- Une URL (ne pas ouvrir automatiquement)
- Du texte arbitraire (ne pas l'afficher dans `v-html`)
- Une fausse UUID (valider le format avant navigateTo)

**Protection en place dans `scan.vue` :**
```ts
// Seul un UUID valide déclenche la navigation
if (/^[0-9a-f]{8}-...-[0-9a-f]{12}$/i.test(trimmed)) {
  scanned.value = true // empêche le double-scan
  await navigateTo(`/client/${trimmed}`)
}
```

## 7. Fichiers à ne jamais commiter

```bash
# Déjà exclus dans .gitignore
.env
.env.*
!.env.example
.nuxt/
node_modules/
dist/
```

Vérifier avant `git add` :
```bash
git status        # Aucun .env dans les fichiers modifiés
git diff --cached # Revue du diff avant commit
```

## 8. Si des API routes SSR sont ajoutées (futur)

Si le projet évolue vers un backend Nuxt (`server/api/`), ajouter systématiquement :

```ts
// server/api/endpoint.post.ts
export default defineEventHandler(async (event) => {
  // 1. Lire et valider le body
  const body = await readBody(event)

  // 2. Valider avec zod ou manuellement
  if (!body.clientId || typeof body.clientId !== 'string') {
    throw createError({ statusCode: 400, message: 'clientId requis' })
  }

  // 3. Ne jamais retourner d'erreurs système raw
  try {
    // logique
  }
  catch {
    throw createError({ statusCode: 500, message: 'Erreur interne' })
  }
})
```

---

## Confirmation en fin de vérification

> ✅ **security-checks passé**
> - Secrets : [aucun secret en dur]
> - RGPD : [aucune PII collectée / stockée]
> - Stockage : [client_id dans IndexedDB, mode dans localStorage uniquement]
> - Inputs : [validation Zod + trim en place]
> - QR scan : [UUID validé par regex avant navigateTo]
> - Dépendances : [pnpm audit → 0 vulnérabilité critique]

