Skip to content

fix: do not let a string type swallow array input in multi-type schemas - #876

Open
chuanghiduoc wants to merge 1 commit into
fastify:mainfrom
chuanghiduoc:fix/multitype-string-swallows-array
Open

fix: do not let a string type swallow array input in multi-type schemas#876
chuanghiduoc wants to merge 1 commit into
fastify:mainfrom
chuanghiduoc:fix/multitype-string-swallows-array

Conversation

@chuanghiduoc

Copy link
Copy Markdown

When a schema lists string before array in its type array, array input serializes to a comma-joined string and the JSON structure is lost.

Steps to reproduce

const build = require('fast-json-stringify')

const stringify = build({
  type: ['string', 'array'],
  items: { type: 'integer' }
}, { ajv: { allowUnionTypes: true } })

console.log(stringify([1, 2, 3]))
// ACTUAL:   "1,2,3"
// EXPECTED: [1,2,3]

Nested objects are destroyed the same way:

const stringify = build({
  type: ['string', 'array'],
  items: { type: 'object', properties: { a: { type: 'integer' } } }
}, { ajv: { allowUnionTypes: true } })

stringify([{ a: 1 }, { a: 2 }])
// ACTUAL:   "[object Object],[object Object]"
// EXPECTED: [{"a":1},{"a":2}]

Swapping the order to type: ['array', 'string'] produces the correct output, so the bug is order dependent.

Root cause

buildMultiTypeSerializer emits one branch per listed type, in list order. The string branch accepts anything that quacks like a stringifiable object:

typeof input === "object" &&
typeof input.toString === "function" &&
input.toString !== Object.prototype.toString

An array satisfies all three (Array.prototype.toString !== Object.prototype.toString), so array input is captured by the string branch and the later else if (Array.isArray(input)) branch is unreachable.

This is the same class of bug as #851 (object branch capturing arrays because typeof [] === 'object'), which was fixed by excluding arrays from that branch.

Fix

Exclude arrays from the string branch's duck-typing check, mirroring the object-branch guard from #851. Date/RegExp and genuine custom-toString objects keep working, since they are matched by the earlier instanceof checks or still pass the (now array-excluded) object check.

Tests

Added multi-type [string, array] round-trips an array to test/typesArray.test.js, covering both the array input and a plain string input for the same schema. It fails on main (fail 1) and passes with the fix.

Full unit suite: 501 passing, 0 failing, coverage stays at 100%.

A schema with type: ['string', 'array'] serialized array input as a
comma-joined string, dropping the JSON structure, because the string
branch's duck-typing check (typeof x === 'object' && x.toString !==
Object.prototype.toString) also matches arrays and made the sibling
array branch unreachable.

Exclude arrays from that check, mirroring the object-branch guard added
for the same class of bug in fastify#851.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant