Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@unleash/proxy

Package Overview
Dependencies
Maintainers
4
Versions
64
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@unleash/proxy - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

dist/event-service.js

8

CHANGELOG.md
# Changelog
### 0.5.0
- feat: additional possibility to customize header for proxy secrets (#39)
- fix: POST should handle empty toggle names
- fix: pin dependencies
- fix: Build multi-arch Docker container to support arm64 and amd64 (#30)
- docs: fix typo in `docker pull` command (#26)
- docs: Add proxyPort/PORT to the list of options/env vars (#28)
### 0.4.0

@@ -4,0 +12,0 @@ - feat: add compression #23

18

dist/config.js

@@ -49,2 +49,8 @@ "use strict";

}
function loadClientKeys(option) {
return (option.clientKeys ||
resolveStringToArray(process.env.UNLEASH_PROXY_CLIENT_KEYS) ||
option.proxySecrets ||
resolveStringToArray(process.env.UNLEASH_PROXY_SECRETS));
}
function createProxyConfig(option) {

@@ -61,6 +67,5 @@ const unleashUrl = option.unleashUrl || process.env.UNLEASH_URL;

loadCustomStrategies(process.env.UNLEASH_CUSTOM_STRATEGIES_FILE);
const proxySecrets = option.proxySecrets ||
resolveStringToArray(process.env.UNLEASH_PROXY_SECRETS);
if (!proxySecrets) {
throw new TypeError('You must specify the unleashProxySecrets option (UNLEASH_PROXY_SECRETS)');
const clientKeys = loadClientKeys(option);
if (!clientKeys) {
throw new TypeError('You must specify the clientKeys option (UNLEASH_PROXY_CLIENT_KEYS)');
}

@@ -81,3 +86,3 @@ const logLevel = option.logLevel || process.env.LOG_LEVEL;

customStrategies,
proxySecrets,
clientKeys,
proxyBasePath: option.proxyBasePath || process.env.PROXY_BASE_PATH || '',

@@ -95,2 +100,5 @@ refreshInterval: option.refreshInterval ||

tags,
clientKeysHeaderName: option.clientKeysHeaderName ||
process.env.CLIENT_KEY_HEADER_NAME ||
'authorization',
};

@@ -97,0 +105,0 @@ }

@@ -92,3 +92,3 @@ "use strict";

process.env.UNLEASH_API_TOKEN = 'token';
process.env.UNLEASH_PROXY_SECRETS = 's1';
process.env.UNLEASH_PROXY_CLIENT_KEYS = 's1';
process.env.UNLEASH_INSTANCE_ID = 'i1';

@@ -98,4 +98,4 @@ const config = config_1.createProxyConfig({});

expect(config.unleashApiToken).toBe('token');
expect(config.proxySecrets.length).toBe(1);
expect(config.proxySecrets[0]).toBe('s1');
expect(config.clientKeys.length).toBe(1);
expect(config.clientKeys[0]).toBe('s1');
expect(config.unleashInstanceId).toBe('i1');

@@ -105,2 +105,18 @@ // cleanup

delete process.env.UNLEASH_API_TOKEN;
delete process.env.UNLEASH_PROXY_CLIENT_KEYS;
delete process.env.UNLEASH_INSTANCE_ID;
});
test('should allow old "UNLEASH_PROXY_SECRETS" option via env', () => {
process.env.UNLEASH_URL = 'some';
process.env.UNLEASH_API_TOKEN = 'token';
process.env.UNLEASH_PROXY_SECRETS = 's1-token, s2-token';
const config = config_1.createProxyConfig({});
expect(config.unleashUrl).toBe('some');
expect(config.unleashApiToken).toBe('token');
expect(config.clientKeys.length).toBe(2);
expect(config.clientKeys[0]).toBe('s1-token');
expect(config.clientKeys[1]).toBe('s2-token');
// cleanup
delete process.env.UNLEASH_URL;
delete process.env.UNLEASH_API_TOKEN;
delete process.env.UNLEASH_PROXY_SECRETS;

@@ -121,3 +137,3 @@ });

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
customStrategies: [new TestStrat()],

@@ -139,3 +155,3 @@ });

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -156,3 +172,3 @@ expect(config.customStrategies?.length).toBe(1);

