🔒 [security fix] Fix sensitive data exposure in logs
- Change core.info to core.debug for model responses in src/inference.ts - Change core.info to core.debug for tool execution details in src/mcp.ts - Change core.info to core.debug for custom header logging in src/helpers.ts - Remove sensitive response previews from error messages in src/inference.ts - Update tests to reflect changes from core.info to core.debug
This commit is contained in:
+13
-13
@@ -150,9 +150,9 @@ X-Custom-Header: custom-value`
|
|||||||
header2: 'value2',
|
header2: 'value2',
|
||||||
'X-Custom-Header': 'custom-value',
|
'X-Custom-Header': 'custom-value',
|
||||||
})
|
})
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: header1: value1')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: header1: value1')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: header2: value2')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: header2: value2')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: X-Custom-Header: custom-value')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: X-Custom-Header: custom-value')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('parses JSON format headers correctly', () => {
|
it('parses JSON format headers correctly', () => {
|
||||||
@@ -165,9 +165,9 @@ X-Custom-Header: custom-value`
|
|||||||
header2: 'value2',
|
header2: 'value2',
|
||||||
'X-Team': 'engineering',
|
'X-Team': 'engineering',
|
||||||
})
|
})
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: header1: value1')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: header1: value1')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: header2: value2')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: header2: value2')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: X-Team: engineering')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: X-Team: engineering')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns empty object for empty input', () => {
|
it('returns empty object for empty input', () => {
|
||||||
@@ -194,13 +194,13 @@ password: pass123`
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Sensitive headers should be masked
|
// Sensitive headers should be masked
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: Ocp-Apim-Subscription-Key: ***MASKED***')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: Ocp-Apim-Subscription-Key: ***MASKED***')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: X-Api-Token: ***MASKED***')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: X-Api-Token: ***MASKED***')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: Authorization: ***MASKED***')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: Authorization: ***MASKED***')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: password: ***MASKED***')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: password: ***MASKED***')
|
||||||
|
|
||||||
// Non-sensitive headers should not be masked
|
// Non-sensitive headers should not be masked
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: serviceName: my-service')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: serviceName: my-service')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('validates header names and skips invalid ones', () => {
|
it('validates header names and skips invalid ones', () => {
|
||||||
@@ -367,8 +367,8 @@ systemID: terraform-ci`
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Only the subscription key should be masked
|
// Only the subscription key should be masked
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: Ocp-Apim-Subscription-Key: ***MASKED***')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: Ocp-Apim-Subscription-Key: ***MASKED***')
|
||||||
expect(core.info).toHaveBeenCalledWith('Custom header added: serviceName: terraform-plan-workflow')
|
expect(core.debug).toHaveBeenCalledWith('Custom header added: serviceName: terraform-plan-workflow')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ describe('inference.ts', () => {
|
|||||||
|
|
||||||
expect(result).toBe('Hello, user!')
|
expect(result).toBe('Hello, user!')
|
||||||
expect(core.info).toHaveBeenCalledWith('Running simple inference without tools')
|
expect(core.info).toHaveBeenCalledWith('Running simple inference without tools')
|
||||||
expect(core.info).toHaveBeenCalledWith('Model response: Hello, user!')
|
expect(core.debug).toHaveBeenCalledWith('Model response: Hello, user!')
|
||||||
|
|
||||||
// Verify the request structure
|
// Verify the request structure
|
||||||
expect(mockCreate).toHaveBeenCalledWith({
|
expect(mockCreate).toHaveBeenCalledWith({
|
||||||
@@ -136,7 +136,7 @@ describe('inference.ts', () => {
|
|||||||
const result = await simpleInference(mockRequest)
|
const result = await simpleInference(mockRequest)
|
||||||
|
|
||||||
expect(result).toBeNull()
|
expect(result).toBeNull()
|
||||||
expect(core.info).toHaveBeenCalledWith('Model response: No response content')
|
expect(core.debug).toHaveBeenCalledWith('Model response: No response content')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('includes response format when specified', async () => {
|
it('includes response format when specified', async () => {
|
||||||
|
|||||||
@@ -177,8 +177,8 @@ describe('mcp.ts', () => {
|
|||||||
name: 'test-tool',
|
name: 'test-tool',
|
||||||
content: JSON.stringify(toolResult.content),
|
content: JSON.stringify(toolResult.content),
|
||||||
})
|
})
|
||||||
expect(core.info).toHaveBeenCalledWith('Executing GitHub MCP tool: test-tool with args: {"param": "value"}')
|
expect(core.debug).toHaveBeenCalledWith('Executing GitHub MCP tool: test-tool with args: {"param": "value"}')
|
||||||
expect(core.info).toHaveBeenCalledWith('GitHub MCP tool test-tool executed successfully')
|
expect(core.debug).toHaveBeenCalledWith('GitHub MCP tool test-tool executed successfully')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('handles tool execution errors gracefully', async () => {
|
it('handles tool execution errors gracefully', async () => {
|
||||||
|
|||||||
+2
-2
@@ -143,9 +143,9 @@ function validateAndMaskHeaders(headers: Record<string, unknown>): Record<string
|
|||||||
const lowerName = name.toLowerCase()
|
const lowerName = name.toLowerCase()
|
||||||
const isSensitive = sensitivePatterns.some(pattern => lowerName.includes(pattern))
|
const isSensitive = sensitivePatterns.some(pattern => lowerName.includes(pattern))
|
||||||
if (isSensitive) {
|
if (isSensitive) {
|
||||||
core.info(`Custom header added: ${name}: ***MASKED***`)
|
core.debug(`Custom header added: ${name}: ***MASKED***`)
|
||||||
} else {
|
} else {
|
||||||
core.info(`Custom header added: ${name}: ${stringValue}`)
|
core.debug(`Custom header added: ${name}: ${stringValue}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-6
@@ -61,7 +61,7 @@ export async function simpleInference(request: InferenceRequest): Promise<string
|
|||||||
|
|
||||||
const response = await chatCompletion(client, chatCompletionRequest, 'simpleInference')
|
const response = await chatCompletion(client, chatCompletionRequest, 'simpleInference')
|
||||||
const modelResponse = response.choices[0]?.message?.content
|
const modelResponse = response.choices[0]?.message?.content
|
||||||
core.info(`Model response: ${modelResponse || 'No response content'}`)
|
core.debug(`Model response: ${modelResponse || 'No response content'}`)
|
||||||
return modelResponse || null
|
return modelResponse || null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ export async function mcpInference(
|
|||||||
const modelResponse = assistantMessage?.content
|
const modelResponse = assistantMessage?.content
|
||||||
const toolCalls = assistantMessage?.tool_calls
|
const toolCalls = assistantMessage?.tool_calls
|
||||||
|
|
||||||
core.info(`Model response: ${modelResponse || 'No response content'}`)
|
core.debug(`Model response: ${modelResponse || 'No response content'}`)
|
||||||
|
|
||||||
messages.push({
|
messages.push({
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
@@ -181,16 +181,14 @@ async function chatCompletion(
|
|||||||
try {
|
try {
|
||||||
response = JSON.parse(response)
|
response = JSON.parse(response)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const preview = response.slice(0, 400)
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`${context}: Chat completion response was a string and not valid JSON (${(e as Error).message}). Preview: ${preview}`,
|
`${context}: Chat completion response was a string and not valid JSON (${(e as Error).message})`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!response || typeof response !== 'object' || !('choices' in response)) {
|
if (!response || typeof response !== 'object' || !('choices' in response)) {
|
||||||
const preview = JSON.stringify(response)?.slice(0, 800)
|
throw new Error(`${context}: Unexpected response shape (no choices)`)
|
||||||
throw new Error(`${context}: Unexpected response shape (no choices). Preview: ${preview}`)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return response as OpenAI.Chat.Completions.ChatCompletion
|
return response as OpenAI.Chat.Completions.ChatCompletion
|
||||||
|
|||||||
+2
-2
@@ -96,7 +96,7 @@ export async function connectToGitHubMCP(token: string, toolsets?: string): Prom
|
|||||||
* Execute a single tool call via GitHub MCP
|
* Execute a single tool call via GitHub MCP
|
||||||
*/
|
*/
|
||||||
export async function executeToolCall(githubMcpClient: Client, toolCall: ToolCall): Promise<ToolResult> {
|
export async function executeToolCall(githubMcpClient: Client, toolCall: ToolCall): Promise<ToolResult> {
|
||||||
core.info(`Executing GitHub MCP tool: ${toolCall.function.name} with args: ${toolCall.function.arguments}`)
|
core.debug(`Executing GitHub MCP tool: ${toolCall.function.name} with args: ${toolCall.function.arguments}`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const args = JSON.parse(toolCall.function.arguments)
|
const args = JSON.parse(toolCall.function.arguments)
|
||||||
@@ -106,7 +106,7 @@ export async function executeToolCall(githubMcpClient: Client, toolCall: ToolCal
|
|||||||
arguments: args,
|
arguments: args,
|
||||||
})
|
})
|
||||||
|
|
||||||
core.info(`GitHub MCP tool ${toolCall.function.name} executed successfully`)
|
core.debug(`GitHub MCP tool ${toolCall.function.name} executed successfully`)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tool_call_id: toolCall.id,
|
tool_call_id: toolCall.id,
|
||||||
|
|||||||
Reference in New Issue
Block a user