Skip to content

fix(validation): read request bodies and field tables by own keys - #112

Merged
Upd4ting merged 1 commit into
mainfrom
fix/ant-1398
Nov 9, 2026
Merged

Upd4ting merged 1 commit into
mainfrom
fix/ant-1398

Conversation

@Upd4ting

@Upd4ting Upd4ting commented Nov 8, 2026

Copy link
Copy Markdown
Member

Why

A controller field whose name matches an Object.prototype member (toString, valueOf, constructor) was found on the prototype instead of in the request body or field table. A mandatory field of that name was never reported missing. Its validator ran against the inherited function when the body omitted the field. A non-field target default of that name was written as a column. Field names come from the controller class, not the client, so a request cannot introduce such a name.

Fixes #65

What changed

Every site of this family now reads own keys (Object.hasOwn or the existing ownEntry helper from #60):

Site Before After
src/components.ts isAbsentFromBody (Validation.MandatoryFields) !(name in body) !Object.hasOwn(body, name)
src/components.ts Validation.ValidateTypes !(name in obj) !Object.hasOwn(obj, name)
src/internal/properties.ts writeTargetDefaults key in meta.fields ownEntry(meta.fields, key)
src/internal/properties.ts WriteProperties props and setters bodyData[key] ownEntry(bodyData, key)
src/internal/list.ts isHeldByKeyedModifier fields[field]?.some(...) ownEntry(fields, field)?.some(...)

The issue lists the first three. The other two came up while fixing them. With only the validation sites fixed, a new row still copied the inherited toString function from the body and answered 500. A list sorted on a sortable field named valueOf threw fields[field]?.some is not a function (500).

For any other field name the behaviour is unchanged. An absent field still reads as undefined, and the edit path already filtered by own keys.

flowchart LR
  B[request body] --> M{"Object.hasOwn(body, name)?"}
  M -- no --> R400[400 missing_mandatory_fields]
  M -- yes --> V{"validator and Object.hasOwn(body, name)?"}
  V -- validate --> W[WriteProperties: ownEntry body values]
  V -- skip --> W
  D[target defaults] --> F{"ownEntry(meta.fields, key)?"}
  F -- field --> W
  F -- not a field --> I[instance only, no column]
  W --> DB[(row)]
Loading

Proof

New tests in src/tests/components/prototype_named_field.test.ts and one added to prototype_named_filter.test.ts, run with pnpm exec ajs module test . -f dist/tests/components/<file>.test.js.

Before, on main (354796d):

    1) reports the field missing when it is mandatory and the body omits it
    2) skips the field's validator when the body omits it
    3) writes only declared fields
  1) reports the field missing when it is mandatory and the body omits it:
      Field 'toString' holds a bigint, a symbol or a function, which no database stores
      -500
      +400
  2) skips the field's validator when the body omits it:
      AssertionError: {"code":"invalid_field_types","fields":["toString"]}: expected 400 to equal 200
  3) writes only declared fields:
      AssertionError: expected [ 'valueOf', 'title' ] to deeply equal [ 'title' ]
  1) List route - a field named like an Object.prototype member
       sorts on the field:
      (0 , common_1.getMetadata)(...).fields[field]?.some is not a function
      -500
      +200

After:

  List route - a field named like an Object.prototype member
    ✔ lists the rows when no request filter is given (65ms)
    ✔ filters on another field
    ✔ sorts on the field

  New route - a field named like an Object.prototype member
    ✔ reports the field missing when it is mandatory and the body omits it
    ✔ skips the field's validator when the body omits it

  Query.WriteProperties - a target default named like an Object.prototype member
    ✔ writes only declared fields

  6 passing (102ms)

pnpm run checks: Checks: 8 passed, 0 failed.

Attention

antelopejs/no-inherited-key-lookup flags none of these sites (an in against a parameter typed Record/FieldValues, and an optional-chained index into a metadata table). Widening the rule belongs in the tooling preset, not this repo.

A field named like an Object.prototype member (toString, valueOf) was
always present in a request body, was validated against the inherited
function, was written from it on a new row, and crashed a list sorted on
it. A target default of such a name was written as a column.

Fixes #65
@Upd4ting
Upd4ting merged commit dd3ae1c into main Nov 9, 2026
8 checks passed
@Upd4ting
Upd4ting deleted the fix/ant-1398 branch November 9, 2026 14:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant