|
|
||
|---|---|---|
| .forgejo/workflows | ||
| conductor | ||
| css | ||
| js | ||
| public | ||
| test | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| BUILD.md | ||
| build.sh | ||
| deploy.sh | ||
| eslint.config.js | ||
| GEMINI.md | ||
| index.html | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| vitest.config.js | ||
| WORKFLOW.md | ||
Visionneuse de clé PGP publique
Une application web moderne, sécurisée et accessible pour afficher et partager votre clé PGP publique.
Demo : https://pgp.manu.quebec
✨ Fonctionnalités
- 📋 Affichage élégant de la clé PGP au format ASCII
- 📊 Extraction automatique des métadonnées (ID, empreinte, algorithme, dates, UIDs)
- 📋 Copie rapide vers le presse-papiers (un clic)
- 💾 Téléchargement de la clé en fichier .asc
- 📱 Design responsive optimisé mobile/tablette/desktop
- 🌓 Mode sombre automatique selon les préférences système
- ♿ Accessible (WCAG 2.1 niveau AA) - lecteurs d'écran, navigation clavier
- 🔒 Sécurisé - CSP strict, protection XSS, validation des entrées
- ⚡ Performant - Zéro dépendance, JavaScript vanilla, fichiers minifiés
- ✅ Tests automatisés - CI/CD avec un job de validation complet
- 🚀 Déploiement continu - Mise à jour automatique via webhook
📁 Structure du projet
pgp-key-viewer/
├── index.html # Page principale avec ARIA
├── README.md # Documentation
├── LICENSE # MIT License
├── .gitignore
├── robots.txt # SEO
│
├── .forgejo/
│ └── workflows/
│ └── tests.yml # CI/CD (tests + build)
│
├── css/
│ └── styles.css # Design moderne responsive
│
├── js/
│ ├── main.js # Orchestration et gestion d'état
│ ├── pgp-parser.js # Parser OpenPGP (SHA-1, packets)
│ └── utils.js # Utilitaires (copie, notifications, etc.)
│
└── public/
└── keys/
└── public.asc # Votre clé PGP publique
Branches:
├── main # Code source (développement)
└── dist # Build de production (minifié)
🚀 Installation rapide
1. Cloner le dépôt
git clone https://code.manu.quebec/manu/pgp-key-viewer.git
cd pgp-key-viewer
2. Ajouter votre clé PGP
# Exporter votre clé (remplacer YOUR_KEY_ID)
gpg --export --armor YOUR_KEY_ID > public/keys/public.asc
# Vérifier le format
head -1 public/keys/public.asc
# Doit afficher: -----BEGIN PGP PUBLIC KEY BLOCK-----
3. Tester localement
# Option 1: Python 3
python3 -m http.server 8000
# Option 2: Node.js (si installé)
npx http-server -p 8000
# Puis ouvrir http://localhost:8000
📦 Déploiement en production
Option 1 : Déploiement automatique (recommandé)
Cette méthode utilise la branche dist (qui contient les fichiers de production) et un webhook pour le déploiement continu.
-
Clonage initial sur votre serveur :
# Cloner uniquement la branche 'dist' dans le répertoire de votre site git clone -b dist https://code.manu.quebec/manu/pgp-key-viewer.git /srv/pgp-key-viewer -
Mise à jour automatique :
Après la configuration d'un webhook (voir ci-dessous), chaque mise à jour de la branche
distsur Forgejo déclenchera automatiquement ungit pullsur votre serveur, assurant un déploiement instantané.
Option 2 : Build local
Si vous ne souhaitez pas utiliser le build automatique, vous pouvez construire les fichiers de production localement.
# Installer les dépendances localement
npm install
# Lancer le build
npm run build
# Les fichiers optimisés sont dans le dossier dist/
# Vous pouvez ensuite les téléverser sur votre serveur.
cd dist && python3 -m http.server 8000
Configuration Caddy (Exemple)
Voici un exemple de configuration pour Caddy qui inclut les en-têtes de sécurité, le cache, et la gestion du webhook.
pgp.manu.quebec {
# Chemin vers les fichiers du site
root * /srv/pgp-key-viewer
# Gérer le webhook de déploiement
handle /hooks/* {
reverse_proxy webhook:9000
}
# Servir les fichiers statiques
file_server
# Gérer les erreurs 404
handle_errors {
@404 expression {http.error.status_code} == 404
redir @404 / permanent
}
# Activer la compression
encode gzip
# En-têtes de sécurité
header {
Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; manifest-src 'self'; base-uri 'self'; form-action 'none'; frame-ancestors 'none'; upgrade-insecure-requests;"
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
X-Frame-Options "DENY"
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
Referrer-Policy "strict-origin-when-cross-origin"
Permissions-Policy "geolocation=(), microphone=(), camera=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()"
}
# Cache pour les ressources statiques (1 an)
# Le cache busting (?v=hash) assure le rafraîchissement lors des mises à jour
header /css/* /js/* /favicon* /apple-touch-icon* /site.webmanifest Cache-Control "public, max-age=31536000, immutable"
# Pas de cache pour les fichiers d'entrée pour garantir que le navigateur voit les nouveaux liens
@nocache {
path / /index.html /public/keys/*
}
header @nocache Cache-Control "no-cache, no-store, must-revalidate"
}
🔧 Configuration
Changer le chemin de la clé
Dans js/main.js :
const CONFIG = {
KEY_PATH: './public/keys/public.asc', // Modifier ici
FILENAME: 'public.asc',
MAX_RETRIES: 3,
RETRY_DELAY: 1000
};
Personnaliser les couleurs
Dans css/styles.css :
:root {
--color-primary: #4a6fa5; /* Couleur principale */
--color-success: #10b981; /* Vert succès */
--color-error: #ef4444; /* Rouge erreur */
--color-background: #ffffff; /* Fond clair */
/* ... */
}
/* Mode sombre */
@media (prefers-color-scheme: dark) {
:root {
--color-background: #0d1117;
--color-text: #e6edf3;
/* ... */
}
}
🧪 Tests automatisés
Le workflow CI/CD s'exécute automatiquement à chaque push sur main.
Jobs du workflow
-
Validation et Tests (
all-tests) : Ce job exécute une série de vérifications en une seule étape :- Structure du projet et contenu des fichiers.
- Syntaxe JavaScript (
node -c). - Qualité du code (
npm run lintetnpm run format:check). - Format de la clé PGP et de la documentation.
- Présence de fonctions de sécurité et de balises d'accessibilité.
- Tests d'intégration avec
npm test.
-
Build de production (
build-production) : S'exécute uniquement si le joball-testsréussit. Il minifie les fichiers HTML, CSS, JS et publie le résultat sur la branchedistvia le scriptdeploy.sh. Le commit de build contient le hash du commit source de la branchemain.
Voir les résultats : Actions sur Forgejo
Tests locaux
# Syntaxe JavaScript
node -c js/main.js
node -c js/utils.js
node -c js/pgp-parser.js
# Vérifier la qualité du code (Lint & Format)
npm run lint
npm run format:check
# Lancer les tests d'intégration
npm test
# Lancer un build de production local
./build.sh
🔒 Sécurité
Protections implémentées
- ✅ Content Security Policy (CSP) strict - Aucun script inline (même pour le thème)
- ✅ Protection XSS -
escapeHtml(),textContentuniquement - ✅ Validation des entrées - Taille de fichiers (1MB max), format hexadécimal
- ✅ Protection contre les boucles infinies - Limite de 1000 paquets OpenPGP
- ✅ Timeout sur requêtes réseau - 10 secondes max avec retry automatique
- ✅ Pas de
innerHTML- Utilisation decreateElement()ettextContent - ✅ Headers de sécurité - HSTS, X-Frame-Options, X-Content-Type-Options
- ✅ Validation des UIDs - Filtrage des caractères de contrôle, limite de 50 UIDs
- ✅ Pas de localStorage - Aucune donnée persistante
Audit de sécurité
Scores attendus :
- Mozilla Observatory : A+
- SecurityHeaders.com : A+
- OWASP ZAP : Aucune vulnérabilité
♿ Accessibilité
Conformité WCAG 2.1 niveau AA
- ✅ Structure sémantique - HTML5,
<main>,<aside>,<time> - ✅ ARIA -
role,aria-label,aria-live,aria-hidden - ✅ Navigation clavier - Focus visible, ordre logique, pas de piège
- ✅ Lecteurs d'écran - Empreinte lue correctement, notifications annoncées
- ✅ Contraste - Ratio 4.5:1 minimum (mode clair et sombre)
- ✅ Responsive - Zoom 200%, touch targets 48x48px
- ✅ Animations - Respect de
prefers-reduced-motion
Tests recommandés
- NVDA (Windows), VoiceOver (macOS/iOS), JAWS
- Navigation clavier uniquement
- Zoom 200%
- axe DevTools, WAVE
📊 Performance
Optimisations
- Minification - HTML, CSS, JS réduits de ~40-50%
- Pas de dépendances - Aucune bibliothèque externe
- Cache agressif - 1 an pour CSS/JS/assets
- Compression serveur - gzip/zstd via Caddy
- Lazy loading - Chargement à la demande
Métriques attendues
- Lighthouse : 95-100/100 (Performance, Accessibility, Best Practices, SEO)
- First Contentful Paint : < 1s
- Time to Interactive : < 2s
- Taille totale : ~50-80 KB (minifié + gzipped)
🛠️ API JavaScript
Fonctions exposées dans la console :
// Configuration
console.log(pgpViewer.config);
// Récupérer la clé en cache
const key = pgpViewer.getCachedKey();
// Recharger la clé
await pgpViewer.reloadKey();
// Parser manuellement
const metadata = parseKeyMetadata(keyContent);
// Utilitaires
showNotification('Message', 'success', 2000);
await copyToClipboard('texte');
downloadFile('contenu', 'fichier.txt');
formatFingerprint('0123456789ABCDEF...');
🐛 Dépannage
Erreur : Clé non trouvée (404)
# Vérifier le fichier existe
ls -la public/keys/public.asc
# Vérifier les permissions
chmod 644 public/keys/public.asc
# Vérifier le chemin dans le code
grep "KEY_PATH" js/main.js
Erreur : Format de clé invalide
# Vérifier le format
head -1 public/keys/public.asc
tail -1 public/keys/public.asc
# Réexporter proprement
gpg --export --armor YOUR_KEY_ID > public/keys/public.asc
Bouton copier ne fonctionne pas
- Cause : API Clipboard nécessite HTTPS en production
- Solution : Utiliser HTTPS (Let's Encrypt avec Caddy)
- Vérifier la console (F12) pour erreurs
Erreur CSP : Style/Script bloqué
- Cause : Styles ou scripts inline dans le HTML
- Solution : Tous les styles doivent être dans
styles.css, pas dansstyle="..." - Vérifier qu'aucun
innerHTMLn'injecte du HTML non échappé
Build échoue : dossier dist/ dans la branche dist
- Cause : Problème de copie dans le workflow
- Solution : Déjà corrigé dans le workflow actuel
- Le dossier
dist/est automatiquement supprimé s'il apparaît
📚 Documentation technique
Architecture
- Pattern : MVC simplifié (Modèle-Vue-Contrôleur)
- Contrôleur (
main.js) : Orchestre l'application. Il initialise le chargement de la clé, gère les événements utilisateur (clics sur les boutons) et met à jour la vue avec les données du modèle. - Modèle (
pgp-parser.js) : Responsable de la logique métier. Il contient la logique complexe pour parser le bloc de clé PGP, extraire les métadonnées (ID, empreinte, etc.) et les valider. - Vue (
index.html+css/styles.css) : La structure sémantique (HTML) et la présentation visuelle (CSS) de l'application. Elle est mise à jour par le contrôleur. - Utilitaires (
utils.js) : Une bibliothèque de fonctions de support découplées de la logique principale. Elle gère des tâches transversales comme l'affichage des notifications, la copie vers le presse-papiers, le téléchargement de fichiers, et l'échappement de caractères HTML pour la sécurité (XSS).
- Contrôleur (
Format OpenPGP
Le parser implémente :
- Décodage base64 ASCII armored
- Parsing des paquets OpenPGP (RFC 4880)
- Extraction des métadonnées (fingerprint SHA-1, UIDs, dates)
- Validation du format hexadécimal
Workflow Git
graph TD
subgraph "CI/CD sur Forgejo"
A[Push sur main] -- Déclenche --> B{Workflow};
B -- Job: all-tests --> C{Tests, Lint & Format};
C -- ✅ Succès --> D[Job: build-production];
C -- ❌ Échec --> E[Notification d'erreur];
D -- Push force --> F[Branche 'dist' mise à jour];
end
subgraph "Serveur de Production"
F -- Déclenche --> G{Webhook};
G -- git pull --> H[Site web mis à jour];
end
🤝 Contribution
- Fork le projet sur Forgejo
- Créer une branche :
git checkout -b feature/amelioration - Commit :
git commit -am 'Ajouter amélioration' - Push :
git push origin feature/amelioration - Ouvrir une Pull Request
Guidelines
- Respecter la structure existante
- Ajouter des tests pour les nouvelles fonctionnalités
- Vérifier que tous les tests passent localement
- Pas de dépendances externes
- Maintenir la compatibilité accessibilité
📄 License
MIT License - Libre d'utilisation commerciale et personnelle.
Voir le fichier LICENSE pour plus de détails.
🔗 Ressources
- Dépôt : https://code.manu.quebec/manu/pgp-key-viewer
- Demo : https://pgp.manu.quebec
- Caddy : https://caddyserver.com
- OpenPGP : https://www.openpgp.org
- WCAG 2.1 : https://www.w3.org/WAI/WCAG21/quickref/
📈 Statistiques
- Lignes de code : ~1500 (HTML + CSS + JS)
- Taille source : ~100 KB
- Taille minifiée : ~60 KB
- Taille gzippée : ~20 KB
- Compatibilité : Chrome 90+, Firefox 88+, Safari 14+
Dernière revue complète de sécurité et mise à jour des dépendances : 7 janvier 2026.