namePrefix: 'somePrefix',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -166,3 +182,3 @@ expect(config.namePrefix).toBe('somePrefix');

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -194,3 +210,3 @@ expect(config.namePrefix).toBe('prefixViaEnv');

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -205,3 +221,3 @@ expect(config.tags).toBeUndefined();

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -208,0 +224,0 @@ expect(config.tags).toStrictEqual([

@@ -50,2 +50,84 @@ "use strict";

});
test('Should handle POST with empty body', () => {
const toggles = [
{
name: 'test',
enabled: true,
},
{
name: 'test2',
enabled: true,
},
];
const client = new client_mock_1.default(toggles);
const proxySecrets = ['sdf'];
const app = app_1.createApp({ proxySecrets, unleashUrl, unleashApiToken }, client);
client.emit('ready');
return supertest_1.default(app)
.post('/proxy')
.send({ blah: 'hello' })
.set('Accept', 'application/json')
.set('Authorization', 'sdf')
.expect(200)
.expect('Content-Type', /json/)
.then((response) => {
expect(response.body.toggles).toHaveLength(0);
});
});
test('Should handle POST with toggle names', () => {
const toggles = [
{
name: 'test',
enabled: true,
},
{
name: 'test2',
enabled: true,
},
];
const client = new client_mock_1.default(toggles);
const proxySecrets = ['sdf'];
const app = app_1.createApp({ proxySecrets, unleashUrl, unleashApiToken }, client);
client.emit('ready');
return supertest_1.default(app)
.post('/proxy')
.send({ toggles: ['test'] })
.set('Accept', 'application/json')
.set('Authorization', 'sdf')
.expect(200)
.expect('Content-Type', /json/)
.then((response) => {
expect(response.body.toggles).toHaveLength(1);
expect(response.body.toggles[0].name).toBe('test');
});
});
test('Should return list of toggles with custom proxy client key header', () => {
const toggles = [
{
name: 'test',
enabled: true,
},
{
name: 'test2',
enabled: true,
},
];
const client = new client_mock_1.default(toggles);
const proxySecrets = ['sdf'];
const app = app_1.createApp({
proxySecrets,
unleashUrl,
unleashApiToken,
clientKeysHeaderName: 'NotAuthorized',
}, client);
client.emit('ready');
return supertest_1.default(app)
.get('/proxy')
.set('NotAuthorized', 'sdf')
.expect(200)
.expect('Content-Type', /json/)
.then((response) => {
expect(response.body.toggles.length).toEqual(2);
});
});
test('Should return list of toggles using env with multiple secrets', () => {

@@ -52,0 +134,0 @@ process.env.UNLEASH_PROXY_SECRETS = 'secret1,secret2';

@@ -11,3 +11,4 @@ "use strict";

this.logger = config.logger;
this.proxySecrets = config.proxySecrets;
this.clientKeys = config.clientKeys;
this.clientKeysHeaderName = config.clientKeysHeaderName;
this.client = client;

@@ -32,11 +33,15 @@ if (client.isReady()) {

}
setProxySecrets(proxySecrets) {
this.proxySecrets = proxySecrets;
// kept for backward compatibility
setProxySecrets(clientKeys) {
this.setClientKeys(clientKeys);
}
setClientKeys(clientKeys) {
this.clientKeys = clientKeys;
}
getEnabledToggles(req, res) {
const apiToken = req.header('authorization');
const apiToken = req.header(this.clientKeysHeaderName);
if (!this.ready) {
res.status(503).send(NOT_READY);
}
else if (!apiToken || !this.proxySecrets.includes(apiToken)) {
else if (!apiToken || !this.clientKeys.includes(apiToken)) {
res.sendStatus(401);

@@ -54,13 +59,13 @@ }

lookupToggles(req, res) {
const apiToken = req.header('authorization');
const apiToken = req.header(this.clientKeysHeaderName);
if (!this.ready) {
res.status(503).send(NOT_READY);
}
else if (!apiToken || !this.proxySecrets.includes(apiToken)) {
else if (!apiToken || !this.clientKeys.includes(apiToken)) {
res.sendStatus(401);
}
else {
const { context, toggles: toggleNames } = req.body;
const { context, toggles: toggleNames = [] } = req.body;
const toggles = this.client.getDefinedToggles(toggleNames, context);
res.send(toggles);
res.send({ toggles });
}

@@ -67,0 +72,0 @@ }

@@ -8,3 +8,3 @@ const port = process.env.PORT || 3000;

unleashApiToken: '56907a2fa53c1d16101d509a10b78e36190b0f918d9f122d',
proxySecrets: ['proxy-secret', 'another-proxy-secret', 's1'],
clientKeys: ['proxy-secret', 'another-proxy-secret', 's1'],
refreshInterval: 1000,

@@ -18,3 +18,3 @@ // unleashInstanceId: '1337',

app.listen(port, () =>
console.log(`Unleash-proxy is listening on port ${port}!`),
console.log(`Unleash-proxy is listening on port ${port}!`),
);
{
"name": "@unleash/proxy",
"version": "0.4.0",
"version": "0.5.0",
"description": "The Unleash Proxy (Open-Source)",

@@ -5,0 +5,0 @@ "main": "dist/index.js",

@@ -33,3 +33,3 @@ ![Build & Tests](https://github.com/Unleash/unleash-proxy/workflows/Node.js%20CI/badge.svg?branch=main)

```bash
docker pull docker pull unleashorg/unleash-proxy
docker pull unleashorg/unleash-proxy
```

@@ -111,3 +111,5 @@

| unleashApiToken | `UNLEASH_API_TOKEN` | n/a | yes | API token (client) needed to connect to Unleash API. |
| proxySecrets | `UNLEASH_PROXY_SECRETS` | n/a | yes | List of proxy secrets the proxy accept. Proxy SDKs needs to set the Proxy secret as the `Authorization` heder when querying the proxy |
| clientKeys | `UNLEASH_CLIENT_KEYS` | n/a | yes | List of client keys that the proxy should accept. When querying the proxy, Proxy SDKs must set the request's _client keys header_ to one of these values. The default client keys header is `Authorization`. |
| proxySecrets | `UNLEASH_PROXY_SECRETS` | n/a | no | Deprecated alias for `clientKeys`. Please use `clientKeys` instead. |
| proxyPort | `PORT` | 3000 | no | The port where the proxy should listen. |
| proxyBasePath | `PROXY_BASE_PATH` |"/proxy" | no | The base path to run the proxy from. Defaults to "/proxy" |

@@ -126,2 +128,3 @@ | unleashAppName | `UNLEASH_APP_NAME` |"unleash-proxy"| no | App name to used when registering with Unleash |

| tags | `UNLEASH_TAGS` | undefined | no | Used to filter features by using tags set for features. Format should be `tagName:tagValue,tagName2:tagValue2` |
| clientKeysHeaderName | `CLIENT_KEYS_HEADER_NAME` | "authorization" | no | The name of the HTTP header to use for client keys. Incoming requests must set the value of this header to one of the Proxy's `clientKeys` to be authorized successfully. |

@@ -148,3 +151,3 @@ ### Run with Node.js:

unleashApiToken: '56907a2fa53c1d16101d509a10b78e36190b0f918d9f122d',
proxySecrets: ['proxy-secret', 'another-proxy-secret', 's1'],
clientKeys: ['proxy-secret', 'another-proxy-secret', 's1'],
refreshInterval: 1000,

@@ -151,0 +154,0 @@ // logLevel: 'info',

@@ -12,2 +12,3 @@ import { Strategy, TagFilter } from 'unleash-client';

proxySecrets?: string[];
clientKeys?: string[];
proxyPort?: number;

@@ -24,2 +25,3 @@ proxyBasePath?: string;

tags?: Array<TagFilter>;
clientKeysHeaderName?: string;
}

@@ -33,3 +35,3 @@

customStrategies?: Strategy[];
proxySecrets: string[];
clientKeys: string[];
proxyBasePath: string;

@@ -45,2 +47,3 @@ refreshInterval: number;

tags?: Array<TagFilter>;
clientKeysHeaderName: string;
}

@@ -94,2 +97,11 @@

function loadClientKeys(option: IProxyOption): string[] | undefined {
return (
option.clientKeys ||
resolveStringToArray(process.env.UNLEASH_PROXY_CLIENT_KEYS) ||
option.proxySecrets ||
resolveStringToArray(process.env.UNLEASH_PROXY_SECRETS)
);
}
export function createProxyConfig(option: IProxyOption): IProxyConfig {

@@ -115,8 +127,6 @@ const unleashUrl = option.unleashUrl || process.env.UNLEASH_URL;

const proxySecrets =
option.proxySecrets ||
resolveStringToArray(process.env.UNLEASH_PROXY_SECRETS);
if (!proxySecrets) {
const clientKeys = loadClientKeys(option);
if (!clientKeys) {
throw new TypeError(
'You must specify the unleashProxySecrets option (UNLEASH_PROXY_SECRETS)',
'You must specify the clientKeys option (UNLEASH_PROXY_CLIENT_KEYS)',
);

@@ -146,3 +156,3 @@ }

customStrategies,
proxySecrets,
clientKeys,
proxyBasePath:

@@ -163,3 +173,7 @@ option.proxyBasePath || process.env.PROXY_BASE_PATH || '',

tags,
clientKeysHeaderName:
option.clientKeysHeaderName ||
process.env.CLIENT_KEY_HEADER_NAME ||
'authorization',
};
}

@@ -88,3 +88,3 @@ import * as path from 'path';

process.env.UNLEASH_API_TOKEN = 'token';
process.env.UNLEASH_PROXY_SECRETS = 's1';
process.env.UNLEASH_PROXY_CLIENT_KEYS = 's1';
process.env.UNLEASH_INSTANCE_ID = 'i1';

@@ -95,4 +95,4 @@ const config = createProxyConfig({});

expect(config.unleashApiToken).toBe('token');
expect(config.proxySecrets.length).toBe(1);
expect(config.proxySecrets[0]).toBe('s1');
expect(config.clientKeys.length).toBe(1);
expect(config.clientKeys[0]).toBe('s1');
expect(config.unleashInstanceId).toBe('i1');

@@ -103,2 +103,21 @@

delete process.env.UNLEASH_API_TOKEN;
delete process.env.UNLEASH_PROXY_CLIENT_KEYS;
delete process.env.UNLEASH_INSTANCE_ID;
});
test('should allow old "UNLEASH_PROXY_SECRETS" option via env', () => {
process.env.UNLEASH_URL = 'some';
process.env.UNLEASH_API_TOKEN = 'token';
process.env.UNLEASH_PROXY_SECRETS = 's1-token, s2-token';
const config = createProxyConfig({});
expect(config.unleashUrl).toBe('some');
expect(config.unleashApiToken).toBe('token');
expect(config.clientKeys.length).toBe(2);
expect(config.clientKeys[0]).toBe('s1-token');
expect(config.clientKeys[1]).toBe('s2-token');
// cleanup
delete process.env.UNLEASH_URL;
delete process.env.UNLEASH_API_TOKEN;
delete process.env.UNLEASH_PROXY_SECRETS;

@@ -122,3 +141,3 @@ });

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
customStrategies: [new TestStrat()],

@@ -142,3 +161,3 @@ });

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -161,3 +180,3 @@

namePrefix: 'somePrefix',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -173,3 +192,3 @@

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -208,3 +227,3 @@

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -221,3 +240,3 @@

unleashApiToken: 'some',
proxySecrets: ['s1'],
clientKeys: ['s1'],
});

