diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index d38d9a72be..92271f8521 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/api-report.md @@ -472,9 +472,6 @@ export type ReadUrlResponse = { etag?: string; }; -// @public (undocumented) -export type RedactionMap = Record; - // @public export function requestLoggingHandler(logger?: Logger_2): RequestHandler; @@ -543,7 +540,7 @@ export type ServiceBuilder = { }; // @public (undocumented) -export function setRedactionMap(newRedactionMap: RedactionMap): void; +export function setRedactionList(redactionList: string[]): void; // @public (undocumented) export function setRootLogger(newLogger: winston.Logger): void; diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index ca41d8c1bf..39d0ef1096 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -18,39 +18,36 @@ import { resolve as resolvePath } from 'path'; import parseArgs from 'minimist'; import { Logger } from 'winston'; import { findPaths } from '@backstage/cli-common'; -import { loadConfigSchema, loadConfig } from '@backstage/config-loader'; +import { + loadConfigSchema, + loadConfig, + ConfigSchema, +} from '@backstage/config-loader'; import { AppConfig, Config, ConfigReader, JsonValue } from '@backstage/config'; -import { setRedactionMap } from './logging'; +import { setRedactionList } from './logging'; // Fetch the schema and get all the secrets to pass to the rootLogger for redaction -const updateRedactionMap = async (configs: AppConfig[], logger: Logger) => { - // Consider all packages in the monorepo when loading in config - const { Project } = require('@lerna/project'); - const project = new Project(); - const packages = await project.getPackages(); - const localPackageNames = packages.map((p: any) => p.name); - - const schema = await loadConfigSchema({ dependencies: localPackageNames }); +const updateRedactionMap = ( + schema: ConfigSchema, + configs: AppConfig[], + logger: Logger, +) => { const secretAppConfigs = schema.process(configs, { visibility: ['secret'] }); const secretConfig = ConfigReader.fromConfigs(secretAppConfigs); - const configMap = secretConfig.getMap(); + const values = new Set(); + const data = secretConfig.get(); + + JSON.parse( + JSON.stringify(data), + (_, v) => typeof v === 'string' && values.add(v), + ); logger.info( - `${ - Object.keys(configMap).length - } secrets found in the config which will be redacted`, + `${values.size} secrets found in the config which will be redacted`, ); - setRedactionMap( - Object.entries(configMap).reduce>( - (map, [key, value]) => { - map[value] = key; - return map; - }, - {}, - ), - ); + setRootLoggerRedactionList(Array.from(values)); }; export class ObservableConfigProxy implements Config { @@ -120,9 +117,6 @@ export class ObservableConfigProxy implements Config { get(key?: string): T { return this.select(true).get(key); } - getMap() { - return this.config.getMap(); - } getOptional(key?: string): T | undefined { return this.select(false)?.getOptional(key); } @@ -185,6 +179,9 @@ export async function loadBackendConfig(options: { const args = parseArgs(options.argv); const configPaths: string[] = [args.config ?? []].flat(); + const schema = await loadConfigSchema({ + dependencies: ['@backstage/backend-common'], + }); const config = new ObservableConfigProxy(options.logger); /* eslint-disable-next-line no-restricted-syntax */ diff --git a/packages/backend-common/src/logging/rootLogger.ts b/packages/backend-common/src/logging/rootLogger.ts index aaf91ff232..3691f5cd91 100644 --- a/packages/backend-common/src/logging/rootLogger.ts +++ b/packages/backend-common/src/logging/rootLogger.ts @@ -19,10 +19,8 @@ import { LoggerOptions } from 'winston'; import { coloredFormat } from './formats'; /** @public */ -export type RedactionMap = Record; - let rootLogger: winston.Logger; -let redactionMap: RedactionMap; +let redactionRegExp: RegExp; /** @public */ export function getRootLogger(): winston.Logger { @@ -35,8 +33,8 @@ export function setRootLogger(newLogger: winston.Logger) { } /** @public */ -export function setRedactionMap(newRedactionMap: RedactionMap) { - redactionMap = newRedactionMap; +export function setRedactionList(redactionList: string[]) { + redactionRegExp = new RegExp(`(${redactionList.join('|')})`, 'g'); } /** @@ -46,11 +44,9 @@ export function setRedactionMap(newRedactionMap: RedactionMap) { function redactLogLine(info: winston.Logform.TransformableInfo) { // TODO(hhogg): The logger is created before the config is loaded, // because the logger is needed in the config loader. There is a risk of - // a secret being logged out during the config loading stage 🤷‍♂️ - if (redactionMap) { - Object.entries(redactionMap || {}).forEach(([key, value]) => { - info.message = info.message.replace(new RegExp(key, 'g'), `{{${value}}}`); - }); + // a secret being logged out during the config loading stage. + if (redactionRegExp) { + info.message = info.message.replace(redactionRegExp, '[REDACTED]'); } return info; diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 0947f8c8ab..bc8a860c85 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -17,7 +17,6 @@ import { JsonValue, JsonObject } from '@backstage/types'; import { AppConfig, Config } from './types'; import cloneDeep from 'lodash/cloneDeep'; -import merge from 'lodash/merge'; import mergeWith from 'lodash/mergeWith'; // Update the same pattern in config-loader package if this is changed @@ -119,28 +118,6 @@ export class ConfigReader implements Config { return value as T; } - getMap() { - const map: Record = {}; - - const flatten = (data: JsonValue, path = '') => { - if (isObject(data)) { - Object.entries(data).forEach(([key, value]: [string, any]) => - flatten(value, `${path}${path ? '.' : ''}${key}`), - ); - } else if (Array.isArray(data)) { - data.forEach((value, key) => { - flatten(value, `${path}[${key}]`); - }); - } else { - map[path] = data; - } - }; - - flatten(merge({}, this.fallback?.data, this.data)); - - return map; - } - getOptional(key?: string): T | undefined { const value = this.readValue(key); const fallbackValue = this.fallback?.getOptional(key); diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 628178379e..a543233277 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -65,12 +65,6 @@ export type Config = { */ keys(): string[]; - /** - * Returns a flattened map of the config with the full path to the keys and - * the config value as the value. - */ - getMap(): Record; - /** * Same as `getOptional`, but will throw an error if there's no value for the given key. */