Summary
apiops extract is expected to mark secrets as *** REDACTED *** in extracted artifacts, but secrets hardcoded inline in policy XML are exported in cleartext. This is a security issue — secrets can be committed to source control.
Severity
Security / data exposure — inline policy secrets land in Git in plaintext.
Command that triggered the bug
apiops extract
Expected behavior
Secrets embedded in any policy.xml (service, product, API, operation, resolver level) are redacted to *** REDACTED ***, consistent with how secret named values are handled.
Actual behavior
Policy XML is written verbatim. Redaction only applies to NamedValue resources with properties.secret === true; the policy path has no redaction step.
Root cause (confirmed)
- Redaction is implemented only for
NamedValue (src/services/secret-redactor.ts:31, requires properties.secret === true at :41).
- Policy XML is written verbatim (only HTML-decoded):
src/clients/artifact-store.ts:44-70 (writeContent(..., ''policy'')).
- Extract call sites with no redaction:
- ServicePolicy —
src/services/extract-service.ts:404
- ProductPolicy —
src/services/product-extractor.ts:218
- ApiPolicy —
src/services/api-extractor.ts:287, :366
- ApiOperationPolicy —
src/services/api-extractor.ts:423
- GraphQLResolverPolicy —
src/services/api-extractor.ts:534
Proposed solution (agreed: redact-and-warn)
- Detect literal secrets in every
policy.xml via a structured scan of known secret-bearing locations: set-header (Authorization, Ocp-Apim-Subscription-Key, x-functions-key, api-key), set-query-parameter (code, sig, subscription-key), authentication-basic password, authentication-certificate inline body, validate-jwt signing/decryption keys, connection-string fragments (AccountKey=, SharedAccessKey=).
- Redact each detected literal to
*** REDACTED *** (reuse REDACTION_MARKER).
- Warn per finding, stating: a secret was found and redacted (naming policy + location); publish will fail due to the redacted secret; and the policy should be updated to use a named value — linking to https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-properties
- Fail-safe: a still-redacted policy fails publish loudly rather than leaking or pushing a broken value.
Do NOT redact (non-secret allow-list)
{{namedValue}} references (pointers, not literals), App Insights instrumentation keys / connection strings, AAD tenant/client IDs, public URLs / resource / audience.
Summary
apiops extractis expected to mark secrets as*** REDACTED ***in extracted artifacts, but secrets hardcoded inline in policy XML are exported in cleartext. This is a security issue — secrets can be committed to source control.Severity
Security / data exposure — inline policy secrets land in Git in plaintext.
Command that triggered the bug
apiops extractExpected behavior
Secrets embedded in any
policy.xml(service, product, API, operation, resolver level) are redacted to*** REDACTED ***, consistent with how secret named values are handled.Actual behavior
Policy XML is written verbatim. Redaction only applies to
NamedValueresources withproperties.secret === true; the policy path has no redaction step.Root cause (confirmed)
NamedValue(src/services/secret-redactor.ts:31, requiresproperties.secret === trueat:41).src/clients/artifact-store.ts:44-70(writeContent(..., ''policy'')).src/services/extract-service.ts:404src/services/product-extractor.ts:218src/services/api-extractor.ts:287,:366src/services/api-extractor.ts:423src/services/api-extractor.ts:534Proposed solution (agreed: redact-and-warn)
policy.xmlvia a structured scan of known secret-bearing locations:set-header(Authorization,Ocp-Apim-Subscription-Key,x-functions-key,api-key),set-query-parameter(code,sig,subscription-key),authentication-basicpassword,authentication-certificateinline body,validate-jwtsigning/decryption keys, connection-string fragments (AccountKey=,SharedAccessKey=).*** REDACTED ***(reuseREDACTION_MARKER).Do NOT redact (non-secret allow-list)
{{namedValue}}references (pointers, not literals), App Insights instrumentation keys / connection strings, AAD tenant/client IDs, public URLs /resource/audience.