Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 2 additions & 14 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,17 +105,5 @@ jobs:
working-directory: example
- run: npm audit signatures
working-directory: example
- name: Smoke test example handler
working-directory: example
run: |
node <<'NODE'
const assert = require('node:assert/strict')
const { handler } = require('./index')

handler({}, {})
.then((result) => assert.equal(result, 'all good!'))
.catch((error) => {
console.error(error)
process.exitCode = 1
})
NODE
- name: Test example error reporting against a local endpoint
run: npm run test:example
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

- Updated vulnerable transitive development dependencies.
- Preserved existing full Lambda context capture and added regression tests for identity, client context, application-added fields, and overlapping invocations.
- Documented context capture boundaries and redaction with the Raygun client's `onBeforeSend` hook.

## 0.0.9

- Maintenance-only release; no public API or runtime behavior changes.
Expand Down
13 changes: 13 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ The recommended IDE for working on this project is Visual Studio Code.

To run tests, run `npm run test` or run all tests from VSCode.

To test the actual example's success and error paths against a local mock
Raygun endpoint, run these commands from the repository root:

```sh
npm run prepare
npm --prefix example ci --ignore-scripts
npm run test:example
```

The example test uses a test API key and does not contact AWS or Raygun.
The main test suite also checks overlapping async and callback invocations
for breadcrumb and context isolation.

### Code analysis

To check the code, run `npm run eslint` and `npm run tseslint`.
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@ exports.handler = awsHandler({ client }, async function (event, context) {

### AWS Lambda function context in Custom Data

The `awsHandler` also adds automatically the function call context in the "Custom Data" payload of the error report.
The `awsHandler` adds the full function call context to the error report's
"Custom Data" and the automatic "Running AWS Function" breadcrumb. The example
below shows common metadata fields, not an exhaustive list. Serializable
`identity`, `clientContext`, and application-added fields are also included when
present. Context changes made by your handler are reflected in the report.

This payload can be found in the "Custom" tab in the Raygun Crash Reporting error report page.

Expand All @@ -124,6 +128,31 @@ context: {
}
```

### Protecting sensitive data

The wrapper does not automatically capture the event, but it does capture the
full context. Review context fields such as `identity`, `clientContext`, and
application-added data for credentials or personal information. Context also
contains infrastructure identifiers, including the function ARN and log names.
Avoid adding sensitive data to custom data, breadcrumbs, or error messages.

The Raygun client's `filters` option applies only to request headers, query
parameters, and parsed request bodies. It does not redact Lambda context,
custom data, breadcrumbs, or error messages. Use the client's `onBeforeSend` hook
to remove or redact these sections before transport. For example, to omit all
custom data and breadcrumbs:

```js
client.onBeforeSend((payload) => {
delete payload.details.userCustomData;
delete payload.details.breadcrumbs;
return payload;
});
```

See the Raygun client's [sensitive-data guidance](https://github.com/MindscapeHQ/raygun4node#protecting-sensitive-data)
for request filtering and logging precautions.

## Release History

[View the changelog here](CHANGELOG.md)
Expand Down
40 changes: 22 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"//prepare": "prepare project for distribution",
"prepare": "tsc",
"//test": "runs tests on main project",
"test": "tap --disable-coverage test/*_test.js"
"test": "tap --disable-coverage test/*_test.js",
"test:example": "tap --disable-coverage test/example_integration.js"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
Expand Down
71 changes: 71 additions & 0 deletions test/example_integration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const test = require("tap").test;
const { Client } = require("raygun");
const { makeClientWithMockServer } = require("./utils");

test("actual Lambda sample reports its error to a local endpoint", async (t) => {
const environment = await makeClientWithMockServer();
t.teardown(() => environment.stop());
const init = Client.prototype.init;
let sampleClient;
Client.prototype.init = function (options) {
sampleClient = this;
return init.call(this, {
...options,
apiKey: "TEST_API_KEY",
host: "localhost",
port: environment.address.port,
useSSL: false,
});
};
let handler;
try {
({ handler } = require("../example/index"));
} finally {
Client.prototype.init = init;
}
t.teardown(() => sampleClient.stop());

t.equal(await handler({}, { functionName: "sample-success" }), "all good!");
t.equal(environment.server.entries.length, 0);
const nextRequest = environment.nextRequest();
await t.rejects(
handler(
{ error: true, token: "event-only-sentinel" },
{
functionName: "sample-failure",
awsRequestId: "sample-request",
identity: { cognitoIdentityId: "sample-identity" },
clientContext: { custom: { client: "sample-client" } },
applicationField: "sample-application-field",
},
),
/It's an AWS error!/,
);
const message = await nextRequest;
t.equal(environment.server.entries.length, 1);
t.equal(message.details.error.message, "It's an AWS error!");
t.same(message.details.tags, ["AWS Handler"]);
t.same(message.details.userCustomData.context, {
functionName: "sample-failure",
awsRequestId: "sample-request",
identity: { cognitoIdentityId: "sample-identity" },
clientContext: { custom: { client: "sample-client" } },
applicationField: "sample-application-field",
});
t.same(message.details.breadcrumbs[0].customData, {
functionName: "sample-failure",
awsRequestId: "sample-request",
identity: { cognitoIdentityId: "sample-identity" },
clientContext: { custom: { client: "sample-client" } },
applicationField: "sample-application-field",
});
t.same(
message.details.breadcrumbs.map((crumb) => crumb.message),
[
"Running AWS Function: sample-failure",
"breadcrumb on event received",
"event has error data!",
],
);
t.notMatch(JSON.stringify(message), "event-only-sentinel");
});
Loading
Loading