npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mostajs/garde-http

v0.1.0

Published

Cette requête est-elle légitime, et que dit-on au navigateur ? Contrôle CSRF par métadonnées de requête (Sec-Fetch-Site, Origin) et en-têtes de sécurité (CSP à nonce, frame-ancestors, nosniff, HSTS). Zéro dépendance, sans état.

Readme

@mostajs/garde-http

Auteur : Dr Hamid MADANI [email protected] · Licence : AGPL-3.0-or-later · Niveau N0, zéro dépendance, Node ≥ 20

Deux décisions qu'une application node:http doit prendre à chaque requête, et qu'elle finit sinon par réécrire à sa façon — ou par oublier :

  1. Une requête qui modifie vient-elle bien de mon site ? — requeteLegitime(req, { origine })
  2. Quels en-têtes protègent la page que je rends ? — enTetesSecurite({ https })

Le module décide ; il n'écrit rien dans la réponse. Pas d'état, pas de configuration globale, pas de dépendance.

D'où il vient

De l'audit de sécurité de QATrax du 30/09/2026. Deux constats :

  • V-08 — depuis clienta.sandbox.qatrax.dev, une page pouvait régénérer le jeton d'intégration de clientb.sandbox.qatrax.dev. Le cookie SameSite=Lax part vers un « même site », et deux sous-domaines d'un même domaine sont le même site pour le navigateur — mais deux clients différents pour nous.
  • V-09 — aucune page ne portait d'en-tête de sécurité : la page de connexion pouvait être encadrée dans un site tiers.

mdseal avait le même manque. Trois applications allaient donc écrire la même chose : c'est un module.

1. Le contrôle d'origine

import { requeteLegitime } from '@mostajs/garde-http';

const v = requeteLegitime(req, { origine: 'https://qatrax.dev' });
if (!v.ok) { res.writeHead(403); return res.end(v.motif); }   // 'site-tiers' | 'origine-etrangere'

Il juge sur les en-têtes que le navigateur pose lui-même et qu'aucune page ne peut falsifier :

| la requête | verdict | |---|---| | GET, HEAD, OPTIONS | admise — une méthode sûre ne modifie rien | | Sec-Fetch-Site: same-origin (ou none) | admise | | Sec-Fetch-Site: same-site — un sous-domaine voisin | refusée | | Sec-Fetch-Site: cross-site | refusée | | pas de Sec-Fetch-Site, Origin admis | admise | | pas de Sec-Fetch-Site, Origin étranger ou null | refusée | | ni l'un ni l'autre — curl, intégration continue, serveur à serveur | admise |

Le dernier cas n'est pas une faille : le CSRF est une attaque par le navigateur de la victime, qui porte son cookie. Un client machine n'a pas de cookie volé à rejouer ; il porte ses propres secrets. C'est ce qui permet de protéger les pages sans casser l'automatisation.

Pourquoi pas un jeton caché dans chaque formulaire ? Il faut un état côté serveur, modifier chaque formulaire, et il ne protège pas les fetch. Les métadonnées de requête couvrent tout, sans état.

2. Les en-têtes

import { enTetesSecurite } from '@mostajs/garde-http';

const ENTETES = enTetesSecurite({ https: true, csp: { 'style-src': "'self' 'unsafe-inline'" } });
for (const [k, v] of Object.entries(ENTETES)) res.setHeader(k, v);    // avant writeHead

| en-tête | effet | |---|---| | Content-Security-Policy | aucun script inline ni externe non déclaré, aucun encadrement (frame-ancestors 'none'), aucun formulaire vers ailleurs, aucun <object> | | X-Frame-Options: DENY | pour les navigateurs qui ignorent frame-ancestors | | X-Content-Type-Options: nosniff | un fichier déposé ne s'exécute pas comme un script | | Referrer-Policy: same-origin | un lien public qui porte un jeton ne le fuit pas vers un site tiers | | Cross-Origin-Opener-Policy: same-origin | une fenêtre ouverte par un tiers ne garde pas la main | | Permissions-Policy | caméra, micro, géolocalisation, paiement : refusés | | Strict-Transport-Security | seulement si https: true |

csp remplace une directive, null la retire. Un script inline indispensable se déclare par nonce, un par réponse : enTetesSecurite({ nonce: creerNonce() }).

Passer à une CSP sans script inline

Un onclick="return confirm('Supprimer ${nom} ?')" exécute ce qu'il contient : le navigateur décode &#39; avant d'exécuter, et un nom X');alert(1);// devient du code (X-02 de l'audit). La sortie : des attributs de données, lus par un fichier servi par l'application.

<button data-confirmer="Supprimer « Thermostat » ?">Supprimer</button>
<select name="id" data-soumettre>…</select>
<script src="/static/app.js" defer></script>
document.addEventListener('click', (e) => {
  const el = e.target.closest('[data-confirmer]');
  if (el && !confirm(el.getAttribute('data-confirmer'))) e.preventDefault();
}, true);

3. Le cookie de session

res.setHeader('set-cookie', cookieStrict('session', jeton, { maxAge: 7 * 86400 }));
// session=…; HttpOnly; Path=/; SameSite=Strict; Secure; Max-Age=604800

SameSite=Strict a un prix : un lien suivi depuis un courriel arrive sans cookie, et l'utilisateur se reconnecte. C'est ce qui protège entre sous-domaines ; Lax ne le fait pas.

Ce qu'il faut savoir

  • Une route GET qui modifie échappe au contrôle. La faute est à la route : on modifie par POST.
  • Derrière un proxy, passez l'origine publique (https://…). Sinon tout navigateur réel est refusé — le module échoue fermé, c'est voulu.
  • Une route publique qui ne modifie rien et que d'autres sites appellent (vérifier un document) se met hors contrôle explicitement, par l'hôte.
  • includeSubDomains engage tous les sous-domaines en https pendant un an : seulement s'ils le sont tous.

Ce qu'il ne fait pas

| besoin | module | |---|---| | limite de débit, essai en force | @mostajs/auth-lite | | plafonds de corps (413) | @mostajs/http-pont | | sessions révocables | @mostajs/auth-plug | | clés machine | @mostajs/api-keys |

Essais

npm test    # 14 essais mjs-unit — chacun nomme l'attaque qu'il empêche

Le plan Dev+Test (docs/DEVTEST-PLAN.garde-http.json) est suivi dans MostaQatraxFlow.