Skip to content

Commit 54384ff

Browse files
jamietannaClaude Sonnet 5
andauthored
fix(util/host-rules): sanitise TLS credential fields in hostRules (#45119)
* chore(util): note that `httpsCertificate` and `httpsCertificateAuthority` aren't secret Although we're currently treating them as such, they're not technically unsafe to appear in the logs, as a Certificate (and the authority) are public. * fix(util/host-rules): sanitise TLS credential fields in hostRules We previously noted that the `httpsPrivateKey`, `httpsCertificate`, and `httpsCertificateAuthority` fields would be marked as confidential, but we weren't correctly sanitising them. We can make sure that these fields are correctly flagged as secret ("confidential") and that they need to be sanitised when referenced. Although the `httpsCertificate` and `httpsCertificateAuthority` aren't secret, we can keep them sanitised for consistency with how we've currently documented them as being sanitised. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> * test(util/host-rules): ensure that all secret fields are marked as secret As a way to make sure that new fields being added `HostRule`s are considered as to whether the field should be marked as secret ("confidential") or not, we can add a test (and a compile-time exhaustive check) to validate that `redactedFields` and `confidentialFields` are kept in sync. Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io> --------- Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
1 parent 244bfe1 commit 54384ff

3 files changed

Lines changed: 72 additions & 1 deletion

File tree

lib/util/host-rules.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,60 @@
11
import { NugetDatasource } from '../modules/datasource/nuget/index.ts';
2+
import type { HostRule } from '../types/index.ts';
23
import {
34
add,
45
clear,
6+
confidentialFields,
57
find,
68
findAll,
79
getAll,
810
hostType,
911
hosts,
1012
} from './host-rules.ts';
13+
import { redactedFields, sanitize } from './sanitize.ts';
1114

1215
describe('util/host-rules', () => {
1316
beforeEach(() => {
1417
clear();
1518
});
1619

20+
it('registers every redactedFields entry that is a HostRule field for value-level sanitizing', () => {
21+
// exhaustive check for fields of `HostRule`, to introduce a compile-time error when adding a new field to `HostRule`
22+
const allHostRuleFields: Record<keyof HostRule, true> = {
23+
authType: true,
24+
token: true,
25+
username: true,
26+
password: true,
27+
insecureRegistry: true,
28+
timeout: true,
29+
abortOnError: true,
30+
abortIgnoreStatusCodes: true,
31+
enabled: true,
32+
enableHttp2: true,
33+
concurrentRequestLimit: true,
34+
maxRequestsPerSecond: true,
35+
headers: true,
36+
maxRetryAfter: true,
37+
keepAlive: true,
38+
artifactAuth: true,
39+
httpsCertificateAuthority: true,
40+
httpsPrivateKey: true,
41+
httpsCertificate: true,
42+
encrypted: true,
43+
hostType: true,
44+
matchHost: true,
45+
resolvedHost: true,
46+
readOnly: true,
47+
};
48+
49+
const expectedConfidentialFields = redactedFields.filter(
50+
(field) => field in allHostRuleFields,
51+
);
52+
53+
expect([...confidentialFields].sort()).toEqual(
54+
expectedConfidentialFields.sort(),
55+
);
56+
});
57+
1758
describe('add()', () => {
1859
it('throws if both domainName and hostName', () => {
1960
expect(() =>
@@ -107,6 +148,20 @@ describe('util/host-rules', () => {
107148
username: 'user2',
108149
});
109150
});
151+
152+
it('sanitizes TLS credential values', () => {
153+
add({
154+
matchHost: 'https://some.endpoint',
155+
httpsPrivateKey: 'private-key-value',
156+
httpsCertificate: 'certificate-value',
157+
httpsCertificateAuthority: 'certificate-authority-value',
158+
});
159+
expect(
160+
sanitize(
161+
'key=private-key-value cert=certificate-value ca=certificate-authority-value',
162+
),
163+
).toBe('key=**redacted** cert=**redacted** ca=**redacted**');
164+
});
110165
});
111166

112167
describe('find()', () => {

lib/util/host-rules.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@ import { isHttpUrl, massageHostUrl, parseUrl } from './url.ts';
88

99
let hostRules: HostRule[] = [];
1010

11+
/**
12+
* Fields within `HostRule`s that must have their value registered for sanitising through `sanitize.addSecretForSanitizing()`.
13+
*
14+
* Kept in sync with `redactedFields` through tests.
15+
*/
16+
export const confidentialFields: (keyof HostRule)[] = [
17+
'password',
18+
'token',
19+
'httpsPrivateKey',
20+
/* not actually sensitive, but redacted nonetheless */
21+
'httpsCertificate',
22+
/* not actually sensitive, but redacted nonetheless */
23+
'httpsCertificateAuthority',
24+
];
25+
1126
export interface LegacyHostRule {
1227
hostName?: string;
1328
domainName?: string;
@@ -41,7 +56,6 @@ export function migrateRule(rule: LegacyHostRule & HostRule): HostRule {
4156
export function add(params: HostRule): void {
4257
const rule = migrateRule(params);
4358

44-
const confidentialFields: (keyof HostRule)[] = ['password', 'token'];
4559
if (rule.matchHost) {
4660
rule.matchHost = massageHostUrl(rule.matchHost);
4761
const parsedUrl = parseUrl(rule.matchHost);

lib/util/sanitize.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ export const redactedFields = [
1515
'gitPrivateKey',
1616
'forkToken',
1717
'password',
18+
/* not actually sensitive, but redacted nonetheless */
1819
'httpsCertificate',
1920
'httpsPrivateKey',
21+
/* not actually sensitive, but redacted nonetheless */
2022
'httpsCertificateAuthority',
2123
];
2224

0 commit comments

Comments
 (0)