fix(web): Steuerzeichen in safe-next.ts als Unicode-Escapes statt Rohbytes
FORBIDDEN_CHARS_RE in sanitizeNextPath bestand aus drei rohen Steuerbytes (NUL, US, DEL) statt Escapes - jeder Editor, Formatierer oder Minifier in der Kette kann solche Bytes stillschweigend verschlucken. Umgeschrieben auf Unicode-Escapes fuer den Bereich U+0000 bis U+001F und U+007F. Zeichenmenge ueber alle 65536 Codepunkte aus U+0000 bis U+FFFF als unveraendert nachgewiesen (54 abgewiesene Codepunkte, Bitmap-SHA-256 3d58108b87e4641e506602cc701a66d11ec19cf20551937fada1826f50ecbe64 vor und nach dem Umbau identisch). Kommentar korrigiert: die vorherige Behauptung, Escapes seien "am Edge" noetig, war falsch - zwischen den beiden Escape-Schreibweisen gibt es zur Laufzeit keinen Unterschied. Quick-Vorgang 260921-i8x, Task 1/3. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
This commit is contained in:
@@ -1,6 +1,30 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { buildNextParam, sanitizeNextPath } from './safe-next';
|
||||
|
||||
// Die 54 Codepunkte, die `sanitizeNextPath` bei der Planung von quick-260921-i8x
|
||||
// ueber alle 65536 Codepunkte aus U+0000..U+FFFF gemessen abweist (SHA-256 der
|
||||
// Bitmap: 3d58108b87e4641e506602cc701a66d11ec19cf20551937fada1826f50ecbe64).
|
||||
// Hart hinterlegt, nicht aus der Regel abgeleitet (D-01): eine aus derselben
|
||||
// Regel erzeugte Erwartung wuerde jede Aenderung der Regel mitmachen und
|
||||
// bewiese damit nichts.
|
||||
const EXPECTED_REJECTED_CODEPOINTS: number[] = [
|
||||
...Array.from({ length: 0x20 }, (_, i) => i), // 0x00-0x1f
|
||||
0x20,
|
||||
0x5c,
|
||||
0x7f,
|
||||
0xa0,
|
||||
0x1680,
|
||||
...Array.from({ length: 0x200a - 0x2000 + 1 }, (_, i) => 0x2000 + i), // 0x2000-0x200a
|
||||
0x2028,
|
||||
0x2029,
|
||||
0x202f,
|
||||
0x205f,
|
||||
0x3000,
|
||||
0xfeff,
|
||||
];
|
||||
|
||||
/**
|
||||
* safe-next.test — Ruecksprung nach Anmeldung (quick-260917-gyd).
|
||||
*
|
||||
@@ -70,4 +94,34 @@ describe('sanitizeNextPath', () => {
|
||||
expect(sanitizeNextPath('/login/')).toBe('/');
|
||||
expect(sanitizeNextPath('/loginhistory')).toBe('/loginhistory');
|
||||
});
|
||||
|
||||
it('Test A: die abgewiesene Zeichenmenge ist und bleibt genau diese 54 Codepunkte (T-i8x-01)', () => {
|
||||
const rejected: number[] = [];
|
||||
for (let cp = 0; cp <= 0xffff; cp++) {
|
||||
const input = `/a${String.fromCharCode(cp)}b`;
|
||||
if (sanitizeNextPath(input) === '/') {
|
||||
rejected.push(cp);
|
||||
}
|
||||
}
|
||||
expect(rejected).toEqual(EXPECTED_REJECTED_CODEPOINTS);
|
||||
expect(rejected).toHaveLength(54);
|
||||
});
|
||||
|
||||
it('Test B: die Datei enthaelt kein rohes Steuerzeichen', () => {
|
||||
const filePath = join(import.meta.dirname, 'safe-next.ts');
|
||||
const bytes = readFileSync(filePath);
|
||||
let count = 0;
|
||||
for (const byte of bytes) {
|
||||
if ((byte < 0x20 && byte !== 0x0a) || byte === 0x7f) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
expect(count).toBe(0);
|
||||
});
|
||||
|
||||
it('Test C: der Zwei-Zeichen-Praefixtest bleibt scharf', () => {
|
||||
expect(sanitizeNextPath('//host')).toBe('/');
|
||||
expect(sanitizeNextPath('/\\host')).toBe('/');
|
||||
expect(sanitizeNextPath(`/${String.fromCharCode(0x00)}`)).toBe('/');
|
||||
});
|
||||
});
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user