39 lines
1.9 KiB
JavaScript
39 lines
1.9 KiB
JavaScript
// okf-links.mjs
|
|
// Delt bundle-root-relativ lenke-resolver (Step 6), brukt av BAADE relasjons-
|
|
// emitteringen (innboks-relations) og okf-check++ --strict-ingest (Step 8).
|
|
// Konvensjon: en trygg bundle-lenke er ROOT-RELATIV med leading '/' (resolveres
|
|
// mot bundle-rota, jf. SC-regex \]\(/.*\.md\)). Avviser '../'-escape, absolutt
|
|
// Windows-sti (backslash), og scheme-lenker (file://, http(s)://). Den ENESTE
|
|
// lenke-allow-listen -- delt slik at emit og validering aldri divergerer.
|
|
// Zero npm dependencies.
|
|
|
|
import path from 'node:path';
|
|
|
|
// Scheme-prefiks (file://, http://, https://, ...) -- avvises uansett.
|
|
const SCHEME_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
// '..'-segment hvor som helst i stien (escape ut av bundle-rota).
|
|
const PARENT_SEGMENT_RE = /(^|\/)\.\.(\/|$)/;
|
|
|
|
// Sann hvis target er en trygg, bundle-root-relativ .md-lenke (leading '/').
|
|
export function isSafeBundleLink(target) {
|
|
if (typeof target !== 'string' || target === '') return false;
|
|
if (target.includes('\0')) return false; // null-byte
|
|
if (target.includes('\\')) return false; // backslash (Windows-sti)
|
|
if (SCHEME_RE.test(target)) return false; // file://, http(s)://, ...
|
|
if (!target.startsWith('/')) return false; // maa vaere bundle-root-relativ
|
|
if (PARENT_SEGMENT_RE.test(target)) return false; // ingen '..'-escape
|
|
if (!target.endsWith('.md')) return false; // bundle-konseptlenker er .md
|
|
return true;
|
|
}
|
|
|
|
// Resolverer en trygg bundle-lenke til absolutt sti UNDER bundleRoot, ellers null.
|
|
// Belt-and-suspenders: isSafeBundleLink avviser '..' allerede, men confinement
|
|
// re-sjekkes paa den resolverte stien.
|
|
export function resolveBundleLink(target, bundleRoot) {
|
|
if (!isSafeBundleLink(target)) return null;
|
|
const rel = target.replace(/^\/+/, '');
|
|
const root = path.resolve(bundleRoot);
|
|
const resolved = path.resolve(root, rel);
|
|
if (resolved !== root && !resolved.startsWith(root + path.sep)) return null;
|
|
return resolved;
|
|
}
|