| .forgejo/workflows | ||
| css | ||
| js | ||
| public/keys | ||
| test | ||
| .gitignore | ||
| apple-touch-icon.png | ||
| BUILD.md | ||
| build.sh | ||
| favicon-96x96.png | ||
| favicon.ico | ||
| favicon.svg | ||
| index.html | ||
| LICENSE | ||
| package.json | ||
| README.md | ||
| robots.txt | ||
| site.webmanifest | ||
| web-app-manifest-192x192.png | ||
| web-app-manifest-512x512.png | ||
| 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 outils de minification globalement (une seule fois)
npm install -g html-minifier-terser terser clean-css-cli
# Lancer le build
chmod +x build.sh
./build.sh
# 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'; 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)
header /css/* /js/* /favicon* /apple-touch-icon* /site.webmanifest Cache-Control "public, max-age=31536000, immutable"
# Pas de cache pour les fichiers dynamiques ou susceptibles de changer
@nocache {
path /*.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). - 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 branchedist. 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
# 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/style inline
- ✅ 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');
escapeHtml('<script>alert("xss")</script>');
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 & Validations};
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+
Statut : ✅ Production-ready, sécurisé, accessible