security(hooks): Wrap hook-injected context in distinct XML tags (#17237)

Co-authored-by: Yuna Seol <yunaseol@google.com>
This commit is contained in:
Yuna Seol
2026-01-21 21:11:45 -05:00
committed by GitHub
parent 1f8b26efc6
commit bb4967b91c
8 changed files with 136 additions and 12 deletions
+12 -1
View File
@@ -177,13 +177,24 @@ describe('Hook Output Classes', () => {
expect(output.applyToolConfigModifications(target)).toBe(target);
});
it('getAdditionalContext should return additionalContext if present', () => {
it('getAdditionalContext should return additional context if present', () => {
const output = new DefaultHookOutput({
hookSpecificOutput: { additionalContext: 'some context' },
});
expect(output.getAdditionalContext()).toBe('some context');
});
it('getAdditionalContext should sanitize context by escaping <', () => {
const output = new DefaultHookOutput({
hookSpecificOutput: {
additionalContext: 'context with <tag> and </hook_context>',
},
});
expect(output.getAdditionalContext()).toBe(
'context with &lt;tag&gt; and &lt;/hook_context&gt;',
);
});
it('getAdditionalContext should return undefined if additionalContext is not present', () => {
const output = new DefaultHookOutput({
hookSpecificOutput: { other: 'value' },
+7 -2
View File
@@ -213,7 +213,7 @@ export class DefaultHookOutput implements HookOutput {
}
/**
* Get additional context for adding to responses
* Get sanitized additional context for adding to responses.
*/
getAdditionalContext(): string | undefined {
if (
@@ -221,7 +221,12 @@ export class DefaultHookOutput implements HookOutput {
'additionalContext' in this.hookSpecificOutput
) {
const context = this.hookSpecificOutput['additionalContext'];
return typeof context === 'string' ? context : undefined;
if (typeof context !== 'string') {
return undefined;
}
// Sanitize by escaping < and > to prevent tag injection
return context.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
return undefined;
}