Merge pull request #18837 from backstage/rugvip/strict-config-schema
config-loader: reject invalid configuration schema
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/config-loader': minor
|
||||
---
|
||||
|
||||
Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored.
|
||||
|
||||
In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-bitbucket-server': patch
|
||||
'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch
|
||||
'@backstage/plugin-catalog-backend-module-puppetdb': patch
|
||||
'@backstage/plugin-catalog-backend-module-msgraph': patch
|
||||
'@backstage/plugin-catalog-backend-module-gerrit': patch
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
'@backstage/plugin-catalog-backend-module-gitlab': patch
|
||||
'@backstage/plugin-events-backend-module-aws-sqs': patch
|
||||
'@backstage/plugin-catalog-backend-module-azure': patch
|
||||
'@backstage/plugin-catalog-backend-module-aws': patch
|
||||
'@backstage/backend-common': patch
|
||||
'@backstage/plugin-jenkins-backend': patch
|
||||
'@backstage/plugin-proxy-backend': patch
|
||||
---
|
||||
|
||||
Fixed invalid configuration schema. The configuration schema may be more strict as a result.
|
||||
Vendored
+3
-3
@@ -77,17 +77,17 @@ export interface Config {
|
||||
*/
|
||||
connection:
|
||||
| string
|
||||
| Partial<{
|
||||
| {
|
||||
/**
|
||||
* Password that belongs to the client User
|
||||
* @visibility secret
|
||||
*/
|
||||
password: string;
|
||||
password?: string;
|
||||
/**
|
||||
* Other connection settings
|
||||
*/
|
||||
[key: string]: unknown;
|
||||
}>;
|
||||
};
|
||||
/** Database name prefix override */
|
||||
prefix?: string;
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import { EOL } from 'os';
|
||||
import {
|
||||
resolve as resolvePath,
|
||||
relative as relativePath,
|
||||
@@ -163,7 +164,7 @@ async function compileTsSchemas(paths: string[]) {
|
||||
|
||||
// Lazy loaded, because this brings up all of TypeScript and we don't
|
||||
// want that eagerly loaded in tests
|
||||
const { getProgramFromFiles, generateSchema } = await import(
|
||||
const { getProgramFromFiles, buildGenerator } = await import(
|
||||
'typescript-json-schema'
|
||||
);
|
||||
|
||||
@@ -183,17 +184,40 @@ async function compileTsSchemas(paths: string[]) {
|
||||
const tsSchemas = paths.map(path => {
|
||||
let value;
|
||||
try {
|
||||
value = generateSchema(
|
||||
const generator = buildGenerator(
|
||||
program,
|
||||
// All schemas should export a `Config` symbol
|
||||
'Config',
|
||||
// This enables the use of these tags in TSDoc comments
|
||||
{
|
||||
required: true,
|
||||
validationKeywords: ['visibility', 'deepVisibility', 'deprecated'],
|
||||
},
|
||||
[path.split(sep).join('/')], // Unix paths are expected for all OSes here
|
||||
) as JsonObject | null;
|
||||
);
|
||||
|
||||
// All schemas should export a `Config` symbol
|
||||
value = generator?.getSchemaForSymbol('Config') as JsonObject | null;
|
||||
|
||||
// This makes sure that no additional symbols are defined in the schema. We don't allow
|
||||
// this because they share a global namespace and will be merged together, leading to
|
||||
// unpredictable behavior.
|
||||
const userSymbols = new Set(generator?.getUserSymbols());
|
||||
userSymbols.delete('Config');
|
||||
if (userSymbols.size !== 0) {
|
||||
const names = Array.from(userSymbols).join("', '");
|
||||
throw new Error(
|
||||
`Invalid configuration schema in ${path}, additional symbol definitions are not allowed, found '${names}'`,
|
||||
);
|
||||
}
|
||||
|
||||
// This makes sure that no unsupported types are used in the schema, for example `Record<,>`.
|
||||
// The generator will extract these as a schema reference, which will in turn be broken for our usage.
|
||||
const reffedDefs = Object.keys(generator?.ReffedDefinitions ?? {});
|
||||
if (reffedDefs.length !== 0) {
|
||||
const lines = reffedDefs.join(`${EOL} `);
|
||||
throw new Error(
|
||||
`Invalid configuration schema in ${path}, the following definitions are not supported:${EOL}${EOL} ${lines}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
assertError(error);
|
||||
if (error.message !== 'type Config not found') {
|
||||
|
||||
+4
-5
@@ -59,9 +59,8 @@ export interface Config {
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
| Record<
|
||||
string,
|
||||
{
|
||||
| {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Required) AWS S3 Bucket Name
|
||||
*/
|
||||
@@ -81,8 +80,8 @@ export interface Config {
|
||||
* (Optional) TaskScheduleDefinition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+30
-30
@@ -16,35 +16,6 @@
|
||||
|
||||
import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks';
|
||||
|
||||
interface AzureDevOpsConfig {
|
||||
/**
|
||||
* (Optional) The DevOps host; leave empty for `dev.azure.com`, otherwise set to your self-hosted instance host.
|
||||
*/
|
||||
host: string;
|
||||
/**
|
||||
* (Required) Your organization slug.
|
||||
*/
|
||||
organization: string;
|
||||
/**
|
||||
* (Required) Your project slug.
|
||||
*/
|
||||
project: string;
|
||||
/**
|
||||
* (Optional) The repository name. Wildcards are supported as show on the examples above.
|
||||
* If not set, all repositories will be searched.
|
||||
*/
|
||||
repository?: string;
|
||||
/**
|
||||
* (Optional) Where to find catalog-info.yaml files. Wildcards are supported.
|
||||
* If not set, defaults to /catalog-info.yaml.
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* (Optional) TaskScheduleDefinition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
|
||||
export interface Config {
|
||||
catalog?: {
|
||||
/**
|
||||
@@ -54,7 +25,36 @@ export interface Config {
|
||||
/**
|
||||
* AzureDevopsEntityProvider configuration
|
||||
*/
|
||||
azureDevOps?: Record<string, AzureDevOpsConfig>;
|
||||
azureDevOps?: {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Optional) The DevOps host; leave empty for `dev.azure.com`, otherwise set to your self-hosted instance host.
|
||||
*/
|
||||
host: string;
|
||||
/**
|
||||
* (Required) Your organization slug.
|
||||
*/
|
||||
organization: string;
|
||||
/**
|
||||
* (Required) Your project slug.
|
||||
*/
|
||||
project: string;
|
||||
/**
|
||||
* (Optional) The repository name. Wildcards are supported as show on the examples above.
|
||||
* If not set, all repositories will be searched.
|
||||
*/
|
||||
repository?: string;
|
||||
/**
|
||||
* (Optional) Where to find catalog-info.yaml files. Wildcards are supported.
|
||||
* If not set, defaults to /catalog-info.yaml.
|
||||
*/
|
||||
path?: string;
|
||||
/**
|
||||
* (Optional) TaskScheduleDefinition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+12
-13
@@ -45,24 +45,23 @@ export interface Config {
|
||||
*/
|
||||
filters?: {
|
||||
/**
|
||||
* (Optional) Filter for the repository slug.
|
||||
* (Optional) Regular expression filter for the repository slug.
|
||||
* @visibility frontend
|
||||
*/
|
||||
repoSlug?: RegExp;
|
||||
repoSlug?: string;
|
||||
/**
|
||||
* (Optional) Filter for the project key.
|
||||
* (Optional) Regular expression filter for the project key.
|
||||
* @visibility frontend
|
||||
*/
|
||||
projectKey?: RegExp;
|
||||
projectKey?: string;
|
||||
};
|
||||
/**
|
||||
* (Optional) TaskScheduleDefinition for the discovery.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
| Record<
|
||||
string,
|
||||
{
|
||||
| {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Optional) Path to the catalog file. Default to "/catalog-info.yaml".
|
||||
* @visibility frontend
|
||||
@@ -79,22 +78,22 @@ export interface Config {
|
||||
*/
|
||||
filters?: {
|
||||
/**
|
||||
* (Optional) Filter for the repository slug.
|
||||
* (Optional) Regular expression filter for the repository slug.
|
||||
* @visibility frontend
|
||||
*/
|
||||
repoSlug?: RegExp;
|
||||
repoSlug?: string;
|
||||
/**
|
||||
* (Optional) Filter for the project key.
|
||||
* (Optional) Regular expression filter for the project key.
|
||||
* @visibility frontend
|
||||
*/
|
||||
projectKey?: RegExp;
|
||||
projectKey?: string;
|
||||
};
|
||||
/**
|
||||
* (Optional) TaskScheduleDefinition for the discovery.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+12
-13
@@ -37,24 +37,23 @@ export interface Config {
|
||||
*/
|
||||
filters?: {
|
||||
/**
|
||||
* (Optional) Filter for the repository slug.
|
||||
* (Optional) Regular expression filter for the repository slug.
|
||||
* @visibility frontend
|
||||
*/
|
||||
repoSlug?: RegExp;
|
||||
repoSlug?: string;
|
||||
/**
|
||||
* (Optional) Filter for the project key.
|
||||
* (Optional) Regular expression filter for the project key.
|
||||
* @visibility frontend
|
||||
*/
|
||||
projectKey?: RegExp;
|
||||
projectKey?: string;
|
||||
};
|
||||
/**
|
||||
* (Optional) TaskScheduleDefinition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
| Record<
|
||||
string,
|
||||
{
|
||||
| {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Optional) Path to the catalog file. Default to "/catalog-info.yaml".
|
||||
* @visibility frontend
|
||||
@@ -66,22 +65,22 @@ export interface Config {
|
||||
*/
|
||||
filters?: {
|
||||
/**
|
||||
* (Optional) Filter for the repository slug.
|
||||
* (Optional) Regular expression filter for the repository slug.
|
||||
* @visibility frontend
|
||||
*/
|
||||
repoSlug?: RegExp;
|
||||
repoSlug?: string;
|
||||
/**
|
||||
* (Optional) Filter for the project key.
|
||||
* (Optional) Regular expression filter for the project key.
|
||||
* @visibility frontend
|
||||
*/
|
||||
projectKey?: RegExp;
|
||||
projectKey?: string;
|
||||
};
|
||||
/**
|
||||
* (Optional) TaskScheduleDefinition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+4
-5
@@ -25,9 +25,8 @@ export interface Config {
|
||||
*
|
||||
* Maps provider id with configuration.
|
||||
*/
|
||||
gerrit?: Record<
|
||||
string,
|
||||
{
|
||||
gerrit?: {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Required) The host of the Gerrit integration to use.
|
||||
*/
|
||||
@@ -42,8 +41,8 @@ export interface Config {
|
||||
* The branch where the provider will try to find entities. Defaults to "master".
|
||||
*/
|
||||
branch?: string;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+4
-5
@@ -118,9 +118,8 @@ export interface Config {
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
| Record<
|
||||
string,
|
||||
{
|
||||
| {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Optional) The hostname of your GitHub Enterprise instance.
|
||||
* Default: `github.com`.
|
||||
@@ -182,8 +181,8 @@ export interface Config {
|
||||
* (Optional) TaskScheduleDefinition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+7
-8
@@ -22,9 +22,8 @@ export interface Config {
|
||||
/**
|
||||
* GitlabDiscoveryEntityProvider configuration
|
||||
*/
|
||||
gitlab?: Record<
|
||||
string,
|
||||
{
|
||||
gitlab?: {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Required) Gitlab's host name.
|
||||
*/
|
||||
@@ -51,21 +50,21 @@ export interface Config {
|
||||
/**
|
||||
* (Optional) RegExp for the Project Name Pattern
|
||||
*/
|
||||
projectPattern?: RegExp;
|
||||
projectPattern?: string;
|
||||
/**
|
||||
* (Optional) RegExp for the User Name Pattern
|
||||
*/
|
||||
userPattern?: RegExp;
|
||||
userPattern?: string;
|
||||
/**
|
||||
* (Optional) RegExp for the Group Name Pattern
|
||||
*/
|
||||
groupPattern?: RegExp;
|
||||
groupPattern?: string;
|
||||
/**
|
||||
* (Optional) Skip forked repository
|
||||
*/
|
||||
skipForkedRepos?: boolean;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+4
-5
@@ -209,9 +209,8 @@ export interface Config {
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
| Record<
|
||||
string,
|
||||
{
|
||||
| {
|
||||
[name: string]: {
|
||||
/**
|
||||
* The prefix of the target that this matches on, e.g.
|
||||
* "https://graph.microsoft.com/v1.0", with no trailing slash.
|
||||
@@ -296,8 +295,8 @@ export interface Config {
|
||||
* (Optional) TaskScheduleDefinition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinitionConfig;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+4
-5
@@ -46,9 +46,8 @@ export interface Config {
|
||||
*/
|
||||
schedule?: TaskScheduleDefinition;
|
||||
}
|
||||
| Record<
|
||||
string,
|
||||
{
|
||||
| {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Required) The base URL of PuppetDB API instance.
|
||||
*/
|
||||
@@ -61,8 +60,8 @@ export interface Config {
|
||||
* (Optional) Task schedule definition for the refresh.
|
||||
*/
|
||||
schedule?: TaskScheduleDefinition;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
+4
-5
@@ -31,9 +31,8 @@ export interface Config {
|
||||
* Contains a record per topic for which an AWS SQS queue
|
||||
* should be used as source of events.
|
||||
*/
|
||||
topics: Record<
|
||||
string,
|
||||
{
|
||||
topics: {
|
||||
[name: string]: {
|
||||
/**
|
||||
* (Required) Queue-related configuration.
|
||||
*/
|
||||
@@ -69,8 +68,8 @@ export interface Config {
|
||||
* Default: 1 minute.
|
||||
*/
|
||||
waitTimeAfterEmptyReceive: HumanDuration;
|
||||
}
|
||||
>;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Vendored
+5
-5
@@ -42,13 +42,13 @@ export interface Config {
|
||||
username: string;
|
||||
/** @visibility secret */
|
||||
apiKey: string;
|
||||
extraRequestHeaders?: Partial<{
|
||||
extraRequestHeaders?: {
|
||||
/** @visibility secret */
|
||||
Authorization: string;
|
||||
Authorization?: string;
|
||||
/** @visibility secret */
|
||||
authorization: string;
|
||||
[key: string]: string;
|
||||
}>;
|
||||
authorization?: string;
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
Vendored
+7
-7
@@ -31,17 +31,17 @@ export interface Config {
|
||||
/**
|
||||
* Object with extra headers to be added to target requests.
|
||||
*/
|
||||
headers?: Partial<{
|
||||
headers?: {
|
||||
/** @visibility secret */
|
||||
Authorization: string;
|
||||
Authorization?: string;
|
||||
/** @visibility secret */
|
||||
authorization: string;
|
||||
authorization?: string;
|
||||
/** @visibility secret */
|
||||
'X-Api-Key': string;
|
||||
'X-Api-Key'?: string;
|
||||
/** @visibility secret */
|
||||
'x-api-key': string;
|
||||
[key: string]: string;
|
||||
}>;
|
||||
'x-api-key'?: string;
|
||||
[key: string]: string | undefined;
|
||||
};
|
||||
/**
|
||||
* Changes the origin of the host header to the target URL. Default: true.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user