Skip to content

Commit f603d47

Browse files
thePunderWomankirjs
authored andcommitted
fix(core): escape forward slashes in transfer state to prevent crawler indexing
This commit escapes forward slashes in the transfer state JSON output as \u002F to prevent search engine crawlers from aggressively indexing relative paths inside the inline script tag. It also updates related unit and integration tests across core and platform-server. Fixes #65310 (cherry picked from commit 3c76411)
1 parent b72b6b4 commit f603d47

File tree

3 files changed

+24
-5
lines changed

3 files changed

+24
-5
lines changed

packages/core/src/transfer_state.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,8 @@ export class TransferState {
142142

143143
// Escape script tag to avoid break out of <script> tag in serialized output.
144144
// Encoding of `<` is the same behaviour as G3 script_builders.
145-
return JSON.stringify(this.store).replace(/</g, '\\u003C');
145+
// Encoding of `/` prevents crawlers from incorrectly indexing relative URLs in inline JSON.
146+
return JSON.stringify(this.store).replace(/</g, '\\u003C').replace(/\//g, '\\u002F');
146147
}
147148
}
148149

packages/core/test/transfer_state_spec.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,18 @@ describe('TransferState', () => {
136136

137137
transferState.set(DELAYED_KEY, '</script><script>alert(\'Hello&\' + "World");');
138138
expect(transferState.toJson()).toBe(
139-
`{"delayed":"\\u003C/script>\\u003Cscript>alert('Hello&' + \\"World\\");"}`,
139+
`{"delayed":"\\u003C\\u002Fscript>\\u003Cscript>alert('Hello&' + \\"World\\");"}`,
140140
);
141141
});
142142

143-
it('should decode `\\u003C` (<) when restoring stating', () => {
144-
const encodedState = `{"delayed":"\\u003C/script>\\u003Cscript>alert('Hello&' + \\"World\\");"}`;
143+
it('should encode `/` to avoid crawler indexing of inline JSON', () => {
144+
const transferState = TestBed.inject(TransferState);
145+
transferState.set(DELAYED_KEY, '/foo/bar');
146+
expect(transferState.toJson()).toBe(`{"delayed":"\\u002Ffoo\\u002Fbar"}`);
147+
});
148+
149+
it('should decode `\\u003C` (<) and `\\u002F` (/) when restoring stating', () => {
150+
const encodedState = `{"delayed":"\\u003C\\u002Fscript>\\u003Cscript>alert('Hello&' + \\"World\\");"}`;
145151
addScriptTag(doc, APP_ID, encodedState);
146152
const transferState = TestBed.inject(TransferState);
147153

@@ -150,4 +156,16 @@ describe('TransferState', () => {
150156
'</script><script>alert(\'Hello&\' + "World");',
151157
);
152158
});
159+
160+
it('should properly encode and decode relative links in JSON', () => {
161+
const relativeLink = '/about/us?query=1';
162+
const encodedState = `{"delayed":"\\u002Fabout\\u002Fus?query=1"}`;
163+
164+
// Ensure restoring from the encoded state correctly decodes the relative link
165+
addScriptTag(doc, APP_ID, encodedState);
166+
const transferState = TestBed.inject(TransferState);
167+
168+
expect(transferState.get(DELAYED_KEY, null)).toBe(relativeLink);
169+
expect(transferState.toJson()).toBe(encodedState);
170+
});
153171
});

packages/platform-server/test/transfer_state_spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ describe('transfer_state', () => {
7777
expect(output).toBe(
7878
'<html><head></head><body><esc-app ng-version="0.0.0-PLACEHOLDER" ng-server-context="other">Works!</esc-app>' +
7979
'<script id="ng-state" type="application/json">' +
80-
`{"testString":"\\u003C/script>\\u003Cscript>alert('Hello&' + \\"World\\");"}` +
80+
`{"testString":"\\u003C\\u002Fscript>\\u003Cscript>alert('Hello&' + \\"World\\");"}` +
8181
'</script></body></html>',
8282
);
8383
});

0 commit comments

Comments
 (0)