Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
Deploy site (production) / build-and-deploy (push) Successful in 20s
Remplace les 547 tirets cadratins (U+2014) et demi-cadratins (U+2013) des fichiers versionnés par la ponctuation contextuelle adaptée (point médian, deux-points, virgule, parenthèses ; tiret simple pour les plages), sur 122 fichiers (appli, vitrine, doc, tests, workflows, scripts). Ajoute le job CI « lint-dashes » (git grep -P) qui échoue si un tiret cadratin/demi-cadratin réapparaît, hors logo binaire et captures brutes du terminal (fidélité des fixtures de détection de dialogue).
34 lines
1.5 KiB
TypeScript
34 lines
1.5 KiB
TypeScript
// Création d'un nouveau projet : un dossier `<root>/<name>` créé sous une racine existante, dans
|
|
// lequel on lance ensuite une session. Couche PURE (testable sans fs) : la création réelle du
|
|
// dossier, le `git init` et le spawn vivent dans `routes/projects.ts`.
|
|
import { join } from 'node:path';
|
|
import { isSafeAbsolutePath } from './git.js';
|
|
|
|
/**
|
|
* Valide un nom de projet : UN SEUL segment de dossier. Anti-traversal de base : refuse les noms
|
|
* vides, trop longs, `.`/`..`, et tout caractère de séparation (`/`, `\`) ou NUL. La résolution du
|
|
* chemin complet (et sa re-validation) est faite par {@link resolveProjectDir}.
|
|
*/
|
|
export function isSafeProjectName(name: string): boolean {
|
|
const n = name.trim();
|
|
if (n.length === 0 || n.length > 255) return false;
|
|
if (n === '.' || n === '..') return false;
|
|
return !/[/\\\0]/.test(n);
|
|
}
|
|
|
|
/**
|
|
* Résout le dossier de projet `<root>/<name>`. Suppose `root` déjà validé absolu et existant par
|
|
* l'appelant ; re-valide le chemin final via {@link isSafeAbsolutePath} (défense en profondeur).
|
|
* Lève si `name` est invalide ou si le chemin résultant échappe l'arborescence.
|
|
*/
|
|
export function resolveProjectDir(root: string, name: string): string {
|
|
if (!isSafeProjectName(name)) {
|
|
throw Object.assign(new Error(`Invalid project name: ${name}`), { statusCode: 400 });
|
|
}
|
|
const dir = join(root, name.trim());
|
|
if (!isSafeAbsolutePath(dir)) {
|
|
throw Object.assign(new Error(`Unsafe project path: ${dir}`), { statusCode: 400 });
|
|
}
|
|
return dir;
|
|
}
|