@@ -224,0 +243,0 @@

@@ -59,2 +59,102 @@ import request, { Response } from 'supertest';

test('Should handle POST with empty body', () => {
const toggles = [
{
name: 'test',
enabled: true,
},
{
name: 'test2',
enabled: true,
},
];
const client = new MockClient(toggles);
const proxySecrets = ['sdf'];
const app = createApp(
{ proxySecrets, unleashUrl, unleashApiToken },
client,
);
client.emit('ready');
return request(app)
.post('/proxy')
.send({ blah: 'hello' })
.set('Accept', 'application/json')
.set('Authorization', 'sdf')
.expect(200)
.expect('Content-Type', /json/)
.then((response) => {
expect(response.body.toggles).toHaveLength(0);
});
});
test('Should handle POST with toggle names', () => {
const toggles = [
{
name: 'test',
enabled: true,
},
{
name: 'test2',
enabled: true,
},
];
const client = new MockClient(toggles);
const proxySecrets = ['sdf'];
const app = createApp(
{ proxySecrets, unleashUrl, unleashApiToken },
client,
);
client.emit('ready');
return request(app)
.post('/proxy')
.send({ toggles: ['test'] })
.set('Accept', 'application/json')
.set('Authorization', 'sdf')
.expect(200)
.expect('Content-Type', /json/)
.then((response) => {
expect(response.body.toggles).toHaveLength(1);
expect(response.body.toggles[0].name).toBe('test');
});
});
test('Should return list of toggles with custom proxy client key header', () => {
const toggles = [
{
name: 'test',
enabled: true,
},
{
name: 'test2',
enabled: true,
},
];
const client = new MockClient(toggles);
const proxySecrets = ['sdf'];
const app = createApp(
{
proxySecrets,
unleashUrl,
unleashApiToken,
clientKeysHeaderName: 'NotAuthorized',
},
client,
);
client.emit('ready');
return request(app)
.get('/proxy')
.set('NotAuthorized', 'sdf')
.expect(200)
.expect('Content-Type', /json/)
.then((response) => {
expect(response.body.toggles.length).toEqual(2);
});
});
test('Should return list of toggles using env with multiple secrets', () => {

@@ -61,0 +161,0 @@ process.env.UNLEASH_PROXY_SECRETS = 'secret1,secret2';

@@ -14,4 +14,6 @@ import { Request, Response, Router } from 'express';

private proxySecrets: string[];
private clientKeys: string[];
private clientKeysHeaderName: string;
private client: IClient;

@@ -25,3 +27,4 @@

this.logger = config.logger;
this.proxySecrets = config.proxySecrets;
this.clientKeys = config.clientKeys;
this.clientKeysHeaderName = config.clientKeysHeaderName;
this.client = client;

@@ -54,12 +57,17 @@

setProxySecrets(proxySecrets: string[]): void {
this.proxySecrets = proxySecrets;
// kept for backward compatibility
setProxySecrets(clientKeys: string[]): void {
this.setClientKeys(clientKeys);
}
setClientKeys(clientKeys: string[]): void {
this.clientKeys = clientKeys;
}
getEnabledToggles(req: Request, res: Response): void {
const apiToken = req.header('authorization');
const apiToken = req.header(this.clientKeysHeaderName);
if (!this.ready) {
res.status(503).send(NOT_READY);
} else if (!apiToken || !this.proxySecrets.includes(apiToken)) {
} else if (!apiToken || !this.clientKeys.includes(apiToken)) {
res.sendStatus(401);

@@ -77,13 +85,13 @@ } else {

lookupToggles(req: Request, res: Response): void {
const apiToken = req.header('authorization');
const apiToken = req.header(this.clientKeysHeaderName);
if (!this.ready) {
res.status(503).send(NOT_READY);
} else if (!apiToken || !this.proxySecrets.includes(apiToken)) {
} else if (!apiToken || !this.clientKeys.includes(apiToken)) {
res.sendStatus(401);
} else {
const { context, toggles: toggleNames } = req.body;
const { context, toggles: toggleNames = [] } = req.body;
const toggles = this.client.getDefinedToggles(toggleNames, context);
res.send(toggles);
res.send({ toggles });
}

@@ -90,0 +98,0 @@ }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc