A filter function checking if a value is in a list. Author passed a string into `Array<number>.includes`. Strict mode would have caught it; the project's tsconfig was looser, so it compiled and returned false at runtime.
The vulnerable diff
// lib/filter.ts
const ALLOWED_PORTS: number[] = [80, 443, 8080];
function isAllowedPort(port: unknown): boolean {
// BUG: port is string ("443" from URL query) — includes returns false.
return ALLOWED_PORTS.includes(port as any);
}What is wrong
Array `includes` and `indexOf` use strict equality (`===`) at runtime. `[1,2,3].includes("1")` is always false because `1 !== "1"`. TypeScript's strict mode catches this at compile time by typing the parameter of `Array<number>.includes` as `number`. Older or less-strict configs allowed any argument, leaving the runtime mismatch invisible.
The attack
Symptom: filter returns nothing, no error, looks like an empty filter.
ALLOWED_PORTS.includes("443") → false
ALLOWED_PORTS.includes(443) → true
// String came from URL.searchParams.get('port'), which always returns string.The fix is to convert the type before the check or to widen the array to accept both.
Mesrai's review comment
mesraipilot · Bot · reviewed 20 sec ago
[mesrai] [code-review] [Language] [Type] [low]
Mixed types in `includes`. ALLOWED_PORTS is number[] but port is
unknown/string. Runtime returns false silently.
Convert at the boundary:
function isAllowedPort(port: unknown): boolean {
const n = Number(port);
return Number.isFinite(n) && ALLOWED_PORTS.includes(n);
}
Or widen the array if the use case calls for it:
const ALLOWED_PORTS: (number | string)[] = [80, "80", 443, "443"];
Prefer the conversion approach — single source of truth in the typed
array.The fix
// lib/filter.ts — fixed
const ALLOWED_PORTS: number[] = [80, 443, 8080];
function isAllowedPort(port: unknown): boolean {
const n = Number(port);
if (!Number.isFinite(n)) return false;
return ALLOWED_PORTS.includes(n);
}Coerce to number, validate it parsed, then check. The check operates on aligned types.
Why human review missed it
Mixed-type `includes` is mostly caught by strict TypeScript but recurs in legacy codebases and at trust boundaries. Mesrai catches the pattern by tracing the array element type and the argument type.
Related rules + further reading
Mesrai rule pack: language/includes-type-mismatch — flags includes/indexOf with mismatched element type.
TypeScript: strict mode and `noImplicitAny`.
Common in URL query handling where everything is string.
Takeaway
Strict mode would catch this. Mesrai catches it regardless of strictness. Convert at the boundary, check on aligned types.