fix: [SDK-4510] encode URL path segments and tighten alias validation#1463
Conversation
`removeTags` was sharing `validateArray` with `removeAliases`, which after the previous commit started enforcing the alias-only character/length rules on tag keys. Tag keys go into the JSON body, not URL paths, and `addTag` never enforced these constraints, so users who previously stored tag keys containing characters now in the blocklist could no longer remove them. Parameterize `validateArray` with a per-element validator so `removeTags` can keep using the lenient `validateStringLabel` while `removeAliases` keeps the new defaults. Adds a regression test covering punctuated tag keys round-tripped through `addTag`/`removeTag`. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@claude review |
There was a problem hiding this comment.
LGTM — the regression I flagged was fixed in 997cf21 and the new tag round-trip test in UserNamespace.test.ts locks in the behavior.
Extended reasoning...
Overview
PR #1463 centralizes URL path-segment encoding in OneSignalApiBase.call and tightens alias label/id validation. Six files touched: the new encoding chokepoint in src/shared/api/base.ts, removal of the now-redundant inline encoder in src/core/requests/api.ts, the validation tightening + parameterized validateArray in src/onesignal/User.ts, a new __test__/unit/http/urlEncoding.test.ts, expanded coverage in src/onesignal/UserNamespace.test.ts, and size-limit threshold bumps.
Security risks
This is a defensive hardening PR, so the security posture improves rather than regresses: percent-encoding at the network boundary closes any per-callsite gaps where caller-supplied data could smuggle path separators, queries, or fragments into URLs, and the alias blocklist (/?#&=\s\x00-\x1F\x7F + ..) plus 128-char cap prevents the most common URL-injection vectors before they ever reach call. The encoding implementation correctly preserves / as a path separator while encoding sub-delims that encodeURIComponent leaves alone, and also encodes % so callers cannot smuggle pre-encoded sequences. No auth/crypto/permissions code is touched.
Level of scrutiny
This sits at the boundary between routine hardening and security-sensitive code. I gave it elevated scrutiny because input validation + URL construction are exactly the surfaces where subtle mistakes have outsized impact, and the validation tightening risked backwards-compatibility issues for existing customer data (which is what I caught in my prior review). With the parameterized validateArray fix and the new round-trip regression test, the tightening is correctly scoped to alias path segments only.
Other factors
The author was responsive: my flagged regression about tag keys being collaterally tightened was acknowledged and fixed within ~12 minutes with the cleaner of the two suggested approaches (parameterizing validateArray rather than duplicating the check in removeTags). The test coverage is solid — urlEncoding.test.ts exercises reserved chars, sub-delims, control bytes, percent signs, and URL-safe pass-through; UserNamespace.test.ts covers the 128-char boundary, traversal substrings, the full character blocklist, and the tag-key round-trip regression case. The bug hunting system found no further issues on the latest revision.
After the previous commit scoped the alias-only constraints out of the shared `validateArray`, `validateLabel` is no longer used by anything but the alias APIs. Rename it to `validateAliasLabel` so the call sites read unambiguously. Co-authored-by: Cursor <cursoragent@cursor.com>
Description
1 Line Summary
Encode API request path segments at the network boundary and tighten alias label/id validation to harden URL construction.
Details
Internal hardening for SDK-4510.
OneSignalApiBase.callnow percent-encodes each segment of the request action viaencodeRFC3986URIComponentbefore composing the URL, instead of trusting per-callsite sanitization. This is a single chokepoint that covers every endpoint defined insrc/core/requests/api.ts.sanitizedAliasblock fromupdateUserByAliassince the encoding is centralized.validateAliasValueinsrc/onesignal/User.tsadds a 128-character limit (matching the documented public alias length) plus a small blocklist of characters that have no place in an alias label or id. BothaddAlias/addAliasesandremoveAlias/removeAliasesnow flow through it forlabelandid.size-limitthresholds modestly (page.es6.js 42.32 -> 42.4 kB, sw.js 12.35 -> 12.4 kB) to fit the new code. Final sizes are 42.37 kB and 12.35 kB respectively.Systems Affected
Validation
Tests
Info
__test__/unit/http/urlEncoding.test.tsverifies the centralized encoding behavior at theOneSignalApiBase.callboundary across spaces, reserved chars, sub-delims, control chars, percent signs, and pass-through of URL-safe segments.src/onesignal/UserNamespace.test.tswith cases covering the new label/id rules: length cap, traversal substring, dangerous characters, control characters, and a 128-character boundary acceptance test.Checklist
Programming Checklist
Interfaces:
Functions:
Typescript:
Other:
Screenshots
Info
Not applicable; no UI changes.
Checklist
Related Tickets
SDK-4510
Made with Cursor