Skip to content

Commit

Permalink
v0.4.0
Browse files Browse the repository at this point in the history
Signed-off-by: Collin McNeese <collinmcneese@github.com>
  • Loading branch information
collinmcneese committed Jan 9, 2023
1 parent 545b387 commit c761746
Show file tree
Hide file tree
Showing 13 changed files with 814 additions and 198 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
dist/
__test__/
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
branches:
- main
paths-ignore:
- '**.md'
- '.github/workflows/codeql.yml'
- '**.md'
- '.github/workflows/codeql.yml'
push:
paths-ignore:
- '**.md'
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ on:
push:
branches: [ "main" ]
paths-ignore:
- '**.md'
- '**.md'
pull_request:
# The branches below must be a subset of the branches above
branches: [ "main" ]
paths-ignore:
- '**.md'
- '**.md'
schedule:
- cron: '32 20 * * 4'

Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ The contents of this repository are individually maintained and are not a direct

- `target-url`: **String**, **Required**, The target URL destination where webhook event payloads will be reflected to.
- `webhook-secret`: **String**, **Optional**, Secret data value to use for webhook payload. Populates `X-Hub-Signature` and `X-Hub-Signature-256` header values. See [Securing Your Webhooks](https://docs.github.com/en/developers/webhooks-and-events/webhooks/securing-your-webhooks) for additional context.
- `allow-list-source`: **String**, **Optional**, Source location for a newline-delimited list of entries which specify the allow-list for `target-url` filtering. Example `allow-list` file contents:

```plain
# example-allow-list.txt
# Comment line and blank lines are ignored
# Entries in this file should be prefixed with transport type (http/https)
https://github.com
https://api.github.com
https://*.github.localdomain
```
### Example
Expand Down Expand Up @@ -55,6 +66,6 @@ Example overview of an implementation which uses reverse proxy or an API gateway

- [ ] Complete documentation
- [X] Add logic for handling secrets to downstream webhook targets
- [ ] Add code testing
- [X] Add code testing
- [ ] Add quick setup example references for easy testing
- [ ] Add URL filtering capability to handle controlling valid URL targets via configuration (allow-list).
9 changes: 9 additions & 0 deletions __test__/allowlist.mock
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This is a comment line which is ignored
# Blank lines are also ignored

# Lines in this file should be prefixed with transport type (http/https)

https://github.com
https://api.github.com
https://*.github.localdomain
https://smee.io
88 changes: 88 additions & 0 deletions __test__/reflector.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Tests for functions in reflector.js

const reflector = require('../src/reflector');
const { validateUrl, fetchAllowListSource, validateAllowList, getWebhookSignature, getRequestOptions } = reflector.reflectorPrivate;

let allowListObject = ['https://github.com', 'https://api.github.com', 'https://*.github.localdomain', 'https://smee.io'];

describe('validateUrl', () => {
test('validateUrl() returns true for valid URL', () => {
expect(validateUrl('https://github.com')).toBe(true);
});

test('validateUrl() throws error for invalid URL', () => {
expect(() => {
validateUrl('github.com');
}).toThrow();
});
});

describe('fetchAllowListSource', () => {
let allowListSource = './__test__/allowlist.mock';

test('fetchAllowListSource() returns an object and that members contain mock values', async () => {
let allowList = await fetchAllowListSource(allowListSource);

expect(typeof allowList).toBe('object');

expect(allowList.sort()).toEqual(allowListObject.sort());
});
});

describe('validateAllowList', () => {
let allowList = allowListObject;

test('validateAllowList() returns true for valid URL', () => {
expect(validateAllowList('https://api.github.com', allowList)).toBe(true);
});

test('validateAllowList() returns true for valid URL with wildcard', () => {
expect(validateAllowList('https://api.github.localdomain', allowList)).toBe(true);
});

test('validateAllowList() returns false for invalid URL', () => {
expect(validateAllowList('https://invalid.url', allowList)).toBe(false);
});
});

describe('getWebhookSignature', () => {

test('getWebhookSignature() returns a sha1 string with the proper value', () => {
let payload = 'payload';
let secret = 'abc123';
let algorithm = 'sha1';
let expected = 'sha1=bf995fbe34d0a428d0cf1d7d45c8990ccefc9250';
expect(getWebhookSignature(payload, secret, algorithm)).toBe(expected);
});

test('getWebhookSignature() returns a sha256 string with the proper value', () => {
let payload = 'payload';
let secret = 'abc123';
let algorithm = 'sha256';
let expected = 'sha256=ba245390d5b4bf305fbef57917c6919d580db46f6989347b7a1f03c4fced02c1';
expect(getWebhookSignature(payload, secret, algorithm)).toBe(expected);
});
});

describe('getRequestOptions', () => {
let context = {
eventName: 'push',
payload: {
test: 'test'
}
};
let targetUrl = 'https://github.com';
let webhookSecret = 'abc123';

test('getRequestOptions() returns an object with the proper values', () => {
let options = getRequestOptions(context, targetUrl, webhookSecret);
expect(typeof options).toBe('object');
expect(options.url).toBe(targetUrl);
expect(options.method).toBe('POST');
expect(options.headers['X-GitHub-Event']).toBe(context.eventName);
expect(options.headers['X-Hub-Signature']).toBe('sha1=2aa4571fded2cb5bc29e911b177f5f0d6e0775fa');
expect(options.headers['X-Hub-Signature-256']).toBe('sha256=34187ae3db37f4e3b61b8b87849737a400be580cd7b05ee81afd5feeb9c3a758');
expect(options.headers['Content-Type']).toBe('application/json');
expect(options.body).toBe(JSON.stringify(context.payload, undefined, 2));
});
});
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ inputs:
webhook-secret:
description: 'The secret to use for signing the event payload.'
required: false
allow-list-source:
description: 'Location to an allow list of target URL entries.'
required: false
runs:
using: 'node16'
main: 'dist/index.js'
Loading

0 comments on commit c761746

Please sign in to comment.