Files
arboretum/packages/server/src/core/project.ts
Johan LEROY 65ef616867
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
chore(typo): retire tous les tirets cadratins/demi-cadratins + garde CI
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).
2026-07-17 16:44:00 +02:00

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;
}