[GH-ISSUE #6890] [Bug] HAR import accepts null timings and negative timing values #4226

Closed
opened 2026-10-31 00:47:52 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @coygeek on GitHub (Oct 23, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6890

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

HAR import accepts timings: null and a timing object with wait: -2 as successful one-record captures. Both values survive final capture-schema validation and remain unmarked in reported evidence. Removing the required wait property from an otherwise valid timing object correctly returns CaptureFormatError with reason format.

The production source worker independently emitted ok:true for the null and negative cases, while emitting ok:false for the missing-field control. This is an offline source-decoder/worker reproduction; no public CLI or MCP session was exercised.

Reproduction

Use the source checkout and locked dependencies with the runtime below. Save this script as /tmp/har-timings.mjs, then run it from the checkout:

FIXTURE_TIME=$(TZ=America/Los_Angeles date '+%Y-%m-%dT%H:%M:%S%:z') node --import ./node_modules/tsx/dist/loader.mjs /tmp/har-timings.mjs
import { pathToFileURL } from 'node:url';
import { createHash } from 'node:crypto';
const load = p => import(pathToFileURL(`${process.cwd()}/${p}`));
const { decodeHarCapture } = await load('src/browser/history/HarCapture.ts');
const { webNetworkCaptureSchema } = await load('src/domain/webNetworkCapture.ts');
for (const [name, edit] of [
 ['valid-control', e=>{}],
 ['missing-wait-control', e=>{delete e.timings.wait;}],
 ['null-timings', e=>{e.timings=null;}],
 ['negative-wait', e=>{e.timings.wait=-2;}],
]) {
 const har={log:{version:'1.2',creator:{name:'local fixture',version:'1'},entries:[{
  startedDateTime:process.env.FIXTURE_TIME,time:0,
  request:{method:'GET',url:'https://example.invalid/message',httpVersion:'HTTP/1.1',cookies:[],headers:[],queryString:[],headersSize:-1,bodySize:0},
  response:{status:200,statusText:'OK',httpVersion:'HTTP/1.1',cookies:[],headers:[],redirectURL:'',headersSize:-1,bodySize:5,content:{size:5,mimeType:'text/plain',text:'hello'}},
  cache:{},timings:{send:0,wait:0,receive:0}
 }]}};
 edit(har.log.entries[0]);const text=JSON.stringify(har);
 try {
  const imported=decodeHarCapture(text,[]);
  const capture=webNetworkCaptureSchema.parse({...imported,artifact:{path:'/tmp/fixture.har',bytes:Buffer.byteLength(text),sha256:createHash('sha256').update(text).digest('hex')},format:'har',runtime_attribution:'unknown',limitations:[]});
  console.log(JSON.stringify({name,accepted:true,total_records:capture.total_records,timings:capture.records[0].reported.timings}));
 } catch(error) {
  console.log(JSON.stringify({name,accepted:false,reason:error.reason,message:error.message,pointer:error.pointer}));
 }
}

The retained probes executed these independent cases before drafting. The listing omits unrelated body-encoding cases from the final combined probe and presents the valid fixture as a timing-specific control. A separate one-change probe set only historicalHar().log.entries[0].timings = null and observed {"total_records":1,"timings":null}. Executable and temporary paths are expressed generically for portability.

Expected behavior

Treat malformed timing data as a capture-format failure, with a useful field coordinate, instead of publishing a successful validated capture. The HAR 1.2 timing definition requires a timing object and nonnegative required send/wait/receive values. This report uses -2, which is invalid even under the upstream schema's apparent intended -1 lower bound.

REA's advertised capture contract in src/contracts/webNetworkCaptureToolContracts.ts:19 says malformed captures return no partial success. docs/web-network-captures.md also describes HAR 1.2 validation before record selection. This concerns malformed standard timing data, not unknown extension fields or faithful retention of numerically unsafe but valid JSON numbers.

Impact and scope

P2: a bounded validation defect. Malformed standard timing fields enter successful capture evidence, so callers cannot rely on successful import to distinguish these invalid records from HAR 1.2 records. The source values are preserved rather than repaired or silently discarded. No downstream crash, valid-input data loss, or live-target effect was demonstrated.

Observed with synthetic local HAR input through the production source decoder and worker. Public CLI/MCP, published packages, other platforms, and real producer captures were not exercised. An independent format check could reject these captures before import, but no separate validator was tested as a workaround.

REA version

rea-agents package metadata 6.7.0; sole tested source revision d0432d63c9fd359ee722c232993523765fe84cf9. No released-package reproduction is claimed.

Runtime and platform

Node.js v24.22.1; Linux x86_64; locked clone-local tsx loader, har-schema 2.0.0, and AJV 8.20.0. No external analysis provider ran.

Target and artifact context

Benign one-entry HAR 1.2 fixtures. Only the timing object or its wait property changes between the malformed cases and their control; the synthetic URL is never contacted.

Interface

Other

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Evidence provenance: observed production source-boundary and source-worker execution, with source review for the cause. Local verification: reproduced. Reproduction completeness: complete for these fixtures; public CLI/MCP remains untested. The final combined source probe exited 0 with empty stderr. Its valid plain-text fixture also passed complete capture-schema validation.

Actual output from the final combined probe:

{"name":"missing-wait-control","accepted":false,"reason":"format","message":"HAR schema validation failed: required (must have required property 'wait').","pointer":"/log/entries/0/timings"}
{"name":"null-timings","accepted":true,"total_records":1,"timings":null}
{"name":"negative-wait","accepted":true,"total_records":1,"timings":{"send":0,"wait":-2,"receive":0}}

Actual production-worker observations from an earlier independent fixture set:

{"case":"control","exit":0,"ok":true,"total_records":1,"timings":{"send":0,"wait":1.25,"receive":0}}
{"case":"missing-wait-control","exit":0,"ok":false,"reason":"format","message":"HAR schema validation failed: required (must have required property 'wait').","pointer":"/log/entries/0/timings"}
{"case":"null-timings","exit":0,"ok":true,"total_records":1,"timings":null}
{"case":"negative-wait","exit":0,"ok":true,"total_records":1,"timings":{"send":0,"wait":-2,"receive":0}}

Worker exit 0 is normal for a typed refusal: the significant distinction is the reply's ok field. The null and negative cases incorrectly have ok:true.

Exact source boundaries: HarSchema.ts loads the unchanged upstream schemas into AJV with strict:false. Locked node_modules/har-schema/lib/timings.json:1-42 omits type:"object" and spells lower-bound constraints min, which is not the draft-06 minimum keyword. src/browser/history/HarCapture.ts:34-38 accepts the validator's result and returns the records at lines 60-102. src/browser/history/NodeCaptureProcess.ts:43 wraps that result in ok:true.

Typed outcome or recovery expected

Return a format refusal identifying /log/entries/0/timings or the affected timing field. Preserve the existing correct missing-wait refusal and acceptance of valid timing objects. Do not invent missing timing measurements.

Analysis or suspected cause

Source-derived: a JSON Schema required constraint applies to objects and does not make null an object. The upstream timing schema's missing object type therefore permits null. Its min properties do not enforce the intended numeric bounds, and AJV's non-strict setup accepts the unknown keyword without providing that constraint. REA currently relies on the upstream result without another format check at this boundary.

Additional context

The supplied local inventory of 1,079 open issues, including full bodies, was searched for HAR timing/schema validation, negative duration, har-schema, and the production validator names. No matching report was found. Open #6044 concerns grouping-view diagnostics and #5444 concerns numeric-field documentation. The submitting agent also searched current open and closed issues before publication. All reproduction finished before drafting, with no post-draft rerun.

Restoration check: the null and negative timing cases must produce a typed format refusal; the valid control must remain accepted and the missing-required-field control must remain rejected. Neither replacing null with invented measurements nor filtering out the malformed record restores the advertised all-record validation contract.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Executed source-decoder and production-worker probes accept invalid standard timing values as successful capture records, despite the advertised malformed-input refusal. The verified effect is bounded validation/evidence correctness; no downstream crash or loss of valid source data was observed.
  • Workaround status: unverified
  • Workaround basis: A separate HAR format validator could reject malformed timing input before import, but no independent validator or general repair preserving unknown measurements was exercised.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6890 ## Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ## Area Evidence, artifacts, or reconstruction ## Observed behavior HAR import accepts `timings: null` and a timing object with `wait: -2` as successful one-record captures. Both values survive final capture-schema validation and remain unmarked in reported evidence. Removing the required `wait` property from an otherwise valid timing object correctly returns `CaptureFormatError` with reason `format`. The production source worker independently emitted `ok:true` for the null and negative cases, while emitting `ok:false` for the missing-field control. This is an offline source-decoder/worker reproduction; no public CLI or MCP session was exercised. ## Reproduction Use the source checkout and locked dependencies with the runtime below. Save this script as `/tmp/har-timings.mjs`, then run it from the checkout: ```sh FIXTURE_TIME=$(TZ=America/Los_Angeles date '+%Y-%m-%dT%H:%M:%S%:z') node --import ./node_modules/tsx/dist/loader.mjs /tmp/har-timings.mjs ``` ```javascript import { pathToFileURL } from 'node:url'; import { createHash } from 'node:crypto'; const load = p => import(pathToFileURL(`${process.cwd()}/${p}`)); const { decodeHarCapture } = await load('src/browser/history/HarCapture.ts'); const { webNetworkCaptureSchema } = await load('src/domain/webNetworkCapture.ts'); for (const [name, edit] of [ ['valid-control', e=>{}], ['missing-wait-control', e=>{delete e.timings.wait;}], ['null-timings', e=>{e.timings=null;}], ['negative-wait', e=>{e.timings.wait=-2;}], ]) { const har={log:{version:'1.2',creator:{name:'local fixture',version:'1'},entries:[{ startedDateTime:process.env.FIXTURE_TIME,time:0, request:{method:'GET',url:'https://example.invalid/message',httpVersion:'HTTP/1.1',cookies:[],headers:[],queryString:[],headersSize:-1,bodySize:0}, response:{status:200,statusText:'OK',httpVersion:'HTTP/1.1',cookies:[],headers:[],redirectURL:'',headersSize:-1,bodySize:5,content:{size:5,mimeType:'text/plain',text:'hello'}}, cache:{},timings:{send:0,wait:0,receive:0} }]}}; edit(har.log.entries[0]);const text=JSON.stringify(har); try { const imported=decodeHarCapture(text,[]); const capture=webNetworkCaptureSchema.parse({...imported,artifact:{path:'/tmp/fixture.har',bytes:Buffer.byteLength(text),sha256:createHash('sha256').update(text).digest('hex')},format:'har',runtime_attribution:'unknown',limitations:[]}); console.log(JSON.stringify({name,accepted:true,total_records:capture.total_records,timings:capture.records[0].reported.timings})); } catch(error) { console.log(JSON.stringify({name,accepted:false,reason:error.reason,message:error.message,pointer:error.pointer})); } } ``` The retained probes executed these independent cases before drafting. The listing omits unrelated body-encoding cases from the final combined probe and presents the valid fixture as a timing-specific control. A separate one-change probe set only `historicalHar().log.entries[0].timings = null` and observed `{"total_records":1,"timings":null}`. Executable and temporary paths are expressed generically for portability. ## Expected behavior Treat malformed timing data as a capture-format failure, with a useful field coordinate, instead of publishing a successful validated capture. The [HAR 1.2 timing definition](https://github.com/ahmadnassri/har-spec/blob/master/versions/1.2.md#timings) requires a timing object and nonnegative required send/wait/receive values. This report uses `-2`, which is invalid even under the upstream schema's apparent intended `-1` lower bound. REA's advertised capture contract in `src/contracts/webNetworkCaptureToolContracts.ts:19` says malformed captures return no partial success. `docs/web-network-captures.md` also describes HAR 1.2 validation before record selection. This concerns malformed standard timing data, not unknown extension fields or faithful retention of numerically unsafe but valid JSON numbers. ## Impact and scope P2: a bounded validation defect. Malformed standard timing fields enter successful capture evidence, so callers cannot rely on successful import to distinguish these invalid records from HAR 1.2 records. The source values are preserved rather than repaired or silently discarded. No downstream crash, valid-input data loss, or live-target effect was demonstrated. Observed with synthetic local HAR input through the production source decoder and worker. Public CLI/MCP, published packages, other platforms, and real producer captures were not exercised. An independent format check could reject these captures before import, but no separate validator was tested as a workaround. ## REA version `rea-agents` package metadata 6.7.0; sole tested source revision `d0432d63c9fd359ee722c232993523765fe84cf9`. No released-package reproduction is claimed. ## Runtime and platform Node.js v24.22.1; Linux x86_64; locked clone-local tsx loader, har-schema 2.0.0, and AJV 8.20.0. No external analysis provider ran. ## Target and artifact context Benign one-entry HAR 1.2 fixtures. Only the timing object or its `wait` property changes between the malformed cases and their control; the synthetic URL is never contacted. ## Interface Other ## Earliest failure stage Request parsing or validation ## Relevant output or evidence Evidence provenance: observed production source-boundary and source-worker execution, with source review for the cause. Local verification: reproduced. Reproduction completeness: complete for these fixtures; public CLI/MCP remains untested. The final combined source probe exited 0 with empty stderr. Its valid plain-text fixture also passed complete capture-schema validation. Actual output from the final combined probe: ```jsonl {"name":"missing-wait-control","accepted":false,"reason":"format","message":"HAR schema validation failed: required (must have required property 'wait').","pointer":"/log/entries/0/timings"} {"name":"null-timings","accepted":true,"total_records":1,"timings":null} {"name":"negative-wait","accepted":true,"total_records":1,"timings":{"send":0,"wait":-2,"receive":0}} ``` Actual production-worker observations from an earlier independent fixture set: ```jsonl {"case":"control","exit":0,"ok":true,"total_records":1,"timings":{"send":0,"wait":1.25,"receive":0}} {"case":"missing-wait-control","exit":0,"ok":false,"reason":"format","message":"HAR schema validation failed: required (must have required property 'wait').","pointer":"/log/entries/0/timings"} {"case":"null-timings","exit":0,"ok":true,"total_records":1,"timings":null} {"case":"negative-wait","exit":0,"ok":true,"total_records":1,"timings":{"send":0,"wait":-2,"receive":0}} ``` Worker exit 0 is normal for a typed refusal: the significant distinction is the reply's `ok` field. The null and negative cases incorrectly have `ok:true`. Exact source boundaries: [`HarSchema.ts`](https://github.com/morluto/rea/blob/d0432d63c9fd359ee722c232993523765fe84cf9/src/browser/history/HarSchema.ts#L13-L29) loads the unchanged upstream schemas into AJV with `strict:false`. Locked `node_modules/har-schema/lib/timings.json:1-42` omits `type:"object"` and spells lower-bound constraints `min`, which is not the draft-06 `minimum` keyword. `src/browser/history/HarCapture.ts:34-38` accepts the validator's result and returns the records at lines 60-102. `src/browser/history/NodeCaptureProcess.ts:43` wraps that result in `ok:true`. ## Typed outcome or recovery expected Return a format refusal identifying `/log/entries/0/timings` or the affected timing field. Preserve the existing correct missing-`wait` refusal and acceptance of valid timing objects. Do not invent missing timing measurements. ## Analysis or suspected cause Source-derived: a JSON Schema `required` constraint applies to objects and does not make `null` an object. The upstream timing schema's missing object type therefore permits `null`. Its `min` properties do not enforce the intended numeric bounds, and AJV's non-strict setup accepts the unknown keyword without providing that constraint. REA currently relies on the upstream result without another format check at this boundary. ## Additional context The supplied local inventory of 1,079 open issues, including full bodies, was searched for HAR timing/schema validation, negative duration, har-schema, and the production validator names. No matching report was found. Open #6044 concerns grouping-view diagnostics and #5444 concerns numeric-field documentation. The submitting agent also searched current open and closed issues before publication. All reproduction finished before drafting, with no post-draft rerun. Restoration check: the null and negative timing cases must produce a typed format refusal; the valid control must remain accepted and the missing-required-field control must remain rejected. Neither replacing null with invented measurements nor filtering out the malformed record restores the advertised all-record validation contract. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Executed source-decoder and production-worker probes accept invalid standard timing values as successful capture records, despite the advertised malformed-input refusal. The verified effect is bounded validation/evidence correctness; no downstream crash or loss of valid source data was observed. - Workaround status: unverified - Workaround basis: A separate HAR format validator could reject malformed timing input before import, but no independent validator or general repair preserving unknown measurements was exercised.
Author
Owner

@gongfpp commented on GitHub (Oct 23, 2026):

I noticed the overlapping implementation in #6904 and will not open a competing fix for this issue. I can help validate the timing-boundary cases on the existing PR, including lossless numeric literals, and check that the documentation accurately describes any divergence from the pinned upstream schema. The lossy fractional-boundary case is already raised in its review, so I will follow that thread rather than duplicate the finding. Please let me know if a separate, non-overlapping validation task would help.

<!-- gh-comment-id:6372535919 --> @gongfpp commented on GitHub (Oct 23, 2026): I noticed the overlapping implementation in #6904 and will not open a competing fix for this issue. I can help validate the timing-boundary cases on the existing PR, including lossless numeric literals, and check that the documentation accurately describes any divergence from the pinned upstream schema. The lossy fractional-boundary case is already raised in its review, so I will follow that thread rather than duplicate the finding. Please let me know if a separate, non-overlapping validation task would help.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4226
No description provided.