Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions packages/common/http/src/transfer_cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -421,10 +421,15 @@ function makeCacheKey(
serializedBody = '';
}

// Joining with `|` lets a shifted field boundary (url `/a` + body `b|c` vs url `/a|b` + body `c`)
// collapse to the same string and thus the same hash. `\0` cannot occur in a valid url or in
// encoded params, so the field boundaries can't be forged by field content.
const key = [method, responseType, mappedRequestUrl, serializedBody, encodedParams].join('\0');
// Serialize the fields as a structured value so field content cannot shift boundaries between
// adjacent entries. Sentinel delimiters are ambiguous when the delimiter occurs in a field.
const key = JSON.stringify([
method,
responseType,
mappedRequestUrl,
serializedBody,
encodedParams,
]);
const hash = generateHash(key);

return makeStateKey(hash);
Expand Down
17 changes: 15 additions & 2 deletions packages/common/http/test/transfer_cache_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,15 +541,15 @@ describe('TransferCache', () => {

const transferState = TestBed.inject(TransferState);
expect(JSON.parse(transferState.toJson()) as Record<string, unknown>).toEqual({
'd501aa2d57b63a95df74e3b0558782b71b077974e968ed303cd30b27e4b70702': {
'1f714be6947c17dea957abf53634001d06b80e55645090da462510f830cb2783': {
[BODY]: 'foo',
[HEADERS]: {},
[STATUS]: 200,
[STATUS_TEXT]: 'OK',
[REQ_URL]: '/test-1',
[RESPONSE_TYPE]: 'json',
},
'ceddc6689dc1f2fc3a0b8c364b6e00a79b99a149f27e84da87cec03d44c150c8': {
'741c59135aade1b04255843ce2685e5864f8a42beea9e2371fb7036df01aa75a': {
[BODY]: 'buzz',
[HEADERS]: {},
[STATUS]: 200,
Expand Down Expand Up @@ -895,6 +895,19 @@ describe('TransferCache', () => {
makeRequestAndExpectOne('/items/a|b', null, {method: 'POST', transferCache: true, body: 'c'});
});

it('should differentiate POST requests with a NUL-shifted url/body boundary', () => {
makeRequestAndExpectOne('/items/a#', 'seed', {
method: 'POST',
transferCache: true,
body: 'b\0c',
});
makeRequestAndExpectOne('/items/a#\0b', 'trusted', {
method: 'POST',
transferCache: true,
body: 'c',
});
});

it('should cache POST with the differing body in object form', () => {
makeRequestAndExpectOne('/test-1', null, {
method: 'POST',
Expand Down