mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(http): strip RFC 6265 DQUOTE characters and handle URIError in parseCookieValue
Previously, `parseCookieValue` did not strip enclosing double quotes (`DQUOTE`) from quoted cookie values as specified in RFC 6265 Section 4.1.1. In addition, malformed percent-encoding in cookie values caused an unhandled `URIError` when calling `decodeURIComponent`.
(cherry picked from commit 280d09b160)
This commit is contained in:
committed by
Jessica Janiuk
parent
688a0a7118
commit
32af9b525e
@@ -135,6 +135,7 @@ describe('HttpXsrfInterceptor', () => {
|
||||
expect(req.request.headers.get('X-XSRF-TOKEN')).toEqual('blah');
|
||||
req.flush({});
|
||||
});
|
||||
|
||||
it('does not set the header for a null token', () => {
|
||||
TestBed.resetTestingModule();
|
||||
TestBed.configureTestingModule({
|
||||
@@ -160,10 +161,12 @@ describe('HttpXsrfInterceptor', () => {
|
||||
expect(req.request.headers.has('X-XSRF-TOKEN')).toEqual(false);
|
||||
req.flush({});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
backend.verify();
|
||||
});
|
||||
});
|
||||
|
||||
describe('HttpXsrfCookieExtractor', () => {
|
||||
let document: {[key: string]: string};
|
||||
let extractor: HttpXsrfCookieExtractor;
|
||||
@@ -181,20 +184,38 @@ describe('HttpXsrfCookieExtractor', () => {
|
||||
});
|
||||
extractor = TestBed.inject(HttpXsrfCookieExtractor);
|
||||
});
|
||||
|
||||
it('parses the cookie from document.cookie', () => {
|
||||
expect(extractor.getToken()).toEqual('test');
|
||||
});
|
||||
|
||||
it('does not re-parse if document.cookie has not changed', () => {
|
||||
expect(extractor.getToken()).toEqual('test');
|
||||
expect(extractor.getToken()).toEqual('test');
|
||||
expect(getParseCount(extractor)).toEqual(1);
|
||||
});
|
||||
|
||||
it('re-parses if document.cookie changes', () => {
|
||||
expect(extractor.getToken()).toEqual('test');
|
||||
document['cookie'] = 'XSRF-TOKEN=blah';
|
||||
expect(extractor.getToken()).toEqual('blah');
|
||||
expect(getParseCount(extractor)).toEqual(2);
|
||||
});
|
||||
|
||||
it('extracts token without quotes when value is enclosed in DQUOTE characters', () => {
|
||||
document['cookie'] = 'XSRF-TOKEN="quoted-token-value"';
|
||||
expect(extractor.getToken()).toEqual('quoted-token-value');
|
||||
});
|
||||
|
||||
it('extracts token without quotes when value is enclosed in URL-encoded DQUOTE characters (%22)', () => {
|
||||
document['cookie'] = 'XSRF-TOKEN=%22quoted-token-value%22';
|
||||
expect(extractor.getToken()).toEqual('quoted-token-value');
|
||||
});
|
||||
|
||||
it('extracts token without crashing when value has malformed percent-encoding', () => {
|
||||
document['cookie'] = 'XSRF-TOKEN=%ZZ';
|
||||
expect(extractor.getToken()).toEqual('%ZZ');
|
||||
});
|
||||
});
|
||||
|
||||
function getParseCount(extractor: HttpXsrfCookieExtractor): number {
|
||||
|
||||
@@ -8,13 +8,29 @@
|
||||
|
||||
export function parseCookieValue(cookieStr: string, name: string): string | null {
|
||||
name = encodeURIComponent(name);
|
||||
|
||||
for (const cookie of cookieStr.split(';')) {
|
||||
const eqIndex = cookie.indexOf('=');
|
||||
const [cookieName, cookieValue]: string[] =
|
||||
eqIndex == -1 ? [cookie, ''] : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)];
|
||||
if (cookieName.trim() === name) {
|
||||
return decodeURIComponent(cookieValue);
|
||||
|
||||
if (cookieName.trim() !== name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let value = cookieValue;
|
||||
try {
|
||||
value = decodeURIComponent(cookieValue);
|
||||
} catch {
|
||||
// Fall back to raw cookie value if decoding fails (e.g. malformed percent-encoding).
|
||||
}
|
||||
|
||||
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
|
||||
value = value.slice(1, -1);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -21,13 +21,30 @@ describe('cookies', () => {
|
||||
const cookie = 'other-cookie=false; xsrf-token=token-value; is_awesome=true; ffo=true;';
|
||||
expect(parseCookieValue(cookie, 'xsrf-token')).toBe('token-value');
|
||||
});
|
||||
|
||||
it('handles encoded keys', () => {
|
||||
expect(parseCookieValue('whitespace%20token=token-value', 'whitespace token')).toBe(
|
||||
'token-value',
|
||||
);
|
||||
});
|
||||
|
||||
it('handles encoded values', () => {
|
||||
expect(parseCookieValue('token=whitespace%20', 'token')).toBe('whitespace ');
|
||||
expect(parseCookieValue('token=whitespace%0A', 'token')).toBe('whitespace\n');
|
||||
});
|
||||
|
||||
it('strips DQUOTE characters per RFC 6265 Section 4.1.1', () => {
|
||||
expect(parseCookieValue('token="abc123"', 'token')).toBe('abc123');
|
||||
expect(parseCookieValue('token=%22abc123%22', 'token')).toBe('abc123');
|
||||
expect(parseCookieValue('token="abc=def"', 'token')).toBe('abc=def');
|
||||
expect(parseCookieValue('token="abc def"', 'token')).toBe('abc def');
|
||||
expect(parseCookieValue('token=""', 'token')).toBe('');
|
||||
expect(parseCookieValue('token="abc"', 'token')).toBe('abc');
|
||||
expect(parseCookieValue('token="', 'token')).toBe('"');
|
||||
});
|
||||
|
||||
it('handles malformed percent-encoding without throwing URIError', () => {
|
||||
expect(parseCookieValue('token=%ZZ', 'token')).toBe('%ZZ');
|
||||
expect(parseCookieValue('token="abc%ZZ"', 'token')).toBe('abc%ZZ');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user