@peterhauge/apiops-cli 0.3.0-alpha.0 → 0.4.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +166 -0
- package/README.md +6 -0
- package/dist/cli/init-command.js +5 -5
- package/dist/cli/init-command.js.map +1 -1
- package/dist/lib/render-template.d.ts +10 -0
- package/dist/lib/render-template.d.ts.map +1 -0
- package/dist/lib/render-template.js +14 -0
- package/dist/lib/render-template.js.map +1 -0
- package/dist/services/api-extractor.d.ts.map +1 -1
- package/dist/services/api-extractor.js +10 -6
- package/dist/services/api-extractor.js.map +1 -1
- package/dist/services/extract-service.d.ts.map +1 -1
- package/dist/services/extract-service.js +4 -2
- package/dist/services/extract-service.js.map +1 -1
- package/dist/services/identity-guide-service.d.ts +3 -4
- package/dist/services/identity-guide-service.d.ts.map +1 -1
- package/dist/services/identity-guide-service.js +6 -40
- package/dist/services/identity-guide-service.js.map +1 -1
- package/dist/services/init-service.d.ts.map +1 -1
- package/dist/services/init-service.js +61 -28
- package/dist/services/init-service.js.map +1 -1
- package/dist/services/product-extractor.d.ts.map +1 -1
- package/dist/services/product-extractor.js +4 -2
- package/dist/services/product-extractor.js.map +1 -1
- package/dist/services/publish-service.d.ts.map +1 -1
- package/dist/services/publish-service.js +30 -0
- package/dist/services/publish-service.js.map +1 -1
- package/dist/services/resource-publisher.d.ts +5 -0
- package/dist/services/resource-publisher.d.ts.map +1 -1
- package/dist/services/resource-publisher.js +15 -1
- package/dist/services/resource-publisher.js.map +1 -1
- package/dist/services/secret-redaction-guard.d.ts +40 -0
- package/dist/services/secret-redaction-guard.d.ts.map +1 -0
- package/dist/services/secret-redaction-guard.js +92 -0
- package/dist/services/secret-redaction-guard.js.map +1 -0
- package/dist/services/secret-redactor.d.ts +18 -0
- package/dist/services/secret-redactor.d.ts.map +1 -1
- package/dist/services/secret-redactor.js +172 -0
- package/dist/services/secret-redactor.js.map +1 -1
- package/dist/templates/azure-devops/publish-pipeline.d.ts.map +1 -1
- package/dist/templates/azure-devops/publish-pipeline.js +62 -28
- package/dist/templates/azure-devops/publish-pipeline.js.map +1 -1
- package/dist/templates/configs/filter-config.d.ts.map +1 -1
- package/dist/templates/configs/filter-config.js +6 -118
- package/dist/templates/configs/filter-config.js.map +1 -1
- package/dist/templates/configs/override-config.d.ts.map +1 -1
- package/dist/templates/configs/override-config.js +7 -92
- package/dist/templates/configs/override-config.js.map +1 -1
- package/dist/templates/configs/schema-ref.d.ts +18 -0
- package/dist/templates/configs/schema-ref.d.ts.map +1 -0
- package/dist/templates/configs/schema-ref.js +26 -0
- package/dist/templates/configs/schema-ref.js.map +1 -0
- package/dist/templates/copilot/configure-filter-prompt.d.ts +10 -0
- package/dist/templates/copilot/configure-filter-prompt.d.ts.map +1 -0
- package/dist/templates/copilot/configure-filter-prompt.js +14 -0
- package/dist/templates/copilot/configure-filter-prompt.js.map +1 -0
- package/dist/templates/copilot/configure-overrides-prompt.d.ts +7 -0
- package/dist/templates/copilot/configure-overrides-prompt.d.ts.map +1 -0
- package/dist/templates/copilot/configure-overrides-prompt.js +12 -0
- package/dist/templates/copilot/configure-overrides-prompt.js.map +1 -0
- package/dist/templates/copilot/identity-setup-prompt.d.ts.map +1 -1
- package/dist/templates/copilot/identity-setup-prompt.js +19 -24
- package/dist/templates/copilot/identity-setup-prompt.js.map +1 -1
- package/dist/templates/generated/embedded-markdown.d.ts +8 -5
- package/dist/templates/generated/embedded-markdown.d.ts.map +1 -1
- package/dist/templates/generated/embedded-markdown.js +8 -5
- package/dist/templates/generated/embedded-markdown.js.map +1 -1
- package/dist/templates/github-actions/extract-workflow.js +4 -4
- package/dist/templates/github-actions/publish-workflow.d.ts +2 -1
- package/dist/templates/github-actions/publish-workflow.d.ts.map +1 -1
- package/dist/templates/github-actions/publish-workflow.js +103 -78
- package/dist/templates/github-actions/publish-workflow.js.map +1 -1
- package/package.json +8 -5
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret redaction pre-flight guard
|
|
3
|
+
*
|
|
4
|
+
* Scans the set of artifacts that are about to be published for leftover
|
|
5
|
+
* redaction markers ('*** REDACTED ***'). Any finding aborts the entire
|
|
6
|
+
* publish before a single PUT is issued (for both real and dry-run modes),
|
|
7
|
+
* so a partially-published service can never result from placeholder secrets.
|
|
8
|
+
*
|
|
9
|
+
* Detection mirrors the per-resource publish guards:
|
|
10
|
+
* - Policies: build the publish payload, apply overrides, then check the
|
|
11
|
+
* merged `properties.value` for the marker (an override may legitimately
|
|
12
|
+
* replace the value with clean content).
|
|
13
|
+
* - Named values: read the resource, apply overrides; KeyVault-backed values
|
|
14
|
+
* have their `value` stripped before publish and are therefore ignored;
|
|
15
|
+
* otherwise a `secret === true` value that exactly equals the marker is a
|
|
16
|
+
* finding.
|
|
17
|
+
*/
|
|
18
|
+
import type { IArtifactStore } from '../clients/iartifact-store.js';
|
|
19
|
+
import type { PublishConfig } from '../models/config.js';
|
|
20
|
+
import type { ResourceDescriptor } from '../models/types.js';
|
|
21
|
+
/**
|
|
22
|
+
* A single artifact that still contains a redaction marker after overrides.
|
|
23
|
+
*/
|
|
24
|
+
export interface RedactionMarkerFinding {
|
|
25
|
+
/** The descriptor for the offending artifact. */
|
|
26
|
+
descriptor: ResourceDescriptor;
|
|
27
|
+
/** Human-readable resource label (e.g. `apis/echo/policies/policy`). */
|
|
28
|
+
label: string;
|
|
29
|
+
/** Where the marker was found (e.g. `policy.xml`, `properties.value`). */
|
|
30
|
+
location: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Scan the supplied descriptors for leftover redaction markers in the content
|
|
34
|
+
* that would actually be published (i.e. after overrides are applied).
|
|
35
|
+
*
|
|
36
|
+
* Returns every finding so the caller can report all offenders at once rather
|
|
37
|
+
* than failing on the first one.
|
|
38
|
+
*/
|
|
39
|
+
export declare function scanForRedactionMarkers(store: IArtifactStore, config: PublishConfig, descriptors: ResourceDescriptor[]): Promise<RedactionMarkerFinding[]>;
|
|
40
|
+
//# sourceMappingURL=secret-redaction-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-redaction-guard.d.ts","sourceRoot":"","sources":["../../src/services/secret-redaction-guard.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAO7D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,iDAAiD;IACjD,UAAU,EAAE,kBAAkB,CAAC;IAC/B,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,kBAAkB,EAAE,GAChC,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAcnC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
/**
|
|
4
|
+
* Secret redaction pre-flight guard
|
|
5
|
+
*
|
|
6
|
+
* Scans the set of artifacts that are about to be published for leftover
|
|
7
|
+
* redaction markers ('*** REDACTED ***'). Any finding aborts the entire
|
|
8
|
+
* publish before a single PUT is issued (for both real and dry-run modes),
|
|
9
|
+
* so a partially-published service can never result from placeholder secrets.
|
|
10
|
+
*
|
|
11
|
+
* Detection mirrors the per-resource publish guards:
|
|
12
|
+
* - Policies: build the publish payload, apply overrides, then check the
|
|
13
|
+
* merged `properties.value` for the marker (an override may legitimately
|
|
14
|
+
* replace the value with clean content).
|
|
15
|
+
* - Named values: read the resource, apply overrides; KeyVault-backed values
|
|
16
|
+
* have their `value` stripped before publish and are therefore ignored;
|
|
17
|
+
* otherwise a `secret === true` value that exactly equals the marker is a
|
|
18
|
+
* finding.
|
|
19
|
+
*/
|
|
20
|
+
import { ResourceType } from '../models/resource-types.js';
|
|
21
|
+
import { applyOverrides } from './override-merger.js';
|
|
22
|
+
import { POLICY_TYPES } from './resource-publisher.js';
|
|
23
|
+
import { REDACTION_MARKER } from './secret-redactor.js';
|
|
24
|
+
import { buildResourceLabel } from '../lib/resource-uri.js';
|
|
25
|
+
/**
|
|
26
|
+
* Scan the supplied descriptors for leftover redaction markers in the content
|
|
27
|
+
* that would actually be published (i.e. after overrides are applied).
|
|
28
|
+
*
|
|
29
|
+
* Returns every finding so the caller can report all offenders at once rather
|
|
30
|
+
* than failing on the first one.
|
|
31
|
+
*/
|
|
32
|
+
export async function scanForRedactionMarkers(store, config, descriptors) {
|
|
33
|
+
const findings = [];
|
|
34
|
+
for (const descriptor of descriptors) {
|
|
35
|
+
if (POLICY_TYPES.has(descriptor.type)) {
|
|
36
|
+
const finding = await scanPolicy(store, config, descriptor);
|
|
37
|
+
if (finding)
|
|
38
|
+
findings.push(finding);
|
|
39
|
+
}
|
|
40
|
+
else if (descriptor.type === ResourceType.NamedValue) {
|
|
41
|
+
const finding = await scanNamedValue(store, config, descriptor);
|
|
42
|
+
if (finding)
|
|
43
|
+
findings.push(finding);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return findings;
|
|
47
|
+
}
|
|
48
|
+
async function scanPolicy(store, config, descriptor) {
|
|
49
|
+
const policyContent = await store.readContent(config.sourceDir, descriptor, 'policy');
|
|
50
|
+
if (!policyContent) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
const payload = {
|
|
54
|
+
properties: {
|
|
55
|
+
value: policyContent.content,
|
|
56
|
+
format: 'rawxml',
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
const merged = applyOverrides(descriptor, payload, config.overrides);
|
|
60
|
+
const mergedProps = merged.properties;
|
|
61
|
+
const mergedValue = mergedProps?.value;
|
|
62
|
+
if (typeof mergedValue === 'string' && mergedValue.includes(REDACTION_MARKER)) {
|
|
63
|
+
return {
|
|
64
|
+
descriptor,
|
|
65
|
+
label: buildResourceLabel(descriptor),
|
|
66
|
+
location: 'policy.xml',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
async function scanNamedValue(store, config, descriptor) {
|
|
72
|
+
const json = await store.readResource(config.sourceDir, descriptor);
|
|
73
|
+
if (!json) {
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
const merged = applyOverrides(descriptor, json, config.overrides);
|
|
77
|
+
const props = merged.properties;
|
|
78
|
+
// KeyVault-backed named values have `properties.value` stripped before publish,
|
|
79
|
+
// so any marker still present in the file is never sent to APIM.
|
|
80
|
+
if (props?.keyVault != null) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
if (props?.secret === true && props.value === REDACTION_MARKER) {
|
|
84
|
+
return {
|
|
85
|
+
descriptor,
|
|
86
|
+
label: buildResourceLabel(descriptor),
|
|
87
|
+
location: 'properties.value',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
return undefined;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=secret-redaction-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-redaction-guard.js","sourceRoot":"","sources":["../../src/services/secret-redaction-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
import { ResourceDescriptor } from '../models/types.js';
|
|
7
7
|
/** Marker used to replace secret values in extracted artifacts */
|
|
8
8
|
export declare const REDACTION_MARKER = "*** REDACTED ***";
|
|
9
|
+
export interface PolicySecretFinding {
|
|
10
|
+
location: string;
|
|
11
|
+
}
|
|
9
12
|
/**
|
|
10
13
|
* Redact secret values from a resource's JSON payload.
|
|
11
14
|
*
|
|
@@ -17,4 +20,19 @@ export declare const REDACTION_MARKER = "*** REDACTED ***";
|
|
|
17
20
|
* @returns JSON with secrets redacted
|
|
18
21
|
*/
|
|
19
22
|
export declare function redactSecrets(descriptor: ResourceDescriptor, json: Record<string, unknown>): Record<string, unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* Redact inline literal secrets in policy XML content.
|
|
25
|
+
*/
|
|
26
|
+
export declare function redactPolicySecrets(policyContent: string): {
|
|
27
|
+
redactedContent: string;
|
|
28
|
+
findings: PolicySecretFinding[];
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Redact inline literal secrets in policy XML content and emit a warning log
|
|
32
|
+
* for every finding. This is the entry point services should use so that
|
|
33
|
+
* redaction and warning always happen together; the underlying
|
|
34
|
+
* {@link redactPolicySecrets} (pure, exported) and `warnPolicySecretRedactions`
|
|
35
|
+
* (private) helpers stay separate for testability.
|
|
36
|
+
*/
|
|
37
|
+
export declare function redactAndWarnPolicySecrets(descriptor: ResourceDescriptor, policyContent: string): string;
|
|
20
38
|
//# sourceMappingURL=secret-redactor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret-redactor.d.ts","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"secret-redactor.d.ts","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAKxD,kEAAkE;AAClE,eAAO,MAAM,gBAAgB,qBAAqB,CAAC;AAoBnD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,kBAAkB,EAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA4BzB;AAqCD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,GACpB;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,mBAAmB,EAAE,CAAA;CAAE,CAgI9D;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,kBAAkB,EAC9B,aAAa,EAAE,MAAM,GACpB,MAAM,CAIR"}
|
|
@@ -8,8 +8,27 @@
|
|
|
8
8
|
import { ResourceType } from '../models/resource-types.js';
|
|
9
9
|
import { logger } from '../lib/logger.js';
|
|
10
10
|
import { getNamePart } from '../lib/resource-path.js';
|
|
11
|
+
import { buildResourceLabel } from '../lib/resource-uri.js';
|
|
11
12
|
/** Marker used to replace secret values in extracted artifacts */
|
|
12
13
|
export const REDACTION_MARKER = '*** REDACTED ***';
|
|
14
|
+
const NAMED_VALUE_REFERENCE_PATTERN = /^\s*\{\{[^{}]+\}\}\s*$/;
|
|
15
|
+
// Headers where inline literal values are typically secrets and should be
|
|
16
|
+
// redacted when present in policy XML. Extend this allow-list when APIM adds
|
|
17
|
+
// new secret-bearing header conventions.
|
|
18
|
+
const SECRET_HEADER_NAMES = new Set([
|
|
19
|
+
'authorization',
|
|
20
|
+
'ocp-apim-subscription-key',
|
|
21
|
+
'x-functions-key',
|
|
22
|
+
'api-key',
|
|
23
|
+
]);
|
|
24
|
+
// Query parameters commonly used to carry secrets/tokens in APIM policies.
|
|
25
|
+
// Keep focused on secret-bearing names to avoid over-redacting non-secrets.
|
|
26
|
+
const SECRET_QUERY_PARAMETER_NAMES = new Set([
|
|
27
|
+
'code',
|
|
28
|
+
'sig',
|
|
29
|
+
'subscription-key',
|
|
30
|
+
]);
|
|
31
|
+
const BEARER_TOKEN_PATTERN = /^(\s*Bearer)(\s+)(.*?)(\s*)$/i;
|
|
13
32
|
/**
|
|
14
33
|
* Redact secret values from a resource's JSON payload.
|
|
15
34
|
*
|
|
@@ -44,4 +63,157 @@ export function redactSecrets(descriptor, json) {
|
|
|
44
63
|
logger.debug(`Redacted secret value for named value "${getNamePart(descriptor.nameParts, 0)}"`);
|
|
45
64
|
return redacted;
|
|
46
65
|
}
|
|
66
|
+
function isApimNamedValueReference(value) {
|
|
67
|
+
return NAMED_VALUE_REFERENCE_PATTERN.test(value);
|
|
68
|
+
}
|
|
69
|
+
function shouldRedactLiteral(value) {
|
|
70
|
+
const trimmed = value.trim();
|
|
71
|
+
if (!trimmed || trimmed === REDACTION_MARKER) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
return !isApimNamedValueReference(trimmed);
|
|
75
|
+
}
|
|
76
|
+
function redactAuthorizationHeaderValue(value) {
|
|
77
|
+
const bearerMatch = BEARER_TOKEN_PATTERN.exec(value);
|
|
78
|
+
if (bearerMatch) {
|
|
79
|
+
const [, scheme, spacing, tokenValue, suffix] = bearerMatch;
|
|
80
|
+
if (!shouldRedactLiteral(tokenValue)) {
|
|
81
|
+
return { redactedValue: value, wasRedacted: false };
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
redactedValue: `${scheme}${spacing}${REDACTION_MARKER}${suffix}`,
|
|
85
|
+
wasRedacted: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
if (!shouldRedactLiteral(value)) {
|
|
89
|
+
return { redactedValue: value, wasRedacted: false };
|
|
90
|
+
}
|
|
91
|
+
return { redactedValue: REDACTION_MARKER, wasRedacted: true };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Redact inline literal secrets in policy XML content.
|
|
95
|
+
*/
|
|
96
|
+
export function redactPolicySecrets(policyContent) {
|
|
97
|
+
const findings = [];
|
|
98
|
+
const addFinding = (location) => {
|
|
99
|
+
findings.push({ location });
|
|
100
|
+
};
|
|
101
|
+
let redacted = policyContent;
|
|
102
|
+
redacted = redacted.replace(/<set-header\b[\s\S]*?<\/set-header>/gi, (setHeaderBlock) => {
|
|
103
|
+
const nameMatch = /\bname\s*=\s*["']([^"']+)["']/i.exec(setHeaderBlock);
|
|
104
|
+
const headerName = nameMatch?.[1]?.toLowerCase();
|
|
105
|
+
if (!headerName || !SECRET_HEADER_NAMES.has(headerName)) {
|
|
106
|
+
return setHeaderBlock;
|
|
107
|
+
}
|
|
108
|
+
return setHeaderBlock.replace(/(<value\b[^>]*>)([\s\S]*?)(<\/value>)/gi, (_full, openTag, value, closeTag) => {
|
|
109
|
+
const shouldRedactHeaderValue = shouldRedactLiteral(value);
|
|
110
|
+
const { redactedValue, wasRedacted } = headerName === 'authorization'
|
|
111
|
+
? redactAuthorizationHeaderValue(value)
|
|
112
|
+
: {
|
|
113
|
+
redactedValue: shouldRedactHeaderValue ? REDACTION_MARKER : value,
|
|
114
|
+
wasRedacted: shouldRedactHeaderValue,
|
|
115
|
+
};
|
|
116
|
+
if (!wasRedacted) {
|
|
117
|
+
return `${openTag}${value}${closeTag}`;
|
|
118
|
+
}
|
|
119
|
+
addFinding(`set-header[${headerName}]`);
|
|
120
|
+
return `${openTag}${redactedValue}${closeTag}`;
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
redacted = redacted.replace(/<set-query-parameter\b[\s\S]*?<\/set-query-parameter>/gi, (setQueryBlock) => {
|
|
124
|
+
const nameMatch = /\bname\s*=\s*["']([^"']+)["']/i.exec(setQueryBlock);
|
|
125
|
+
const parameterName = nameMatch?.[1]?.toLowerCase();
|
|
126
|
+
if (!parameterName || !SECRET_QUERY_PARAMETER_NAMES.has(parameterName)) {
|
|
127
|
+
return setQueryBlock;
|
|
128
|
+
}
|
|
129
|
+
return setQueryBlock.replace(/(<value\b[^>]*>)([\s\S]*?)(<\/value>)/gi, (_full, openTag, value, closeTag) => {
|
|
130
|
+
if (!shouldRedactLiteral(value)) {
|
|
131
|
+
return `${openTag}${value}${closeTag}`;
|
|
132
|
+
}
|
|
133
|
+
addFinding(`set-query-parameter[${parameterName}]`);
|
|
134
|
+
return `${openTag}${REDACTION_MARKER}${closeTag}`;
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
redacted = redacted.replace(/<authentication-basic\b[^>]*>/gi, (tag) => {
|
|
138
|
+
return tag.replace(/(\bpassword\s*=\s*["'])([^"']*)(["'])/i, (_full, prefix, value, suffix) => {
|
|
139
|
+
if (!shouldRedactLiteral(value)) {
|
|
140
|
+
return `${prefix}${value}${suffix}`;
|
|
141
|
+
}
|
|
142
|
+
addFinding('authentication-basic@password');
|
|
143
|
+
return `${prefix}${REDACTION_MARKER}${suffix}`;
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
redacted = redacted.replace(/<authentication-certificate\b[^>]*>/gi, (tag) => {
|
|
147
|
+
return tag.replace(/(\bbody\s*=\s*["'])([^"']*)(["'])/i, (_full, prefix, value, suffix) => {
|
|
148
|
+
if (!shouldRedactLiteral(value)) {
|
|
149
|
+
return `${prefix}${value}${suffix}`;
|
|
150
|
+
}
|
|
151
|
+
addFinding('authentication-certificate@body');
|
|
152
|
+
return `${prefix}${REDACTION_MARKER}${suffix}`;
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
redacted = redacted.replace(/<authentication-certificate\b[\s\S]*?<\/authentication-certificate>/gi, (certificateBlock) => {
|
|
156
|
+
return certificateBlock.replace(/(<certificate\b[^>]*>)([\s\S]*?)(<\/certificate>)/gi, (_full, openTag, value, closeTag) => {
|
|
157
|
+
if (!shouldRedactLiteral(value)) {
|
|
158
|
+
return `${openTag}${value}${closeTag}`;
|
|
159
|
+
}
|
|
160
|
+
addFinding('authentication-certificate/certificate');
|
|
161
|
+
return `${openTag}${REDACTION_MARKER}${closeTag}`;
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
for (const keySection of ['issuer-signing-keys', 'decryption-keys']) {
|
|
165
|
+
const sectionRegex = new RegExp(`<${keySection}\\b[\\s\\S]*?<\\/${keySection}>`, 'gi');
|
|
166
|
+
redacted = redacted.replace(sectionRegex, (sectionBlock) => {
|
|
167
|
+
return sectionBlock.replace(/(<key\b[^>]*>)([\s\S]*?)(<\/key>)/gi, (_full, openTag, value, closeTag) => {
|
|
168
|
+
if (!shouldRedactLiteral(value)) {
|
|
169
|
+
return `${openTag}${value}${closeTag}`;
|
|
170
|
+
}
|
|
171
|
+
addFinding(`validate-jwt ${keySection}/key`);
|
|
172
|
+
return `${openTag}${REDACTION_MARKER}${closeTag}`;
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// AccountKey/SharedAccessKey fragments are used by storage/service-bus style
|
|
177
|
+
// connection strings. App Insights connection strings use InstrumentationKey
|
|
178
|
+
// and therefore do not match this pattern (allow-listed by design).
|
|
179
|
+
// Value exclusions:
|
|
180
|
+
// - ';' stops at the next connection-string key/value delimiter
|
|
181
|
+
// - whitespace/newlines avoid over-capturing adjacent text
|
|
182
|
+
// - '<' and '"' avoid crossing into XML tags/attributes
|
|
183
|
+
redacted = redacted.replace(/(AccountKey|SharedAccessKey)\s*=\s*([^;\r\n<"\s]+)/gi, (_full, key, value) => {
|
|
184
|
+
if (!shouldRedactLiteral(value)) {
|
|
185
|
+
return `${key}=${value}`;
|
|
186
|
+
}
|
|
187
|
+
addFinding(`connection-string[${key}]`);
|
|
188
|
+
return `${key}=${REDACTION_MARKER}`;
|
|
189
|
+
});
|
|
190
|
+
return {
|
|
191
|
+
redactedContent: redacted,
|
|
192
|
+
findings,
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Redact inline literal secrets in policy XML content and emit a warning log
|
|
197
|
+
* for every finding. This is the entry point services should use so that
|
|
198
|
+
* redaction and warning always happen together; the underlying
|
|
199
|
+
* {@link redactPolicySecrets} (pure, exported) and `warnPolicySecretRedactions`
|
|
200
|
+
* (private) helpers stay separate for testability.
|
|
201
|
+
*/
|
|
202
|
+
export function redactAndWarnPolicySecrets(descriptor, policyContent) {
|
|
203
|
+
const { redactedContent, findings } = redactPolicySecrets(policyContent);
|
|
204
|
+
warnPolicySecretRedactions(descriptor, findings);
|
|
205
|
+
return redactedContent;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Emit warning logs for policy secret redaction findings.
|
|
209
|
+
*/
|
|
210
|
+
function warnPolicySecretRedactions(descriptor, findings) {
|
|
211
|
+
const label = buildResourceLabel(descriptor);
|
|
212
|
+
for (const finding of findings) {
|
|
213
|
+
logger.warn(`Found and redacted inline secret in ${label} (${finding.location}). ` +
|
|
214
|
+
`Publish will fail while '${REDACTION_MARKER}' remains. ` +
|
|
215
|
+
'Update the policy to use a named value: ' +
|
|
216
|
+
'https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-properties');
|
|
217
|
+
}
|
|
218
|
+
}
|
|
47
219
|
//# sourceMappingURL=secret-redactor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"secret-redactor.js","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAClC;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAE3D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"secret-redactor.js","sourceRoot":"","sources":["../../src/services/secret-redactor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-pipeline.d.ts","sourceRoot":"","sources":["../../../src/templates/azure-devops/publish-pipeline.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"publish-pipeline.d.ts","sourceRoot":"","sources":["../../../src/templates/azure-devops/publish-pipeline.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAEH,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,qBAAqB,GAAG,MAAM,CA6J7E"}
|
|
@@ -7,17 +7,18 @@
|
|
|
7
7
|
export function generatePublishPipeline(config) {
|
|
8
8
|
const defaultEnvironment = config.environments[0] ?? 'dev';
|
|
9
9
|
const envValues = config.environments.map((env) => ` - '${env}'`).join('\n');
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
// A single parameterized stage drives all environments. The ENVIRONMENT
|
|
11
|
+
// parameter is resolved at template (compile) time, so it can select the
|
|
12
|
+
// variable group, deployment environment, config file, and env-suffixed
|
|
13
|
+
// variable names (via upper()) without duplicating the stage per environment.
|
|
14
|
+
const stage = `- stage: Publish
|
|
15
|
+
displayName: 'Publish to \${{ parameters.ENVIRONMENT }}'
|
|
15
16
|
variables:
|
|
16
|
-
- group: apim
|
|
17
|
+
- group: apim-\${{ parameters.ENVIRONMENT }}
|
|
17
18
|
jobs:
|
|
18
19
|
- deployment: Deploy
|
|
19
|
-
displayName: 'Deploy to
|
|
20
|
-
environment:
|
|
20
|
+
displayName: 'Deploy to \${{ parameters.ENVIRONMENT }}'
|
|
21
|
+
environment: \${{ parameters.ENVIRONMENT }}
|
|
21
22
|
pool:
|
|
22
23
|
vmImage: 'ubuntu-latest'
|
|
23
24
|
strategy:
|
|
@@ -46,54 +47,87 @@ export function generatePublishPipeline(config) {
|
|
|
46
47
|
displayName: 'Install dependencies'
|
|
47
48
|
|
|
48
49
|
- task: replacetokens@6
|
|
49
|
-
displayName: 'Substitute tokens in configuration
|
|
50
|
+
displayName: 'Substitute tokens in configuration.\${{ parameters.ENVIRONMENT }}.yaml'
|
|
50
51
|
inputs:
|
|
51
|
-
sources: 'configuration
|
|
52
|
+
sources: 'configuration.\${{ parameters.ENVIRONMENT }}.yaml'
|
|
52
53
|
tokenPattern: 'custom'
|
|
53
54
|
tokenPrefix: '{#['
|
|
54
55
|
tokenSuffix: ']#}'
|
|
55
56
|
missingVarAction: 'keep'
|
|
57
|
+
missingVarLog: 'error'
|
|
56
58
|
|
|
57
59
|
- script: |
|
|
58
|
-
if grep -q '{#\\[' configuration
|
|
59
|
-
echo "Unresolved tokens remain in configuration
|
|
60
|
-
grep -o '{#\\[[^]]*\\]#}' configuration
|
|
60
|
+
if grep -q '{#\\[' configuration.\${{ parameters.ENVIRONMENT }}.yaml; then
|
|
61
|
+
echo "Unresolved tokens remain in configuration.\${{ parameters.ENVIRONMENT }}.yaml"
|
|
62
|
+
grep -o '{#\\[[^]]*\\]#}' configuration.\${{ parameters.ENVIRONMENT }}.yaml | sort -u
|
|
61
63
|
exit 1
|
|
62
64
|
fi
|
|
63
|
-
displayName: 'Validate token substitution
|
|
65
|
+
displayName: 'Validate token substitution'
|
|
64
66
|
|
|
65
67
|
- task: AzureCLI@2
|
|
66
|
-
displayName: '
|
|
67
|
-
condition: ne('\${{ parameters.COMMIT_ID_CHOICE }}', 'publish-all-artifacts-in-repo')
|
|
68
|
+
displayName: 'Dry-run validation (incremental)'
|
|
69
|
+
condition: and(succeeded(), ne('\${{ parameters.COMMIT_ID_CHOICE }}', 'publish-all-artifacts-in-repo'))
|
|
68
70
|
inputs:
|
|
69
|
-
azureSubscription: 'AZURE_SERVICE_CONNECTION_
|
|
71
|
+
azureSubscription: 'AZURE_SERVICE_CONNECTION_\${{ upper(parameters.ENVIRONMENT) }}'
|
|
70
72
|
scriptType: 'bash'
|
|
71
73
|
scriptLocation: 'inlineScript'
|
|
72
74
|
inlineScript: |
|
|
73
75
|
npx apiops publish \\
|
|
74
|
-
--resource-group $(APIM_RESOURCE_GROUP_
|
|
75
|
-
--service-name $(APIM_SERVICE_NAME_
|
|
76
|
+
--resource-group $(APIM_RESOURCE_GROUP_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
77
|
+
--service-name $(APIM_SERVICE_NAME_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
76
78
|
--source ${config.artifactDir} \\
|
|
77
|
-
--overrides configuration
|
|
79
|
+
--overrides configuration.\${{ parameters.ENVIRONMENT }}.yaml \\
|
|
80
|
+
--commit-id $(Build.SourceVersion) \\
|
|
81
|
+
--subscription-id $(AZURE_SUBSCRIPTION_ID) \\
|
|
82
|
+
--dry-run
|
|
83
|
+
|
|
84
|
+
- task: AzureCLI@2
|
|
85
|
+
displayName: 'Dry-run validation (all artifacts)'
|
|
86
|
+
condition: and(succeeded(), eq('\${{ parameters.COMMIT_ID_CHOICE }}', 'publish-all-artifacts-in-repo'))
|
|
87
|
+
inputs:
|
|
88
|
+
azureSubscription: 'AZURE_SERVICE_CONNECTION_\${{ upper(parameters.ENVIRONMENT) }}'
|
|
89
|
+
scriptType: 'bash'
|
|
90
|
+
scriptLocation: 'inlineScript'
|
|
91
|
+
inlineScript: |
|
|
92
|
+
npx apiops publish \\
|
|
93
|
+
--resource-group $(APIM_RESOURCE_GROUP_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
94
|
+
--service-name $(APIM_SERVICE_NAME_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
95
|
+
--source ${config.artifactDir} \\
|
|
96
|
+
--overrides configuration.\${{ parameters.ENVIRONMENT }}.yaml \\
|
|
97
|
+
--subscription-id $(AZURE_SUBSCRIPTION_ID) \\
|
|
98
|
+
--dry-run
|
|
99
|
+
|
|
100
|
+
- task: AzureCLI@2
|
|
101
|
+
displayName: 'Publish (incremental - last commit only)'
|
|
102
|
+
condition: and(succeeded(), ne('\${{ parameters.COMMIT_ID_CHOICE }}', 'publish-all-artifacts-in-repo'))
|
|
103
|
+
inputs:
|
|
104
|
+
azureSubscription: 'AZURE_SERVICE_CONNECTION_\${{ upper(parameters.ENVIRONMENT) }}'
|
|
105
|
+
scriptType: 'bash'
|
|
106
|
+
scriptLocation: 'inlineScript'
|
|
107
|
+
inlineScript: |
|
|
108
|
+
npx apiops publish \\
|
|
109
|
+
--resource-group $(APIM_RESOURCE_GROUP_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
110
|
+
--service-name $(APIM_SERVICE_NAME_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
111
|
+
--source ${config.artifactDir} \\
|
|
112
|
+
--overrides configuration.\${{ parameters.ENVIRONMENT }}.yaml \\
|
|
78
113
|
--commit-id $(Build.SourceVersion) \\
|
|
79
114
|
--subscription-id $(AZURE_SUBSCRIPTION_ID)
|
|
80
115
|
|
|
81
116
|
- task: AzureCLI@2
|
|
82
|
-
displayName: 'Publish
|
|
83
|
-
condition: eq('\${{ parameters.COMMIT_ID_CHOICE }}', 'publish-all-artifacts-in-repo')
|
|
117
|
+
displayName: 'Publish (all artifacts)'
|
|
118
|
+
condition: and(succeeded(), eq('\${{ parameters.COMMIT_ID_CHOICE }}', 'publish-all-artifacts-in-repo'))
|
|
84
119
|
inputs:
|
|
85
|
-
azureSubscription: 'AZURE_SERVICE_CONNECTION_
|
|
120
|
+
azureSubscription: 'AZURE_SERVICE_CONNECTION_\${{ upper(parameters.ENVIRONMENT) }}'
|
|
86
121
|
scriptType: 'bash'
|
|
87
122
|
scriptLocation: 'inlineScript'
|
|
88
123
|
inlineScript: |
|
|
89
124
|
npx apiops publish \\
|
|
90
|
-
--resource-group $(APIM_RESOURCE_GROUP_
|
|
91
|
-
--service-name $(APIM_SERVICE_NAME_
|
|
125
|
+
--resource-group $(APIM_RESOURCE_GROUP_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
126
|
+
--service-name $(APIM_SERVICE_NAME_\${{ upper(parameters.ENVIRONMENT) }}) \\
|
|
92
127
|
--source ${config.artifactDir} \\
|
|
93
|
-
--overrides configuration
|
|
128
|
+
--overrides configuration.\${{ parameters.ENVIRONMENT }}.yaml \\
|
|
94
129
|
--subscription-id $(AZURE_SUBSCRIPTION_ID)
|
|
95
130
|
`;
|
|
96
|
-
}).join('\n');
|
|
97
131
|
return `# Azure DevOps Pipeline: Run APIM Publisher
|
|
98
132
|
|
|
99
133
|
trigger:
|
|
@@ -123,7 +157,7 @@ parameters:
|
|
|
123
157
|
${envValues}
|
|
124
158
|
|
|
125
159
|
stages:
|
|
126
|
-
${
|
|
160
|
+
${stage}
|
|
127
161
|
`;
|
|
128
162
|
}
|
|
129
163
|
//# sourceMappingURL=publish-pipeline.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publish-pipeline.js","sourceRoot":"","sources":["../../../src/templates/azure-devops/publish-pipeline.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAClC;;;GAGG;AAOH,MAAM,UAAU,uBAAuB,CAAC,MAA6B;IACnE,MAAM,kBAAkB,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAElF,
|
|
1
|
+
{"version":3,"file":"publish-pipeline.js","sourceRoot":"","sources":["../../../src/templates/azure-devops/publish-pipeline.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAClC;;;GAGG;AAOH,MAAM,UAAU,uBAAuB,CAAC,MAA6B;IACnE,MAAM,kBAAkB,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAC3D,MAAM,SAAS,GAAG,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,YAAY,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAElF,wEAAwE;IACxE,yEAAyE;IACzE,wEAAwE;IACxE,8EAA8E;IAC9E,MAAM,KAAK,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iCAgEiB,MAAM,CAAC,WAAW;;;;;;;;;;;;;;;;;iCAiBlB,MAAM,CAAC,WAAW;;;;;;;;;;;;;;;;iCAgBlB,MAAM,CAAC,WAAW;;;;;;;;;;;;;;;;iCAgBlB,MAAM,CAAC,WAAW;;;CAGlD,CAAC;IAEA,OAAO;;;;;;;;WAQE,MAAM,CAAC,WAAW;;;;;;;;;;;;;;;;gBAgBb,kBAAkB;;EAEhC,SAAS;;;EAGT,KAAK;CACN,CAAC;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filter-config.d.ts","sourceRoot":"","sources":["../../../src/templates/configs/filter-config.ts"],"names":[],"mappings":"AAEA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"filter-config.d.ts","sourceRoot":"","sources":["../../../src/templates/configs/filter-config.ts"],"names":[],"mappings":"AAEA;;;GAGG;AAMH,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C"}
|