Pārlūkot izejas kodu

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).
Chinesezjc 1 mēnesi atpakaļ
vecāks
revīzija
0ae52fbb9f

+ 2 - 2
.agents/notes/implemented/feature/2026-07-30-web-read-card.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-07-30-web-read-card.md
-2026-07-30-web-read-card.md: 076959680737d3bc439deb367dcdcdc5da83dfb3
-2026-07-30-web-read-card.zh.md: c3e3ec1c3ccd8b36832bc12b6a7ffd000745fb74
+2026-07-30-web-read-card.md: 7d517beb359eec17948ea312b0478604cf92a49b
+2026-07-30-web-read-card.zh.md: bfcc17e782a6a1caf0f775875264839af357be0d

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 0 - 0
.agents/notes/implemented/feature/2026-07-30-web-read-card.md


Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 2 - 2
.agents/notes/implemented/feature/2026-07-30-web-read-card.zh.md


+ 6 - 1
packages/fs/tool-fs/src/read-render.ts

@@ -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
 }
 
 /**

+ 9 - 0
packages/fs/tool-fs/tests/read-render.spec.ts

@@ -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', () => {

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels