fix(fs): guard langFromPath against Object.prototype extension keys

A filename whose extension is an Object.prototype key (foo.constructor,
foo.__proto__) resolved to the inherited member through the plain-object index,
so a function reached the read card's lang hint and failed the tool-output JSON
validation, failing an otherwise successful read. Look the extension up as an
own property only. Added rejection tests, converted the zh Note headings to the
all-English sibling convention, and named the parallel-file-reads terminal
golden as the TUI-unchanged evidence in the Testing section (both languages).
This commit is contained in:
Chinesezjc
2026-07-30 20:32:51 +08:00
parent 22ff54dda4
commit 0ae52fbb9f
5 changed files with 21 additions and 7 deletions

View File

@@ -201,7 +201,12 @@ export function langFromPath(path: string): string | undefined {
const dot = base.lastIndexOf('.')
// A leading dot is a dotfile (no extension), not an empty extension.
if (dot <= 0) return undefined
return LANG_BY_EXTENSION[base.slice(dot + 1).toLowerCase()]
const ext = base.slice(dot + 1).toLowerCase()
// Own-property check only: a filename whose extension is an Object.prototype
// key (`foo.constructor`, `foo.__proto__`) must map to no language, not to the
// inherited member — otherwise a function would reach `lang` and fail the
// tool-output JSON validation.
return Object.hasOwn(LANG_BY_EXTENSION, ext) ? LANG_BY_EXTENSION[ext] : undefined
}
/**

View File

@@ -139,6 +139,15 @@ describe('langFromPath', () => {
expect(langFromPath('data.unknownext')).toBeUndefined()
expect(langFromPath('trailingdot.')).toBeUndefined()
})
it('returns undefined for a filename whose extension is an Object.prototype key', () => {
// Own-property lookup only: these must not resolve to the inherited member
// (a function/object), which would fail the tool-output JSON validation.
expect(langFromPath('foo.constructor')).toBeUndefined()
expect(langFromPath('foo.__proto__')).toBeUndefined()
expect(langFromPath('foo.toString')).toBeUndefined()
expect(langFromPath('foo.hasOwnProperty')).toBeUndefined()
})
})
describe('readMetaFromMeta', () => {