@@ -13,6 +13,17 @@ import {
1313import { parseJsonc } from "../../jsonc/parse.js" ;
1414import { parseJsonl } from "../../jsonl/parse.js" ;
1515
16+ function expectUtf16SafeLimitError ( run : ( ) => unknown , expectedInput : string ) : void {
17+ try {
18+ run ( ) ;
19+ expect . fail ( "expected an OC_PATH_TOO_LONG error" ) ;
20+ } catch ( err ) {
21+ expect ( err ) . toBeInstanceOf ( OcPathError ) ;
22+ expect ( ( err as OcPathError ) . code ) . toBe ( "OC_PATH_TOO_LONG" ) ;
23+ expect ( ( err as OcPathError ) . input ) . toBe ( expectedInput ) ;
24+ }
25+ }
26+
1627describe ( "encoding edges" , ( ) => {
1728 it ( "strips leading UTF-8 BOM from path string" , ( ) => {
1829 expect ( parseOcPath ( "oc://X/Y" ) . file ) . toBe ( "X" ) ;
@@ -77,6 +88,23 @@ describe("path-string and traversal caps", () => {
7788 expect ( ( ) => parseOcPath ( "oc://X/" + "a" . repeat ( MAX_PATH_LENGTH ) ) ) . toThrow ( / e x c e e d s .* b y t e s / ) ;
7889 } ) ;
7990
91+ it ( "keeps overlong parse input UTF-16 safe" , ( ) => {
92+ const prefix = `oc://${ "a" . repeat ( 74 ) } ` ;
93+ expectUtf16SafeLimitError (
94+ ( ) => parseOcPath ( `${ prefix } 😀${ "b" . repeat ( MAX_PATH_LENGTH ) } ` ) ,
95+ `${ prefix } …` ,
96+ ) ;
97+ } ) ;
98+
99+ it ( "keeps post-NFC overlong parse input UTF-16 safe" , ( ) => {
100+ const prefix = `oc://${ "a" . repeat ( 74 ) } ` ;
101+ const input = `${ prefix } 😀${ "\u0344" . repeat ( MAX_PATH_LENGTH - prefix . length - 2 ) } ` ;
102+ expect ( input ) . toHaveLength ( MAX_PATH_LENGTH ) ;
103+ expect ( input . normalize ( "NFC" ) . length ) . toBeGreaterThan ( MAX_PATH_LENGTH ) ;
104+
105+ expectUtf16SafeLimitError ( ( ) => parseOcPath ( input ) , `${ prefix } …` ) ;
106+ } ) ;
107+
80108 it ( "parseOcPath accepts a path right at the cap" , ( ) => {
81109 const justUnder = "oc://X/" + "a" . repeat ( MAX_PATH_LENGTH - "oc://X/" . length ) ;
82110 expect ( ( ) => parseOcPath ( justUnder ) ) . not . toThrow ( ) ;
@@ -88,6 +116,15 @@ describe("path-string and traversal caps", () => {
88116 ) ;
89117 } ) ;
90118
119+ it ( "keeps overlong formatted paths UTF-16 safe" , ( ) => {
120+ const sectionPrefix = "a" . repeat ( 72 ) ;
121+ expectUtf16SafeLimitError (
122+ ( ) =>
123+ formatOcPath ( { file : "X" , section : `${ sectionPrefix } 😀${ "b" . repeat ( MAX_PATH_LENGTH ) } ` } ) ,
124+ `oc://X/${ sectionPrefix } …` ,
125+ ) ;
126+ } ) ;
127+
91128 it ( "walker depth cap fires on synthetic deeply-nested AST" , ( ) => {
92129 // Bypasses parser depth cap so the walker defense fires in isolation.
93130 type V = import ( "../../jsonc/ast.js" ) . JsoncValue ;
0 commit comments