-
Notifications
You must be signed in to change notification settings - Fork 36
Optionally capture Lambda request and response payloads #222
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
Changes from 4 commits
8a9ddfc
4ee3a1c
a9180a5
5e7655c
d02b3ca
35f5e05
45574c0
cc0f435
52946a4
9ef6d8e
9a71ed7
25b7c65
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 |
---|---|---|
@@ -1,5 +1,6 @@ | ||
{ | ||
"printWidth": 120, | ||
"trailingComma": "all", | ||
"arrowParens": "always" | ||
"arrowParens": "always", | ||
"semi": true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { tagObject } from "./tagObject"; | ||
|
||
describe("tagObject", () => { | ||
const setTag = jest.fn(); | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
it("tags something simple", () => { | ||
const span = { | ||
setTag, | ||
}; | ||
tagObject(span, "lambda_payload", { request: { myKey: "myValue" } }); | ||
expect(setTag).toBeCalledWith("lambda_payload.request.myKey", "myValue"); | ||
}); | ||
it("tags complex objects", () => { | ||
const span = { | ||
setTag, | ||
}; | ||
tagObject(span, "lambda_payload", { | ||
request: { | ||
keyOne: "foobar", | ||
myObject: { | ||
anotherKey: ["array", "of", "values"], | ||
}, | ||
val: null, | ||
number: 1, | ||
}, | ||
}); | ||
expect(setTag.mock.calls).toEqual([ | ||
["lambda_payload.request.keyOne", "foobar"], | ||
["lambda_payload.request.myObject.anotherKey.0", "array"], | ||
["lambda_payload.request.myObject.anotherKey.1", "of"], | ||
["lambda_payload.request.myObject.anotherKey.2", "values"], | ||
["lambda_payload.request.val", null], | ||
["lambda_payload.request.number", 1], | ||
]); | ||
}); | ||
it("tags arrays of objects", () => { | ||
const span = { | ||
setTag, | ||
}; | ||
tagObject(span, "lambda_payload", { | ||
request: { | ||
vals: [{ thingOne: 1 }, { thingTwo: 2 }], | ||
}, | ||
}); | ||
expect(setTag.mock.calls).toEqual([ | ||
["lambda_payload.request.vals.0.thingOne", 1], | ||
["lambda_payload.request.vals.1.thingTwo", 2], | ||
]); | ||
}); | ||
it("redacts common secret keys", () => { | ||
const span = { | ||
setTag, | ||
}; | ||
tagObject(span, "lambda_payload", { request: { headers: { authorization: "myValue" } } }); | ||
expect(setTag).toBeCalledWith("lambda_payload.request.headers.authorization", "redacted"); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
const redactableKeys = ["authorization", "x-authorization", "password", "token"]; | ||
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. Maybe this could be customizable by the customer? 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. Oh good question - we actually already allow the customer to define obfuscation rules via This is just a hardcoded list to prevent non-configured payload capture from indexing basic/common secrets. It's not something we want to automatically index. If a user wants to capture these, they'll have to manually set a tag. 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. oh I see, so maybe we should make a link/note about this config option when we'll release this new env var? 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. Yup! And it'll be in the blog post :) |
||
|
||
export function tagObject(currentSpan: any, key: string, obj: any): any { | ||
if (obj === null) { | ||
return currentSpan.setTag(key, obj); | ||
} | ||
if (typeof obj === "string") { | ||
let parsed: string; | ||
try { | ||
parsed = JSON.parse(obj); | ||
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. If it's a string, does it need to be parsed? 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. Good question. This is needed to deeply tag nested JSON into separate items, otherwise it just tags an object (ala: |
||
} catch (e) { | ||
const redacted = redactVal(key, obj.substring(0, 5000)); | ||
return currentSpan.setTag(key, redacted); | ||
} | ||
return tagObject(currentSpan, key, parsed); | ||
} | ||
if (typeof obj === "number") { | ||
return currentSpan.setTag(key, obj); | ||
} | ||
if (typeof obj === "object") { | ||
for (const [k, v] of Object.entries(obj)) { | ||
tagObject(currentSpan, `${key}.${k}`, v); | ||
} | ||
return; | ||
} | ||
} | ||
|
||
function redactVal(k: string, v: string): string { | ||
const splitKey = k.split(".").pop() || k; | ||
if (redactableKeys.includes(splitKey)) { | ||
return "redacted"; | ||
} | ||
return v; | ||
} |
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.
nit: we typically name files like this
tag-object.ts