This repository has been archived by the owner on Jun 14, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implements slack token replacement as a hook
- Loading branch information
Showing
10 changed files
with
126 additions
and
14 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,90 @@ | ||
export const hookType = 'serializerRawRequestBody'; | ||
import { URLSearchParams, parse as urlParse, format as urlFormat } from 'url'; | ||
import { RequestInfo, SerializerRawRequest } from '../steno'; | ||
import { PrintFn } from '../util'; | ||
import nonce from 'nonce-str'; // tslint:disable-line import-name | ||
|
||
export function processor(): void { | ||
return; | ||
const authorizationHeaderPattern = /^Bearer (.*)$/; | ||
|
||
function createPlaceholderToken(length: number): string { | ||
return `xoxf-${nonce(length - 5)}`; | ||
} | ||
|
||
export function createHook(print: PrintFn): SerializerRawRequest { | ||
print('Starting with Slack token replacement enabled. Each time a token is encountered in an ' + | ||
'interaction, it will be replaced with a placeholder. Make these substitutions in test ' + | ||
'code. This way, when your test code runs against steno in replay mode, the interactions ' + | ||
'will continue to match.\n\n\nWARNING: In this mode, sensitive data is logged to stdout. ' + | ||
'Do not store these logs without access control.\n\n'); | ||
|
||
const tokenReplacements: Map<string, string> = new Map(); | ||
|
||
function replaceToken(token: string): string { | ||
const replacement = tokenReplacements.get(token) || createPlaceholderToken(token.length); | ||
tokenReplacements.set(token, replacement); | ||
print(`Slack token replaced: TOKEN=${token} REPLACEMENT=${replacement}`); | ||
return replacement; | ||
} | ||
|
||
return { | ||
hookType: 'serializerRawRequest', | ||
processor: (request: RequestInfo): RequestInfo => { | ||
// TODO: RequestInfo is useful as an internal representation, we do not want to export this type | ||
|
||
// Look for tokens in all the places we might see one in the Slack API | ||
|
||
// 1. JSON write requests to the Web API include tokens in the Authorization header | ||
let authorization; | ||
let match = null; | ||
if ((authorization = request.headers['authorization'] as string) !== undefined && | ||
(match = authorizationHeaderPattern.exec(authorization)) !== null) { | ||
const token = match[1]; | ||
if (token !== undefined) { | ||
const replacement = replaceToken(token); | ||
request.headers['authorization'] = (request.headers['authorization'] as string).replace(token, replacement); | ||
} | ||
} | ||
|
||
// 2. Some requests to the Web API include tokens in the URL-encoded body | ||
if (request.headers['content-type'] === 'application/x-www-form-urlencoded' && request.body !== undefined) { | ||
const bodyParams = new URLSearchParams(`?${request.body.toString()}`); | ||
const token = bodyParams.get('token'); | ||
if (token !== null) { | ||
const replacement = replaceToken(token); | ||
bodyParams.set('token', replacement); | ||
// NOTE: URLSearchParams has opinions about how to encode itself as a string, that may | ||
// differ from how the original request was encoded. The main example is spaces, which | ||
// can be encoded as "%20", but URLSearchParams chooses to encode them as "+". Both are | ||
// technically correct. | ||
request.body = Buffer.from(bodyParams.toString()); | ||
} | ||
} | ||
|
||
// 3. Other requests to the Web API include tokens in the URL-encoded query parameter | ||
const parsedUrl = urlParse(request.url); | ||
if (parsedUrl.search !== undefined) { | ||
const queryParams = new URLSearchParams(parsedUrl.search); | ||
const token = queryParams.get('token'); | ||
if (token !== null) { | ||
const replacement = replaceToken(token); | ||
queryParams.set('token', replacement); | ||
delete parsedUrl.query; | ||
// NOTE: URLSearchParams has opinions about how to encode itself as a string, that may | ||
// differ from how the original request was encoded. The main example is spaces, which | ||
// can be encoded as "%20", but URLSearchParams chooses to encode them as "+". Both are | ||
// technically correct. | ||
parsedUrl.search = queryParams.toString(); | ||
request.url = urlFormat(parsedUrl); | ||
} | ||
} | ||
|
||
// incoming requests from slash commands, events api, and interactive messages have a | ||
// verification token in them, not a client authentication token | ||
|
||
// outgoing requests from incoming webhooks and response_urls (interactive message or slash | ||
// command) don't have client authentication tokens in them, although the URL itself could | ||
// be considered sentitive data | ||
|
||
return request; | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
|
||
declare module 'nonce-str'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters