Files
renovate/lib/util/http/host-rules.spec.ts
Jamie Tanna 1dce2f8ed0 feat(util/http): fall back to go host rules for go-proxy requests (#47043)
* fix(util/http): fall back to `go` host rules for `go-proxy` requests

Prior to 5665570f0b, all requests made by
the `go` Datasource - including those to a Go module proxy - used the
`go` host type.

When we refactored them into classes, this introduced the ability to
specify a `go-proxy` host type, which would only be used for direct
access to the Go module proxy.

As this isn't documented - and may change in the future - we can add a
fallback to the credentials for `hostType: go`, similar to how i.e.
`gitea-tags` and `gitea` do the same.

Co-Authored-By: Claude Opus 5.5 <jamie.tanna+claude-code@mend.io>

* fixup! fix(util/http): fall back to `go` host rules for `go-proxy` requests

Co-Authored-By: Claude Sonnet 5.5 <jamie.tanna+claude-code@mend.io>

* fixup! fix(util/http): fall back to `go` host rules for `go-proxy` requests

---------

Co-authored-by: Claude Opus 5.5 <jamie.tanna+claude-code@mend.io>
2026-10-15 09:48:27 +00:00

1043 lines
28 KiB
TypeScript

import { GlobalConfig } from '../../config/global.ts';
import { bootstrap } from '../../proxy.ts';
import type { HostRule } from '../../types/index.ts';
import * as hostRules from '../host-rules.ts';
import { applyHostRule, findMatchingRule } from './host-rules.ts';
import type { GotOptions } from './types.ts';
const url = 'https://github.com';
vi.mock('global-agent');
describe('util/http/host-rules', () => {
const options: GotOptions = {
hostType: 'github',
};
beforeEach(() => {
vi.stubEnv('HTTP_PROXY', undefined);
hostRules.add({
hostType: 'github',
token: 'token',
});
hostRules.add({
hostType: 'github',
matchHost: 'api.github.com',
token: 'dotcom-api-token',
});
hostRules.add({
hostType: 'gitea',
password: 'password',
});
hostRules.add({
hostType: 'npm',
authType: 'Basic',
token: 'XXX',
timeout: 5000,
});
hostRules.add({
hostType: 'gitlab',
token: 'abc',
});
hostRules.add({
hostType: 'bitbucket',
token: 'cdef',
});
hostRules.add({
hostType: 'bitbucket-server',
token: 'cdef',
});
});
it('adds token', () => {
const opts = { ...options };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github',
token: 'token',
});
});
it('adds token to an api.github.com URL', () => {
const url = 'https://api.github.com/user';
const opts = { ...options };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-api-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github',
token: 'dotcom-api-token',
});
});
it('adds auth', () => {
const opts = { hostType: 'gitea' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
password: 'password',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'gitea',
password: 'password',
username: undefined,
});
});
it('adds custom auth', () => {
const opts = { hostType: 'npm' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
authType: 'Basic',
timeout: 5000,
token: 'XXX',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: 'Basic',
},
hostType: 'npm',
timeout: 5000,
token: 'XXX',
});
});
it('skips', () => {
const opts = { ...options, token: 'xxx' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'github',
token: 'xxx',
});
});
it('uses http2', () => {
hostRules.add({ enableHttp2: true });
const opts = { ...options, token: 'xxx' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
enableHttp2: true,
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'github',
http2: true,
token: 'xxx',
});
});
it('uses http keep-alive', () => {
hostRules.add({ keepAlive: true });
const opts = { ...options, token: 'xxx' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
keepAlive: true,
token: 'token',
});
expect(applyHostRule(url, opts, hostRule).agent).toBeDefined();
});
it('disables http2', () => {
vi.stubEnv('HTTP_PROXY', 'http://proxy');
bootstrap();
hostRules.add({ enableHttp2: true });
const opts = { ...options, token: 'xxx' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
enableHttp2: true,
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'github',
token: 'xxx',
});
});
it('noAuth', () => {
const opts = { ...options, noAuth: true };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'github',
noAuth: true,
});
});
it('certificateAuthority', () => {
hostRules.add({
hostType: 'maven',
matchHost: 'https://custom.datasource.ca',
httpsCertificateAuthority: 'ca-cert',
});
const url = 'https://custom.datasource.ca/data/path';
const opts = { ...options, hostType: 'maven' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
httpsCertificateAuthority: 'ca-cert',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'maven',
https: {
certificateAuthority: 'ca-cert',
},
});
});
it('privateKey', () => {
hostRules.add({
hostType: 'maven',
matchHost: 'https://custom.datasource.key',
httpsPrivateKey: 'key',
});
const url = 'https://custom.datasource.key/data/path';
const opts = { ...options, hostType: 'maven' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
httpsPrivateKey: 'key',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'maven',
https: {
key: 'key',
},
});
});
it('certificate', () => {
hostRules.add({
hostType: 'maven',
matchHost: 'https://custom.datasource.cert',
httpsCertificate: 'cert',
});
const url = 'https://custom.datasource.cert/data/path';
const opts = { ...options, hostType: 'maven' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
httpsCertificate: 'cert',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'maven',
https: {
certificate: 'cert',
},
});
});
it('no fallback to github', () => {
hostRules.add({
hostType: 'github-tags',
username: 'some2',
password: 'xxx2',
});
hostRules.add({
hostType: 'github-changelog',
token: 'changelogtoken',
});
hostRules.add({
hostType: 'pod',
token: 'pod-token',
});
hostRules.add({
hostType: 'github-releases',
username: 'some',
password: 'xxx',
});
let opts: GotOptions;
let hostRule: HostRule;
opts = { ...options, hostType: 'github-releases' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
password: 'xxx',
username: 'some',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'github-releases',
username: 'some',
password: 'xxx',
});
opts = { ...options, hostType: 'github-tags' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
password: 'xxx2',
username: 'some2',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'github-tags',
username: 'some2',
password: 'xxx2',
});
opts = { ...options, hostType: 'pod' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'pod-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'pod',
token: 'pod-token',
});
opts = { ...options, hostType: 'github-changelog' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'changelogtoken',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github-changelog',
token: 'changelogtoken',
});
});
it('fallback to github', () => {
let opts: GotOptions;
let hostRule: HostRule;
opts = { ...options, hostType: 'github-tags' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github-tags',
token: 'token',
});
opts = { ...options, hostType: 'github-changelog' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github-changelog',
token: 'token',
});
opts = { ...options, hostType: 'pod' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'pod',
token: 'token',
});
// in the case that an API URL is used for GitHub.com, auto-detect it as a `hostType=github`, and use the `url`'s host to find a `matchHost: github.com`
{
const url = 'https://api.github.com/renovatebot/renovate';
{
opts = {};
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-api-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
token: 'dotcom-api-token',
});
}
// in the case a Datasource uses GitHub APIs, but doesn't have an explicit wiring in via PLATFORM_FAMILIES.github.apiUsingHostTypes, we should also auto-detect
// See #30490 #38725
{
const url =
'https://api.github.com/repos/bitrise-io/bitrise-steplib/contents';
opts = { hostType: 'bitrise' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-api-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'bitrise',
token: 'dotcom-api-token',
});
}
// and validate that this has lowest precedence compared to other rules
{
hostRules.clear();
hostRules.add({
hostType: 'github-changelog',
token: 'changelogtoken',
});
hostRules.add({
hostType: 'github',
token: 'token',
});
hostRules.add({
matchHost: 'api.github.com',
token: 'dotcom-api-token',
});
// the specific `hostType` wins if we use it
opts = { hostType: 'github-changelog' };
hostRule = findMatchingRule('https://github.com/chalk/chalk', opts);
expect(hostRule).toEqual({
token: 'changelogtoken',
});
expect(
applyHostRule('https://github.com/chalk/chalk', opts, hostRule),
).toEqual({
context: {
authType: undefined,
},
hostType: 'github-changelog',
token: 'changelogtoken',
});
// but if no `hostType` matching, we'll use our `hostType: github` for `github.com`
opts = {};
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-api-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
token: 'dotcom-api-token',
});
}
}
});
it('when multiple GitHub host types are set', () => {
let opts: GotOptions;
let hostRule: HostRule;
hostRules.clear();
// for instance, if running with GitHub Enterprise Server, this may be set as the default host rule
hostRules.add({
hostType: 'github',
token: 'token',
});
// and our GitHub.com authentication
hostRules.add({
// with the `hostType`
hostType: 'github',
matchHost: 'github.com',
token: 'dotcom-hostType-token',
});
hostRules.add({
// and without `hostType`
matchHost: 'github.com',
token: 'dotcom-token',
});
{
const url = 'https://github.enterprise';
opts = { ...options };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github',
token: 'token',
});
}
{
const url = 'https://github.enterprise/api/v4';
opts = { ...options };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github',
token: 'token',
});
}
{
const url = 'https://api.github.com/user';
opts = { ...options };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-hostType-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github',
token: 'dotcom-hostType-token',
});
}
opts = { ...options, hostType: 'github-tags' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github-tags',
token: 'dotcom-token',
});
opts = { ...options, hostType: 'github-changelog' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'github-changelog',
token: 'dotcom-token',
});
opts = { ...options, hostType: 'pod' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'dotcom-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'pod',
token: 'dotcom-token',
});
});
describe('GHE platform endpoint fallback', () => {
beforeEach(() => {
GlobalConfig.set({
platform: 'github',
endpoint: 'https://ghe.example.com/',
});
hostRules.clear();
hostRules.add({
hostType: 'github',
matchHost: 'ghe.example.com',
token: 'ghe-token',
});
});
it('fallback to github for non-listed hostType targeting GHE endpoint', () => {
// github-digest is NOT in PLATFORM_FAMILIES.github.apiUsingHostTypes,
// but should still get credentials when targeting the GHE endpoint
const opts = { hostType: 'github-digest' };
const hostRule = findMatchingRule(
'https://ghe.example.com/api/v3/',
opts,
);
expect(hostRule).toEqual({
token: 'ghe-token',
});
expect(
applyHostRule('https://ghe.example.com/api/v3/', opts, hostRule),
).toEqual({
context: {
authType: undefined,
},
hostType: 'github-digest',
token: 'ghe-token',
});
});
it('no fallback when request targets a different host', () => {
// Request targets a different host than the platform endpoint — no fallback
const opts = { hostType: 'github-digest' };
const hostRule = findMatchingRule(
'https://other-ghe.example.com/api/v3/',
opts,
);
expect(hostRule).toEqual({});
});
});
it('no fallback to gitlab', () => {
hostRules.add({
hostType: 'gitlab-packages',
token: 'package-token',
});
hostRules.add({
hostType: 'gitlab-releases',
token: 'release-token',
});
hostRules.add({
hostType: 'gitlab-tags',
token: 'tags-token',
});
let opts: GotOptions;
let hostRule: HostRule;
opts = { ...options, hostType: 'gitlab-tags' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'tags-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitlab-tags',
token: 'tags-token',
});
opts = { ...options, hostType: 'gitlab-releases' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'release-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitlab-releases',
token: 'release-token',
});
opts = { ...options, hostType: 'gitlab-packages' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'package-token',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitlab-packages',
token: 'package-token',
});
});
it('fallback to gitlab', () => {
let opts: GotOptions;
let hostRule: HostRule;
opts = { ...options, hostType: 'gitlab-tags' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'abc',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitlab-tags',
token: 'abc',
});
opts = { ...options, hostType: 'gitlab-releases' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'abc',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitlab-releases',
token: 'abc',
});
opts = { ...options, hostType: 'gitlab-packages' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'abc',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitlab-packages',
token: 'abc',
});
opts = { ...options, hostType: 'gitlab-changelog' };
hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'abc',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitlab-changelog',
token: 'abc',
});
});
it('no fallback to bitbucket', () => {
hostRules.add({
hostType: 'bitbucket-tags',
username: 'some',
password: 'xxx',
});
const opts = { ...options, hostType: 'bitbucket-tags' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
password: 'xxx',
username: 'some',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'bitbucket-tags',
username: 'some',
password: 'xxx',
});
});
it('fallback to bitbucket', () => {
const opts = { ...options, hostType: 'bitbucket-tags' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'cdef',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'bitbucket-tags',
token: 'cdef',
});
});
it('no fallback to bitbucket-server', () => {
hostRules.add({
hostType: 'bitbucket-server-tags',
username: 'some',
password: 'xxx',
});
const opts = { ...options, hostType: 'bitbucket-server-tags' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
password: 'xxx',
username: 'some',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'bitbucket-server-tags',
username: 'some',
password: 'xxx',
});
});
it('fallback to bitbucket-server', () => {
const opts = { ...options, hostType: 'bitbucket-server-tags' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'cdef',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'bitbucket-server-tags',
token: 'cdef',
});
});
it('no fallback to gitea', () => {
hostRules.add({
hostType: 'gitea-tags',
token: 'abc',
});
const opts = { ...options, hostType: 'gitea-tags' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
token: 'abc',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
context: {
authType: undefined,
},
hostType: 'gitea-tags',
token: 'abc',
});
});
it('fallback to forgejo', () => {
hostRules.add({
hostType: 'forgejo',
password: 'password',
});
const opts = { ...options, hostType: 'forgejo-tags' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
password: 'password',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'forgejo-tags',
password: 'password',
username: undefined,
});
});
it('fallback to gitea', () => {
const opts = { ...options, hostType: 'gitea-tags' };
const hostRule = findMatchingRule(url, opts);
expect(hostRule).toEqual({
password: 'password',
});
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'gitea-tags',
password: 'password',
username: undefined,
});
});
it('hostType=go-proxy gets a fallback to hostType=go if no hostType=go-proxy credentials', () => {
hostRules.add({
hostType: 'go',
matchHost: 'example.com',
token: 'go-token',
});
const opts = { ...options, hostType: 'go-proxy' };
const hostRule = findMatchingRule('https://example.com/foo/@v/list', opts);
expect(hostRule).toEqual({
token: 'go-token',
});
});
it('hostType=go-proxy gets a fallback to a host-less hostType=go if no hostType=go-proxy credentials', () => {
hostRules.add({
hostType: 'go',
token: 'any-host-go-token',
});
const opts = { ...options, hostType: 'go-proxy' };
const hostRule = findMatchingRule('https://example.com/foo/@v/list', opts);
expect(hostRule).toEqual({
token: 'any-host-go-token',
});
});
it(`hostType=go-proxy's fallback to hostType=go only copies auth-related fields`, () => {
hostRules.add({
hostType: 'go',
matchHost: 'example.com',
token: 'go-token',
username: 'renovatebot',
password: 'super-secret',
httpsCertificate: '--- BEGIN ...',
httpsPrivateKey: '--- BEGIN ...',
httpsCertificateAuthority: '--- BEGIN ...',
// ignored
enabled: true,
timeout: 10000000,
});
const opts = { ...options, hostType: 'go-proxy' };
const hostRule = findMatchingRule('https://example.com/foo/@v/list', opts);
expect(hostRule).toEqual({
token: 'go-token',
username: 'renovatebot',
password: 'super-secret',
httpsCertificate: '--- BEGIN ...',
httpsPrivateKey: '--- BEGIN ...',
httpsCertificateAuthority: '--- BEGIN ...',
});
});
it('hostType=go-proxy does not fallback to hostType=go if hostType=go-proxy already has credentials', () => {
hostRules.add({
hostType: 'go',
matchHost: 'example.com',
token: 'go-token',
});
hostRules.add({
hostType: 'go-proxy',
matchHost: 'example.com',
token: 'go-proxy-token',
});
const opts = { ...options, hostType: 'go-proxy' };
const hostRule = findMatchingRule('https://example.com/foo/@v/list', opts);
expect(hostRule).toEqual({
token: 'go-proxy-token',
});
});
it('should remove forbidden headers from request', () => {
GlobalConfig.set({ allowedHeaders: ['X-*'] });
const hostRule = {
matchHost: 'https://domain.com/all-versions',
headers: {
'X-Auth-Token': 'token',
unallowedHeader: 'token',
},
};
expect(applyHostRule(url, {}, hostRule)).toEqual({
headers: {
'X-Auth-Token': 'token',
},
});
});
it('should replace existing headers with host rule headers', () => {
GlobalConfig.set({ allowedHeaders: ['Accept'] });
const hostRule = {
matchHost: 'https://domain.com/all-versions',
headers: {
Accept: 'replacement',
},
};
const options = {
headers: {
Accept: 'default',
},
};
expect(applyHostRule(url, options, hostRule)).toEqual({
headers: {
Accept: 'replacement',
},
});
});
it('does not drop a header named in trustedHeaderNames, even where allowedHeaders would deny it', () => {
// set by `hostRules.find()` for headers that already bypassed `allowedHeaders` at registration as the self-hosted administrator's own - must not be re-checked, and dropped, here
GlobalConfig.set({ allowedHeaders: ['X-*'] });
const hostRule = {
matchHost: 'https://domain.com/all-versions',
headers: {
Authorization: 'from-admin',
},
trustedHeaderNames: ['Authorization'],
};
expect(applyHostRule(url, {}, hostRule)).toEqual({
headers: {
Authorization: 'from-admin',
},
});
});
it("keeps an admin's trusted headers exempt from allowedHeaders through a hostType fallback", () => {
// `findMatchingRule`'s hostType fallbacks (e.g. `github-tags` -> `github`) rebuild `res` as `{ ...fallbackResult, ...res }` for each one - `trustedHeaderNames` must survive that merge alongside `headers`, not just when queried under the rule's own `hostType`
GlobalConfig.set({ allowedHeaders: ['X-*'] });
hostRules.add(
{
hostType: 'github',
matchHost: 'github.com',
headers: { Authorization: 'from-admin' },
},
{ trusted: true },
);
const opts: GotOptions = { ...options, hostType: 'github-tags' };
const hostRule = findMatchingRule(url, opts);
expect(applyHostRule(url, opts, hostRule).headers).toEqual({
Authorization: 'from-admin',
});
});
it("does not let a fallback hostType's trustedHeaderNames leak onto a rule's own untrusted headers", () => {
// the inverse of the previous test: `hostRules.find()` sets `trustedHeaderNames: undefined` on a rule with only untrusted headers so that it shadows a fallback's `trustedHeaderNames` during `{ ...fallbackResult, ...res }` - otherwise, if `allowedHeaders` narrows after registration, a repository's already-registered untrusted header could inherit the admin's `trustedHeaderNames` from a `github` hostType fallback and wrongly bypass the new, narrower `allowedHeaders`
GlobalConfig.set({ allowedHeaders: ['Authorization'] });
hostRules.add(
{
hostType: 'github',
matchHost: 'github.com',
headers: { Authorization: 'from-admin' },
},
{ trusted: true },
);
hostRules.add({
hostType: 'github-tags',
matchHost: 'github.com',
headers: { Authorization: 'from-repo' },
});
// simulate `allowedHeaders` narrowing after both rules were registered - the repo's header must not be grandfathered in as the admin's trusted one
GlobalConfig.set({ allowedHeaders: [] });
const opts: GotOptions = { ...options, hostType: 'github-tags' };
const hostRule = findMatchingRule(url, opts);
// dropped, not `{ Authorization: 'from-repo' }` - if the fallback's `trustedHeaderNames` had leaked through, this untrusted header would have wrongly bypassed `allowedHeaders`
expect(applyHostRule(url, opts, hostRule).headers).toEqual({});
});
it('enabled=false with noAuth', () => {
hostRules.add({
hostType: 'docker',
enabled: false,
});
const opts = { ...options, hostType: 'docker', noAuth: true };
const hostRule = findMatchingRule(url, opts);
expect(applyHostRule(url, opts, hostRule)).toEqual({
hostType: 'docker',
noAuth: true,
enabled: false,
});
});
});