Repository navigation
Domain-model input validation: prototype keys, invalid Date, fractional timeoutMs, lone surrogates, frozen getAll #76
Copy link
Copy link
Closed
Labels
area:coreCore HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*Core HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*type:bugDefect in shipped behaviorDefect in shipped behavior
Milestone
Description
Part of the audit remediation umbrella #67. Milestone 4. Severity: HIGH (first item), MEDIUM (rest). Requirements: SEAM-27, HTTP-50, HTTP-35, HTTP-5, typed-errors rule.
Current SDK behavior
- Prototype keys satisfy a path placeholder.
substitutePathParams
(packages/core/src/seams/operation.ts:106) readspathParams?.[name]on a plain object.
buildRequest('https://api.example.com', {method:'GET', pathTemplate:'/users/{constructor}', pathParams:{}})
buildshttps://api.example.com/users/function%20Object%28%29%20%7B%20%5Bnative%20code%5D%20%7D
instead of throwingOperationAssemblyError.{toString}and{__proto__}behave the same. - Invalid
Dategoes on the wire.RequestConditionsBuilder.ifModifiedSince/
ifUnmodifiedSince(http/request-conditions.ts:204) accept anyDate.toRfc1123(:30)
isdate.toUTCString(). An invalidDaterendersIf-Modified-Since: Invalid Date, which
passes HTTP-18 outbound validation. - Fractional
timeoutMsthrows at send.RequestOptionsBuilder.timeoutMs(1.5)is accepted
by design (http/request-options.ts:175-186, pinned by a test).composeSignal
(seams/transport.ts:89) passes it toAbortSignal.timeout(), which throws
RangeError: The value of "delay" is out of range. It must be an integer.inside the
transport. Same for values above 4294967295.composeSignalhas no@throws. - Lone surrogate throws
URIError.encodeRfc3986Component(http/rfc3986.ts:13) calls
encodeURIComponent.QueryParams.newBuilder().add('c', '\uD800').build().encode()throws
URIError: URI malformed, outside theDexpaceErrortree, with no@throwsonencode()or
equals().buildRequestsurfaces it for query and path params. getAllon an absent name returns an unfrozen array.headers.ts:133,136and
query-params.ts:141,144return a fresh[]; the TSDoc promises a frozen list. No test
asserts the present-name array is frozen.Headers.equalshas no direct test.headers.ts:184; exercised only through
Request.equals.TeeSinkaccepts a fractionaltapLimit.io/tee-sink.ts:34-37checks>= 0only; the
fault surfaces later inByteQueue.copyTo.
Expected behavior
A builder or factory rejects an invalid input at the call site that supplied it, with an
error from the DexpaceError tree. A value the model accepts does not throw later in another
package. A getter that promises a frozen list returns one on every path. Public methods that
throw name the error class in @throws.
Notes and leads
- Path params:
Object.hasOwn(pathParams, name), or copy into a null-prototype object. - Dates: reject
Number.isNaN(date.getTime())in both setters. timeoutMs: decide once. Reject non-integer and> 2**32 - 1in the setter (update the TSDoc
and the existing fractional-accepting test), or round withMath.ceiland clamp in
composeSignal. The setter is the HTTP-35-faithful place.- Surrogates:
String.prototype.isWellFormed()exists on theengines.node >= 20.3floor.
Check atadd()/set()time and throw the existing query-param validation error; or catch
inencodeRfc3986Componentand rethrow aDexpaceErrorwith{cause}. getAll: return one shared frozen empty array.TeeSink:Number.isInteger(tapLimit) || tapLimit === Number.POSITIVE_INFINITY.- Tests: one per bullet, in the colocated
*.test.ts;{constructor}with emptypathParams;
Headers.equalsdirect cases (order of names, order of values, subset). api:localafter the@throwsedits. Patch changeset for@dexpace/core.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:coreCore HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*Core HTTP, IO, body, context, encoding: HTTP-* IO-* BODY-* CTX-* UTF-*type:bugDefect in shipped behaviorDefect in shipped behavior
Activity
refactor(core): consolidate EMPTY_VALUE_LIST in http/builder.ts
docs(core): errors.ts TSDoc matches what the two classes now cover
docs: http.md states the real timeoutMs range
docs: deviations — two rows for #76, per D0
Wahbeh-Mohammad commentedon Sep 5, 2026
Addressed in PR #92, merged into
audit/remediation-67; reachesmvpthrough PR #98. Decision record:docs/audit-67-decisions.md.