0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2025-04-08 02:54:13 -05:00

fix(proxy): validate protocol of proxy settings ()

* fix(proxy): validate protocol of proxy settings

* Update docs
This commit is contained in:
Marc Bernard 2025-03-09 10:42:11 +01:00 committed by GitHub
parent 83dbde5154
commit 13e0fdef08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 122 additions and 4 deletions
.changeset
packages/proxy
website
docs
versioned_docs/version-6.x

View file

@ -0,0 +1,6 @@
---
'@verdaccio/proxy': patch
'@verdaccio/website': patch
---
fix(proxy): validate protocol of proxy settings

View file

@ -578,12 +578,15 @@ class ProxyStorage implements IProxy {
): void {
let noProxyList;
const proxy_key: string = isHTTPS ? 'https_proxy' : 'http_proxy';
debug('looking for %o', proxy_key);
// get http_proxy and no_proxy configs
if (proxy_key in config) {
this.proxy = config[proxy_key];
debug('found %o in uplink config', this.proxy);
} else if (proxy_key in mainconfig) {
this.proxy = mainconfig[proxy_key];
debug('found %o in main config', this.proxy);
}
if ('no_proxy' in config) {
noProxyList = config.no_proxy;
@ -608,6 +611,7 @@ class ProxyStorage implements IProxy {
}
if (hostname.endsWith(noProxyItem)) {
if (this.proxy) {
debug('not using proxy, excluded by @{rule}', noProxyItem);
this.logger.debug(
{ url: this.url.href, rule: noProxyItem },
'not using proxy for @{url}, excluded by @{rule} rule'
@ -619,7 +623,22 @@ class ProxyStorage implements IProxy {
}
}
// validate proxy protocol matches the proxy_key type
if (this.proxy) {
const proxyUrl = new URL(this.proxy);
const expectedProtocol = isHTTPS ? 'https:' : 'http:';
if (proxyUrl.protocol !== expectedProtocol) {
this.logger.error(
{ proxy: this.proxy, expectedProtocol },
`invalid protocol for ${proxy_key} - must be ${expectedProtocol}`
);
this.proxy = undefined;
return;
}
}
if (typeof this.proxy === 'string') {
debug('using proxy @{proxy} for @{url}', this.proxy, this.url.href);
this.logger.debug(
{ url: this.url.href, proxy: this.proxy },
'using proxy @{proxy} for @{url}'

View file

@ -0,0 +1,87 @@
import { beforeEach, describe, expect, test, vi } from 'vitest';
import { Logger } from '@verdaccio/types';
import { ProxyStorage } from '../src';
const mockDebug = vi.fn();
const mockInfo = vi.fn();
const mockHttp = vi.fn();
const mockError = vi.fn();
const mockWarn = vi.fn();
const logger = {
debug: mockDebug,
info: mockInfo,
http: mockHttp,
error: mockError,
warn: mockWarn,
} as unknown as Logger;
function getProxyInstance(host, uplinkConf, appConfig) {
uplinkConf.url = host;
return new ProxyStorage(uplinkConf, appConfig, logger);
}
describe('Check protocol of proxy', () => {
beforeEach(() => {
vi.clearAllMocks();
});
test('validate main config protocol - http', () => {
expect(
getProxyInstance(
'http://registry.domain.org',
{ http_proxy: 'http://registry.local.org' },
{}
).proxy
).toEqual('http://registry.local.org');
});
test('main config invalid protocol - http', () => {
expect(
getProxyInstance(
'http://registry.domain.org',
{ http_proxy: 'https://registry.local.org' },
{}
).proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
});
test('main config invalid protocol - https', () => {
expect(
getProxyInstance(
'https://registry.domain.org',
{ https_proxy: 'http://registry.local.org' },
{}
).proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
});
test('validate uplink config protocol - http', () => {
expect(
getProxyInstance(
'https://registry.domain.org',
{},
{ https_proxy: 'https://proxy.domain.org' }
).proxy
).toEqual('https://proxy.domain.org');
});
test('uplink config invalid protocol - http', () => {
expect(
getProxyInstance('http://registry.domain.org', {}, { http_proxy: 'https://proxy.domain.org' })
.proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
});
test('uplink config invalid protocol - https', () => {
expect(
getProxyInstance(
'https://registry.domain.org',
{},
{ https_proxy: 'http://proxy.domain.org' }
).proxy
).toEqual(undefined);
expect(mockError).toHaveBeenCalledOnce();
});
});

View file

@ -190,6 +190,8 @@ publish:
allow_offline: false
```
<small>Since: `verdaccio@2.3.6` due [#223](https://github.com/verdaccio/verdaccio/pull/223)</small>
### Checking Package Ownership {#check-owner}
By default, [package access](packages.md) defines who is allowed to publish and unpublish packages. By setting `check_owners` to _true_, only package owners are allowed to make changes to a package. The first owner of a package is the user who published the first version. Further owners can be added or removed using [`npm owner`](https://docs.npmjs.com/cli/v10/commands/npm-owner). You can find the list of current owners using `npm owner list` or by checking the package manifest under `maintainers`.
@ -208,8 +210,6 @@ publish:
keep_readmes: 'tagged'
```
<small>Since: `verdaccio@2.3.6` due [#223](https://github.com/verdaccio/verdaccio/pull/223)</small>
### URL Prefix {#url-prefix}
The prefix is intended to be used when the server runs behinds the proxy and won't work properly if is used without a reverse proxy, check the **reverse proxy setup** page for more details.
@ -307,7 +307,7 @@ https:
### Proxy {#proxy}
Proxies are special-purpose HTTP servers designed to transfer data from remote servers to local clients.
Proxies are special-purpose HTTP servers designed to transfer data from remote servers to local clients. You can define a HTTP or HTTPS proxy in the main configuration or separately for each uplink. The definition for uplinks have higher priority. The proxy protocol (http or https) has to match the protocol of the registry or uplink URLs.
#### http_proxy and https_proxy {#http_proxy-and-https_proxy}

View file

@ -37,6 +37,9 @@ You can define mutiple uplinks and each of them must have an unique name (key).
| maxage | string | No | 10m | all | the time threshold to the cache is valid | 2m |
| fail_timeout | string | No | 10m | all | defines max time when a request becomes a failure | 5m |
| max_fails | number | No | 2 | all | limit maximun failure request | 2 |
| http_proxy | string | No | http://proxy.server.org | all | define HTTP proxy for registry access | No default |
| https_proxy | string | No | https://proxy.server.org | all | define HTTPS proxy for registry access | No default |
| no_proxy | string | No | localhost,127.0.0.1 | all | comma-separated list of hosts that should not use proxy | No default |
| cache | boolean | No | [true,false] | >= 2.1 | cache all remote tarballs in storage | true |
| auth | list | No | [see below](uplinks.md#auth-property) | >= 2.5 | assigns the header 'Authorization' [more info](http://blog.npmjs.org/post/118393368555/deploying-with-npm-private-modules) | disabled |
| headers | list | No | authorization: "Bearer SecretJWToken==" | all | list of custom headers for the uplink | disabled |

View file

@ -315,7 +315,7 @@ https:
### Proxy {#proxy}
Proxies are special-purpose HTTP servers designed to transfer data from remote servers to local clients.
Proxies are special-purpose HTTP servers designed to transfer data from remote servers to local clients. You can define a HTTP or HTTPS proxy in the main configuration or separately for each uplink. The definition for uplinks have higher priority. The proxy protocol (http or https) has to match the protocol of the registry URL.
#### http_proxy and https_proxy {#http_proxy-and-https_proxy}

View file

@ -34,6 +34,9 @@ You can define mutiple uplinks and each of them must have an unique name (key).
| maxage | string | No | 10m | all | the time threshold to the cache is valid | 2m |
| fail_timeout | string | No | 10m | all | defines max time when a request becomes a failure | 5m |
| max_fails | number | No | 2 | all | limit maximun failure request | 2 |
| http_proxy | string | No | http://proxy.server.org | all | define HTTP proxy for registry access | No default |
| https_proxy | string | No | https://proxy.server.org | all | define HTTPS proxy for registry access | No default |
| no_proxy | string | No | localhost,127.0.0.1 | all | comma-separated list of hosts that should not use proxy | No default |
| cache | boolean | No | [true,false] | >= 2.1 | cache all remote tarballs in storage | true |
| auth | list | No | [see below](uplinks.md#auth-property) | >= 2.5 | assigns the header 'Authorization' [more info](http://blog.npmjs.org/post/118393368555/deploying-with-npm-private-modules) | disabled |
| headers | list | No | authorization: "Bearer SecretJWToken==" | all | list of custom headers for the uplink | disabled |