-
Notifications
You must be signed in to change notification settings - Fork 662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
socket-mode(fix): redact ephemeral tokens and secrets from debug logs #1832
base: main
Are you sure you want to change the base?
Changes from all commits
d097cd6
b4718d8
e7a1166
3147e08
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,6 +30,21 @@ | |
Authenticated = 'authenticated', | ||
} | ||
|
||
/** | ||
* Recursive definition for what a value might contain. | ||
*/ | ||
interface Nesting { | ||
[key: string]: NestedRecord | unknown; | ||
} | ||
|
||
/** | ||
* Recursive definiton for possible JSON object values. | ||
* | ||
* FIXME: Prefer using a circular reference if allowed: | ||
* Record<string, NestedRecord> | NestedRecord[] | ||
*/ | ||
type NestedRecord = Nesting | NestedRecord[]; | ||
|
||
/** | ||
* A Socket Mode Client allows programs to communicate with the | ||
* [Slack Platform's Events API](https://api.slack.com/events-api) over WebSocket connections. | ||
|
@@ -284,8 +299,6 @@ | |
this.logger.debug('Unexpected binary message received, ignoring.'); | ||
return; | ||
} | ||
const payload = data.toString(); | ||
this.logger.debug(`Received a message on the WebSocket: ${payload}`); | ||
|
||
// Parse message into slack event | ||
let event: { | ||
|
@@ -299,10 +312,13 @@ | |
accepts_response_payload?: boolean; // type: events_api, slash_commands, interactive | ||
}; | ||
|
||
const payload = data?.toString(); | ||
try { | ||
event = JSON.parse(payload); | ||
this.logger.debug(`Received a message on the WebSocket: ${JSON.stringify(SocketModeClient.redact(event))}`); | ||
} catch (parseError) { | ||
// Prevent application from crashing on a bad message, but log an error to bring attention | ||
this.logger.debug(`Received a message on the WebSocket: ${payload}`); | ||
this.logger.debug( | ||
`Unable to parse an incoming WebSocket message (will ignore): ${parseError}, ${payload}`, | ||
); | ||
|
@@ -325,7 +341,7 @@ | |
// Define Ack, a helper method for acknowledging events incoming from Slack | ||
const ack = async (response: Record<string, unknown>): Promise<void> => { | ||
if (this.logger.getLevel() === LogLevel.DEBUG) { | ||
this.logger.debug(`Calling ack() - type: ${event.type}, envelope_id: ${event.envelope_id}, data: ${JSON.stringify(response)}`); | ||
this.logger.debug(`Calling ack() - type: ${event.type}, envelope_id: ${event.envelope_id}, data: ${JSON.stringify(SocketModeClient.redact(response))}`); | ||
} | ||
await this.send(event.envelope_id, response); | ||
}; | ||
|
@@ -386,9 +402,8 @@ | |
} else { | ||
this.emit('outgoing_message', message); | ||
|
||
const flatMessage = JSON.stringify(message); | ||
this.logger.debug(`Sending a WebSocket message: ${flatMessage}`); | ||
this.websocket.send(flatMessage, (error) => { | ||
this.logger.debug(`Sending a WebSocket message: ${JSON.stringify(SocketModeClient.redact(message))}`); | ||
this.websocket.send(JSON.stringify(message), (error) => { | ||
if (error) { | ||
this.logger.error(`Failed to send a WebSocket message (error: ${error})`); | ||
return reject(websocketErrorWithOriginal(error)); | ||
|
@@ -398,6 +413,37 @@ | |
} | ||
}); | ||
} | ||
|
||
/** | ||
* Removes secrets and tokens from socket request and response objects | ||
* before logging. | ||
* @param body - the object with values for redaction. | ||
* @returns the same object with redacted values. | ||
*/ | ||
private static redact(body: NestedRecord): NestedRecord { | ||
if (body === undefined) { | ||
return body; | ||
} | ||
const record = Object.create(body); | ||
if (Array.isArray(body)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question as above: should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not directly, but it's possible that keys somewhere within the body contain an Array, which would be hit in some recursive case. Possibly with details from |
||
return body.map((item) => ( | ||
(typeof item === 'object' && item !== null) ? | ||
SocketModeClient.redact(item) : | ||
item | ||
)); | ||
} | ||
Object.keys(body).forEach((key: string) => { | ||
const value = body[key]; | ||
if (typeof value === 'object' && value !== null) { | ||
record[key] = SocketModeClient.redact(value as NestedRecord); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This cast seems needed even with the recursive definition above... 🤔 I fumbled a bit with it without too much luck.
|
||
} else if (key.match(/.*token.*/) !== null || key.match(/secret/)) { | ||
record[key] = '[[REDACTED]]'; | ||
} else { | ||
record[key] = value; | ||
} | ||
}); | ||
return record; | ||
} | ||
} | ||
|
||
/* Instrumentation */ | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately this was tossing an error in the editor... Some prior art was tried but without much luck...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair enough, don't sweat it! We do some seriously nasty gymnastics in deno-slack-sdk to avoid this; I do not recommend it, though. Roll with the punches and move on!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am oddly impressed with this magic. I wish this was magic
tsc
took care of though and I didn't have to learn the tricks 😉 🪄