@webpieces/code-rules 0.4.760 → 0.4.761
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/package.json +2 -2
- package/src/code-rules-config-table.js +5 -1
- package/src/code-rules-config-table.js.map +1 -1
- package/src/code-rules-engine.d.ts +4 -2
- package/src/code-rules-engine.js +31 -10
- package/src/code-rules-engine.js.map +1 -1
- package/src/index.d.ts +3 -2
- package/src/index.js +6 -1
- package/src/index.js.map +1 -1
- package/src/validate-ensure-we-are-secure.d.ts +17 -0
- package/src/validate-ensure-we-are-secure.js +395 -0
- package/src/validate-ensure-we-are-secure.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/code-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.761",
|
|
4
4
|
"description": "Standalone code validation rules extracted from architecture-validators, no Nx dependency required",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"directory": "packages/tooling/code-rules"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@webpieces/rules-config": "0.4.
|
|
19
|
+
"@webpieces/rules-config": "0.4.761",
|
|
20
20
|
"@inversifyjs/binding-decorators": "1.1.5",
|
|
21
21
|
"inversify": "7.10.4",
|
|
22
22
|
"reflect-metadata": "0.2.2"
|
|
@@ -27,8 +27,12 @@ exports.CONFIG_BINDINGS = [
|
|
|
27
27
|
[rules_config_1.NoStatePathsInTemplatesConfig, 'no-state-paths-in-templates'],
|
|
28
28
|
[rules_config_1.NoProcessExitOutsideMainConfig, 'no-process-exit-outside-main'],
|
|
29
29
|
[rules_config_1.NoFunctionOutsideClassConfig, 'no-function-outside-class'],
|
|
30
|
-
[
|
|
30
|
+
[
|
|
31
|
+
rules_config_1.InjectAnnotationNotNeededForConcreteClassConfig,
|
|
32
|
+
'inject-annotation-not-needed-for-concrete-class',
|
|
33
|
+
],
|
|
31
34
|
[rules_config_1.FrameworkTagConfig, 'framework-tag'],
|
|
32
35
|
[rules_config_1.RoleTagConfig, 'role-tag'],
|
|
36
|
+
[rules_config_1.EnsureWeAreSecureConfig, 'ensure-we-are-secure'],
|
|
33
37
|
];
|
|
34
38
|
//# sourceMappingURL=code-rules-config-table.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-rules-config-table.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/code-rules-config-table.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"code-rules-config-table.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/code-rules-config-table.ts"],"names":[],"mappings":";;;AAAA,0DAyBiC;AAKjC;;;;;GAKG;AACU,QAAA,eAAe,GAAqE;IAC7F,CAAC,mCAAoB,EAAE,kBAAkB,CAAC;IAC1C,CAAC,iCAAkB,EAAE,gBAAgB,CAAC;IACtC,CAAC,sCAAuB,EAAE,qBAAqB,CAAC;IAChD,CAAC,yCAA0B,EAAE,yBAAyB,CAAC;IACvD,CAAC,iCAAkB,EAAE,gBAAgB,CAAC;IACtC,CAAC,kCAAmB,EAAE,iBAAiB,CAAC;IACxC,CAAC,uCAAwB,EAAE,sBAAsB,CAAC;IAClD,CAAC,oCAAqB,EAAE,kBAAkB,CAAC;IAC3C,CAAC,kCAAmB,EAAE,gBAAgB,CAAC;IACvC,CAAC,sCAAuB,EAAE,qBAAqB,CAAC;IAChD,CAAC,0CAA2B,EAAE,yBAAyB,CAAC;IACxD,CAAC,iDAAkC,EAAE,mCAAmC,CAAC;IACzE,CAAC,qCAAsB,EAAE,qBAAqB,CAAC;IAC/C,CAAC,0DAA2C,EAAE,6CAA6C,CAAC;IAC5F,CAAC,gCAAiB,EAAE,eAAe,CAAC;IACpC,CAAC,4CAA6B,EAAE,6BAA6B,CAAC;IAC9D,CAAC,6CAA8B,EAAE,8BAA8B,CAAC;IAChE,CAAC,2CAA4B,EAAE,2BAA2B,CAAC;IAC3D;QACI,8DAA+C;QAC/C,iDAAiD;KACpD;IACD,CAAC,iCAAkB,EAAE,eAAe,CAAC;IACrC,CAAC,4BAAa,EAAE,UAAU,CAAC;IAC3B,CAAC,sCAAuB,EAAE,sBAAsB,CAAC;CACpD,CAAC","sourcesContent":["import {\n BaseRuleConfig,\n WebpiecesRulesConfig,\n MaxMethodLinesConfig,\n MaxFileLinesConfig,\n RequireReturnTypeConfig,\n NoInlineTypeLiteralsConfig,\n NoAnyUnknownConfig,\n NoImplicitAnyConfig,\n PrismaValidateDtosConfig,\n PrismaConverterConfig,\n NoDestructureConfig,\n CatchErrorPatternConfig,\n NoUnmanagedExceptionsConfig,\n AngularNoDirectApiInResolverConfig,\n NoSymbolDiTokensConfig,\n NoClientCreationOutsideServerOrClientConfig,\n NoCustomCssConfig,\n NoStatePathsInTemplatesConfig,\n NoProcessExitOutsideMainConfig,\n NoFunctionOutsideClassConfig,\n InjectAnnotationNotNeededForConcreteClassConfig,\n FrameworkTagConfig,\n RoleTagConfig,\n EnsureWeAreSecureConfig,\n} from '@webpieces/rules-config';\n\n/** A rule config class usable as an inversify inject-by-type token. */\nexport type ConfigCtor = new () => BaseRuleConfig;\n\n/**\n * The 1:1 table of `[config class, webpieces.config.json rule key]`. The composition root iterates it\n * to bind each rule's config into the container (`bind(ConfigClass).toConstantValue(config[key] ??\n * new ConfigClass())`), so every `@provideSingleton` validator injects its config by type. Data only\n * — the binding loop lives inline in the bin (an inline loop, exempt from no-function-outside-class).\n */\nexport const CONFIG_BINDINGS: ReadonlyArray<readonly [ConfigCtor, keyof WebpiecesRulesConfig]> = [\n [MaxMethodLinesConfig, 'max-method-lines'],\n [MaxFileLinesConfig, 'max-file-lines'],\n [RequireReturnTypeConfig, 'require-return-type'],\n [NoInlineTypeLiteralsConfig, 'no-inline-type-literals'],\n [NoAnyUnknownConfig, 'no-any-unknown'],\n [NoImplicitAnyConfig, 'no-implicit-any'],\n [PrismaValidateDtosConfig, 'prisma-validate-dtos'],\n [PrismaConverterConfig, 'prisma-converter'],\n [NoDestructureConfig, 'no-destructure'],\n [CatchErrorPatternConfig, 'catch-error-pattern'],\n [NoUnmanagedExceptionsConfig, 'no-unmanaged-exceptions'],\n [AngularNoDirectApiInResolverConfig, 'angular-no-direct-api-in-resolver'],\n [NoSymbolDiTokensConfig, 'no-symbol-di-tokens'],\n [NoClientCreationOutsideServerOrClientConfig, 'no-client-creation-outside-server-or-client'],\n [NoCustomCssConfig, 'no-custom-css'],\n [NoStatePathsInTemplatesConfig, 'no-state-paths-in-templates'],\n [NoProcessExitOutsideMainConfig, 'no-process-exit-outside-main'],\n [NoFunctionOutsideClassConfig, 'no-function-outside-class'],\n [\n InjectAnnotationNotNeededForConcreteClassConfig,\n 'inject-annotation-not-needed-for-concrete-class',\n ],\n [FrameworkTagConfig, 'framework-tag'],\n [RoleTagConfig, 'role-tag'],\n [EnsureWeAreSecureConfig, 'ensure-we-are-secure'],\n];\n"]}
|
|
@@ -23,6 +23,7 @@ import { NoFunctionOutsideClassValidator } from './validate-no-function-outside-
|
|
|
23
23
|
import { InjectAnnotationNotNeededForConcreteClassValidator } from './validate-inject-annotation-not-needed-for-concrete-class';
|
|
24
24
|
import { FrameworkTagValidator } from './validate-framework-tag';
|
|
25
25
|
import { RoleTagValidator } from './validate-role-tag';
|
|
26
|
+
import { EnsureWeAreSecureValidator } from './validate-ensure-we-are-secure';
|
|
26
27
|
/**
|
|
27
28
|
* Owns running the code-rules suite. Every built-in validator is injected as a singleton (its config
|
|
28
29
|
* is bound in the container at bootstrap), so this class IS the code-rules DI DAG the design graph
|
|
@@ -55,8 +56,9 @@ export declare class CodeRulesEngine {
|
|
|
55
56
|
private readonly injectAnnotationNotNeeded;
|
|
56
57
|
private readonly frameworkTag;
|
|
57
58
|
private readonly roleTag;
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
private readonly ensureWeAreSecure;
|
|
60
|
+
constructor(workspace: WorkspaceRoot, reporter: RuleReporter, matchRules: MatchRulesHolder, matchChecker: MatchRulesChecker, maxMethodLines: MaxMethodLinesValidator, maxFileLines: MaxFileLinesValidator, requireReturnType: RequireReturnTypeValidator, noInlineTypeLiterals: NoInlineTypeLiteralsValidator, noAnyUnknown: NoAnyUnknownValidator, noImplicitAny: NoImplicitAnyValidator, prismaValidateDtos: PrismaValidateDtosValidator, prismaConverter: PrismaConverterValidator, noDestructure: NoDestructureValidator, catchErrorPattern: CatchErrorPatternValidator, noUnmanagedExceptions: NoUnmanagedExceptionsValidator, noDirectApiResolver: NoDirectApiResolverValidator, noSymbolDiTokens: NoSymbolDiTokensValidator, noClientCreationOutsideServerOrClient: NoClientCreationOutsideServerOrClientValidator, noCustomCss: NoCustomCssValidator, noStatePathsInTemplates: NoStatePathsInTemplatesValidator, noProcessExitOutsideMain: NoProcessExitOutsideMainValidator, noFunctionOutsideClass: NoFunctionOutsideClassValidator, injectAnnotationNotNeeded: InjectAnnotationNotNeededForConcreteClassValidator, frameworkTag: FrameworkTagValidator, roleTag: RoleTagValidator, ensureWeAreSecure: EnsureWeAreSecureValidator);
|
|
61
|
+
/** The injected built-in validators, in run order. */
|
|
60
62
|
private builtIns;
|
|
61
63
|
/**
|
|
62
64
|
* Every ACTIVE run: an injected built-in validator whose `shouldRun()` is true, or an injected
|
package/src/code-rules-engine.js
CHANGED
|
@@ -28,6 +28,7 @@ const validate_no_function_outside_class_1 = require("./validate-no-function-out
|
|
|
28
28
|
const validate_inject_annotation_not_needed_for_concrete_class_1 = require("./validate-inject-annotation-not-needed-for-concrete-class");
|
|
29
29
|
const validate_framework_tag_1 = require("./validate-framework-tag");
|
|
30
30
|
const validate_role_tag_1 = require("./validate-role-tag");
|
|
31
|
+
const validate_ensure_we_are_secure_1 = require("./validate-ensure-we-are-secure");
|
|
31
32
|
/**
|
|
32
33
|
* Owns running the code-rules suite. Every built-in validator is injected as a singleton (its config
|
|
33
34
|
* is bound in the container at bootstrap), so this class IS the code-rules DI DAG the design graph
|
|
@@ -60,8 +61,9 @@ let CodeRulesEngine = class CodeRulesEngine {
|
|
|
60
61
|
injectAnnotationNotNeeded;
|
|
61
62
|
frameworkTag;
|
|
62
63
|
roleTag;
|
|
64
|
+
ensureWeAreSecure;
|
|
63
65
|
// webpieces-disable max-lines-new-methods -- the built-in validator set is flat; each is one injected field
|
|
64
|
-
constructor(workspace, reporter, matchRules, matchChecker, maxMethodLines, maxFileLines, requireReturnType, noInlineTypeLiterals, noAnyUnknown, noImplicitAny, prismaValidateDtos, prismaConverter, noDestructure, catchErrorPattern, noUnmanagedExceptions, noDirectApiResolver, noSymbolDiTokens, noClientCreationOutsideServerOrClient, noCustomCss, noStatePathsInTemplates, noProcessExitOutsideMain, noFunctionOutsideClass, injectAnnotationNotNeeded, frameworkTag, roleTag) {
|
|
66
|
+
constructor(workspace, reporter, matchRules, matchChecker, maxMethodLines, maxFileLines, requireReturnType, noInlineTypeLiterals, noAnyUnknown, noImplicitAny, prismaValidateDtos, prismaConverter, noDestructure, catchErrorPattern, noUnmanagedExceptions, noDirectApiResolver, noSymbolDiTokens, noClientCreationOutsideServerOrClient, noCustomCss, noStatePathsInTemplates, noProcessExitOutsideMain, noFunctionOutsideClass, injectAnnotationNotNeeded, frameworkTag, roleTag, ensureWeAreSecure) {
|
|
65
67
|
this.workspace = workspace;
|
|
66
68
|
this.reporter = reporter;
|
|
67
69
|
this.matchRules = matchRules;
|
|
@@ -87,17 +89,33 @@ let CodeRulesEngine = class CodeRulesEngine {
|
|
|
87
89
|
this.injectAnnotationNotNeeded = injectAnnotationNotNeeded;
|
|
88
90
|
this.frameworkTag = frameworkTag;
|
|
89
91
|
this.roleTag = roleTag;
|
|
92
|
+
this.ensureWeAreSecure = ensureWeAreSecure;
|
|
90
93
|
}
|
|
91
|
-
/** The
|
|
94
|
+
/** The injected built-in validators, in run order. */
|
|
92
95
|
builtIns() {
|
|
93
96
|
return [
|
|
94
|
-
this.maxMethodLines,
|
|
95
|
-
this.
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
97
|
+
this.maxMethodLines,
|
|
98
|
+
this.maxFileLines,
|
|
99
|
+
this.requireReturnType,
|
|
100
|
+
this.noInlineTypeLiterals,
|
|
101
|
+
this.noAnyUnknown,
|
|
102
|
+
this.noImplicitAny,
|
|
103
|
+
this.prismaValidateDtos,
|
|
104
|
+
this.prismaConverter,
|
|
105
|
+
this.noDestructure,
|
|
106
|
+
this.catchErrorPattern,
|
|
107
|
+
this.noUnmanagedExceptions,
|
|
108
|
+
this.noDirectApiResolver,
|
|
109
|
+
this.noSymbolDiTokens,
|
|
110
|
+
this.noClientCreationOutsideServerOrClient,
|
|
111
|
+
this.noCustomCss,
|
|
98
112
|
this.noStatePathsInTemplates,
|
|
99
|
-
this.noProcessExitOutsideMain,
|
|
100
|
-
this.
|
|
113
|
+
this.noProcessExitOutsideMain,
|
|
114
|
+
this.noFunctionOutsideClass,
|
|
115
|
+
this.injectAnnotationNotNeeded,
|
|
116
|
+
this.frameworkTag,
|
|
117
|
+
this.roleTag,
|
|
118
|
+
this.ensureWeAreSecure,
|
|
101
119
|
];
|
|
102
120
|
}
|
|
103
121
|
/**
|
|
@@ -132,7 +150,9 @@ let CodeRulesEngine = class CodeRulesEngine {
|
|
|
132
150
|
console.log(` Active rules: ${runs.map((r) => r.name).join(', ')}`);
|
|
133
151
|
console.log('');
|
|
134
152
|
const result = await this.reporter.runValidators(runs);
|
|
135
|
-
console.log(result.success
|
|
153
|
+
console.log(result.success
|
|
154
|
+
? '\n✅ All code validations passed\n'
|
|
155
|
+
: '\n❌ Some code validations failed\n');
|
|
136
156
|
return result;
|
|
137
157
|
}
|
|
138
158
|
};
|
|
@@ -163,6 +183,7 @@ exports.CodeRulesEngine = CodeRulesEngine = tslib_1.__decorate([
|
|
|
163
183
|
validate_no_function_outside_class_1.NoFunctionOutsideClassValidator,
|
|
164
184
|
validate_inject_annotation_not_needed_for_concrete_class_1.InjectAnnotationNotNeededForConcreteClassValidator,
|
|
165
185
|
validate_framework_tag_1.FrameworkTagValidator,
|
|
166
|
-
validate_role_tag_1.RoleTagValidator
|
|
186
|
+
validate_role_tag_1.RoleTagValidator,
|
|
187
|
+
validate_ensure_we_are_secure_1.EnsureWeAreSecureValidator])
|
|
167
188
|
], CodeRulesEngine);
|
|
168
189
|
//# sourceMappingURL=code-rules-engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-rules-engine.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/code-rules-engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseRuleConfig } from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { CodeValidator, ExecutorResult, RuleRun } from './code-validator';\nimport { RuleReporter } from './rule-reporter';\nimport { WorkspaceRoot, MatchRulesHolder } from './code-rules-context';\nimport { MatchRulesChecker } from './validate-match-rules';\nimport { MaxMethodLinesValidator } from './validate-modified-methods';\nimport { MaxFileLinesValidator } from './validate-modified-files';\nimport { RequireReturnTypeValidator } from './validate-return-types';\nimport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nimport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nimport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nimport { PrismaValidateDtosValidator } from './validate-dtos';\nimport { PrismaConverterValidator } from './validate-prisma-converters';\nimport { NoDestructureValidator } from './validate-no-destructure';\nimport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nimport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nimport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nimport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nimport { NoClientCreationOutsideServerOrClientValidator } from './validate-no-client-creation-outside-server-or-client';\nimport { NoCustomCssValidator } from './validate-no-custom-css';\nimport { NoStatePathsInTemplatesValidator } from './validate-no-state-paths-in-templates';\nimport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nimport { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';\nimport { InjectAnnotationNotNeededForConcreteClassValidator } from './validate-inject-annotation-not-needed-for-concrete-class';\nimport { FrameworkTagValidator } from './validate-framework-tag';\nimport { RoleTagValidator } from './validate-role-tag';\n\n/**\n * Owns running the code-rules suite. Every built-in validator is injected as a singleton (its config\n * is bound in the container at bootstrap), so this class IS the code-rules DI DAG the design graph\n * draws. The per-entry match-rules validators are the one exception — they are config-per-instance,\n * so they are built from the injected {@link MatchRulesHolder}.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class CodeRulesEngine {\n // webpieces-disable max-lines-new-methods -- the built-in validator set is flat; each is one injected field\n constructor(\n private readonly workspace: WorkspaceRoot,\n private readonly reporter: RuleReporter,\n private readonly matchRules: MatchRulesHolder,\n private readonly matchChecker: MatchRulesChecker,\n private readonly maxMethodLines: MaxMethodLinesValidator,\n private readonly maxFileLines: MaxFileLinesValidator,\n private readonly requireReturnType: RequireReturnTypeValidator,\n private readonly noInlineTypeLiterals: NoInlineTypeLiteralsValidator,\n private readonly noAnyUnknown: NoAnyUnknownValidator,\n private readonly noImplicitAny: NoImplicitAnyValidator,\n private readonly prismaValidateDtos: PrismaValidateDtosValidator,\n private readonly prismaConverter: PrismaConverterValidator,\n private readonly noDestructure: NoDestructureValidator,\n private readonly catchErrorPattern: CatchErrorPatternValidator,\n private readonly noUnmanagedExceptions: NoUnmanagedExceptionsValidator,\n private readonly noDirectApiResolver: NoDirectApiResolverValidator,\n private readonly noSymbolDiTokens: NoSymbolDiTokensValidator,\n private readonly noClientCreationOutsideServerOrClient: NoClientCreationOutsideServerOrClientValidator,\n private readonly noCustomCss: NoCustomCssValidator,\n private readonly noStatePathsInTemplates: NoStatePathsInTemplatesValidator,\n private readonly noProcessExitOutsideMain: NoProcessExitOutsideMainValidator,\n private readonly noFunctionOutsideClass: NoFunctionOutsideClassValidator,\n private readonly injectAnnotationNotNeeded: InjectAnnotationNotNeededForConcreteClassValidator,\n private readonly frameworkTag: FrameworkTagValidator,\n private readonly roleTag: RoleTagValidator,\n ) {}\n\n /** The 21 injected built-in validators, in run order. */\n private builtIns(): CodeValidator<BaseRuleConfig>[] {\n return [\n this.maxMethodLines, this.maxFileLines, this.requireReturnType, this.noInlineTypeLiterals,\n this.noAnyUnknown, this.noImplicitAny, this.prismaValidateDtos, this.prismaConverter,\n this.noDestructure, this.catchErrorPattern, this.noUnmanagedExceptions, this.noDirectApiResolver,\n this.noSymbolDiTokens, this.noClientCreationOutsideServerOrClient, this.noCustomCss,\n this.noStatePathsInTemplates,\n this.noProcessExitOutsideMain, this.noFunctionOutsideClass,\n this.injectAnnotationNotNeeded, this.frameworkTag, this.roleTag,\n ];\n }\n\n /**\n * Every ACTIVE run: an injected built-in validator whose `shouldRun()` is true, or an injected\n * match-rule check per configured entry that is active. Built as {@link RuleRun} value objects\n * (name + thunk) so NO DAG member is `new`-ed — the validators and the checker are injected.\n */\n private activeRuns(root: string): RuleRun[] {\n const runs: RuleRun[] = [];\n for (const v of this.builtIns()) {\n if (v.shouldRun()) runs.push(new RuleRun(v.name, () => v.run(root)));\n }\n for (const mr of this.matchRules.rules) {\n if (this.matchChecker.shouldRun(mr)) runs.push(new RuleRun(mr.name, () => this.matchChecker.runForConfig(mr, root)));\n }\n return runs;\n }\n\n /**\n * Run all configured code validators against the workspace. A validator runs only when\n * `shouldRun()` is true (mode not OFF and no branch/epoch escape hatch). Per-run isolation lives\n * in {@link RuleReporter} so one validator can never abort the rest.\n */\n async run(): Promise<ExecutorResult> {\n const runs = this.activeRuns(this.workspace.path);\n if (runs.length === 0) {\n console.log('\\n⏭️ Skipping all code validations (all modes: OFF)\\n');\n return { success: true };\n }\n\n console.log('\\n📏 Running Code Validations\\n');\n console.log(` Active rules: ${runs.map((r: RuleRun) => r.name).join(', ')}`);\n console.log('');\n\n const result = await this.reporter.runValidators(runs);\n console.log(result.success ? '\\n✅ All code validations passed\\n' : '\\n❌ Some code validations failed\\n');\n return result;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"code-rules-engine.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/code-rules-engine.ts"],"names":[],"mappings":"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","sourcesContent":["import { BaseRuleConfig } from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\n\nimport { CodeValidator, ExecutorResult, RuleRun } from './code-validator';\nimport { RuleReporter } from './rule-reporter';\nimport { WorkspaceRoot, MatchRulesHolder } from './code-rules-context';\nimport { MatchRulesChecker } from './validate-match-rules';\nimport { MaxMethodLinesValidator } from './validate-modified-methods';\nimport { MaxFileLinesValidator } from './validate-modified-files';\nimport { RequireReturnTypeValidator } from './validate-return-types';\nimport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nimport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nimport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nimport { PrismaValidateDtosValidator } from './validate-dtos';\nimport { PrismaConverterValidator } from './validate-prisma-converters';\nimport { NoDestructureValidator } from './validate-no-destructure';\nimport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nimport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nimport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nimport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nimport { NoClientCreationOutsideServerOrClientValidator } from './validate-no-client-creation-outside-server-or-client';\nimport { NoCustomCssValidator } from './validate-no-custom-css';\nimport { NoStatePathsInTemplatesValidator } from './validate-no-state-paths-in-templates';\nimport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nimport { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';\nimport { InjectAnnotationNotNeededForConcreteClassValidator } from './validate-inject-annotation-not-needed-for-concrete-class';\nimport { FrameworkTagValidator } from './validate-framework-tag';\nimport { RoleTagValidator } from './validate-role-tag';\nimport { EnsureWeAreSecureValidator } from './validate-ensure-we-are-secure';\n\n/**\n * Owns running the code-rules suite. Every built-in validator is injected as a singleton (its config\n * is bound in the container at bootstrap), so this class IS the code-rules DI DAG the design graph\n * draws. The per-entry match-rules validators are the one exception — they are config-per-instance,\n * so they are built from the injected {@link MatchRulesHolder}.\n */\n@injectable(bindingScopeValues.Singleton)\nexport class CodeRulesEngine {\n // webpieces-disable max-lines-new-methods -- the built-in validator set is flat; each is one injected field\n constructor(\n private readonly workspace: WorkspaceRoot,\n private readonly reporter: RuleReporter,\n private readonly matchRules: MatchRulesHolder,\n private readonly matchChecker: MatchRulesChecker,\n private readonly maxMethodLines: MaxMethodLinesValidator,\n private readonly maxFileLines: MaxFileLinesValidator,\n private readonly requireReturnType: RequireReturnTypeValidator,\n private readonly noInlineTypeLiterals: NoInlineTypeLiteralsValidator,\n private readonly noAnyUnknown: NoAnyUnknownValidator,\n private readonly noImplicitAny: NoImplicitAnyValidator,\n private readonly prismaValidateDtos: PrismaValidateDtosValidator,\n private readonly prismaConverter: PrismaConverterValidator,\n private readonly noDestructure: NoDestructureValidator,\n private readonly catchErrorPattern: CatchErrorPatternValidator,\n private readonly noUnmanagedExceptions: NoUnmanagedExceptionsValidator,\n private readonly noDirectApiResolver: NoDirectApiResolverValidator,\n private readonly noSymbolDiTokens: NoSymbolDiTokensValidator,\n private readonly noClientCreationOutsideServerOrClient: NoClientCreationOutsideServerOrClientValidator,\n private readonly noCustomCss: NoCustomCssValidator,\n private readonly noStatePathsInTemplates: NoStatePathsInTemplatesValidator,\n private readonly noProcessExitOutsideMain: NoProcessExitOutsideMainValidator,\n private readonly noFunctionOutsideClass: NoFunctionOutsideClassValidator,\n private readonly injectAnnotationNotNeeded: InjectAnnotationNotNeededForConcreteClassValidator,\n private readonly frameworkTag: FrameworkTagValidator,\n private readonly roleTag: RoleTagValidator,\n private readonly ensureWeAreSecure: EnsureWeAreSecureValidator,\n ) {}\n\n /** The injected built-in validators, in run order. */\n private builtIns(): CodeValidator<BaseRuleConfig>[] {\n return [\n this.maxMethodLines,\n this.maxFileLines,\n this.requireReturnType,\n this.noInlineTypeLiterals,\n this.noAnyUnknown,\n this.noImplicitAny,\n this.prismaValidateDtos,\n this.prismaConverter,\n this.noDestructure,\n this.catchErrorPattern,\n this.noUnmanagedExceptions,\n this.noDirectApiResolver,\n this.noSymbolDiTokens,\n this.noClientCreationOutsideServerOrClient,\n this.noCustomCss,\n this.noStatePathsInTemplates,\n this.noProcessExitOutsideMain,\n this.noFunctionOutsideClass,\n this.injectAnnotationNotNeeded,\n this.frameworkTag,\n this.roleTag,\n this.ensureWeAreSecure,\n ];\n }\n\n /**\n * Every ACTIVE run: an injected built-in validator whose `shouldRun()` is true, or an injected\n * match-rule check per configured entry that is active. Built as {@link RuleRun} value objects\n * (name + thunk) so NO DAG member is `new`-ed — the validators and the checker are injected.\n */\n private activeRuns(root: string): RuleRun[] {\n const runs: RuleRun[] = [];\n for (const v of this.builtIns()) {\n if (v.shouldRun()) runs.push(new RuleRun(v.name, () => v.run(root)));\n }\n for (const mr of this.matchRules.rules) {\n if (this.matchChecker.shouldRun(mr))\n runs.push(new RuleRun(mr.name, () => this.matchChecker.runForConfig(mr, root)));\n }\n return runs;\n }\n\n /**\n * Run all configured code validators against the workspace. A validator runs only when\n * `shouldRun()` is true (mode not OFF and no branch/epoch escape hatch). Per-run isolation lives\n * in {@link RuleReporter} so one validator can never abort the rest.\n */\n async run(): Promise<ExecutorResult> {\n const runs = this.activeRuns(this.workspace.path);\n if (runs.length === 0) {\n console.log('\\n⏭️ Skipping all code validations (all modes: OFF)\\n');\n return { success: true };\n }\n\n console.log('\\n📏 Running Code Validations\\n');\n console.log(` Active rules: ${runs.map((r: RuleRun) => r.name).join(', ')}`);\n console.log('');\n\n const result = await this.reporter.runValidators(runs);\n console.log(\n result.success\n ? '\\n✅ All code validations passed\\n'\n : '\\n❌ Some code validations failed\\n',\n );\n return result;\n }\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -12,13 +12,14 @@ export { NoDestructureValidator } from './validate-no-destructure';
|
|
|
12
12
|
export { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';
|
|
13
13
|
export { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';
|
|
14
14
|
export { NoClientCreationOutsideServerOrClientValidator } from './validate-no-client-creation-outside-server-or-client';
|
|
15
|
-
export { NoStatePathsInTemplatesValidator, StatePathHit, StatePathViolation } from './validate-no-state-paths-in-templates';
|
|
15
|
+
export { NoStatePathsInTemplatesValidator, StatePathHit, StatePathViolation, } from './validate-no-state-paths-in-templates';
|
|
16
16
|
export { ProjectRoleResolver } from './project-role-resolver';
|
|
17
17
|
export { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';
|
|
18
18
|
export { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';
|
|
19
|
-
export { InjectAnnotationNotNeededForConcreteClassValidator, findRedundantInjectInSource } from './validate-inject-annotation-not-needed-for-concrete-class';
|
|
19
|
+
export { InjectAnnotationNotNeededForConcreteClassValidator, findRedundantInjectInSource, } from './validate-inject-annotation-not-needed-for-concrete-class';
|
|
20
20
|
export { FrameworkTagValidator } from './validate-framework-tag';
|
|
21
21
|
export { RoleTagValidator } from './validate-role-tag';
|
|
22
|
+
export { EnsureWeAreSecureValidator, SecurityContractViolation, auditSecurityContracts, findDirectlyChangedProjectRoots, } from './validate-ensure-we-are-secure';
|
|
22
23
|
export { PrismaValidateDtosValidator } from './validate-dtos';
|
|
23
24
|
export { PrismaConverterValidator } from './validate-prisma-converters';
|
|
24
25
|
export { default as validateCode } from './validate-code';
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ReactNativeCompatibility = exports.validateCode = exports.PrismaConverterValidator = exports.PrismaValidateDtosValidator = exports.RoleTagValidator = exports.FrameworkTagValidator = exports.findRedundantInjectInSource = exports.InjectAnnotationNotNeededForConcreteClassValidator = exports.NoFunctionOutsideClassValidator = exports.NoProcessExitOutsideMainValidator = exports.ProjectRoleResolver = exports.StatePathViolation = exports.StatePathHit = exports.NoStatePathsInTemplatesValidator = exports.NoClientCreationOutsideServerOrClientValidator = exports.NoSymbolDiTokensValidator = exports.NoDirectApiResolverValidator = exports.NoDestructureValidator = exports.NoUnmanagedExceptionsValidator = exports.CatchErrorPatternValidator = exports.NoInlineTypeLiteralsValidator = exports.RequireReturnTypeValidator = exports.MaxFileLinesValidator = exports.runNewMethods = exports.runModifiedMethods = exports.MaxMethodLinesValidator = exports.NoImplicitAnyValidator = exports.NoAnyUnknownValidator = exports.CodeValidator = void 0;
|
|
3
|
+
exports.ReactNativeCompatibility = exports.validateCode = exports.PrismaConverterValidator = exports.PrismaValidateDtosValidator = exports.findDirectlyChangedProjectRoots = exports.auditSecurityContracts = exports.SecurityContractViolation = exports.EnsureWeAreSecureValidator = exports.RoleTagValidator = exports.FrameworkTagValidator = exports.findRedundantInjectInSource = exports.InjectAnnotationNotNeededForConcreteClassValidator = exports.NoFunctionOutsideClassValidator = exports.NoProcessExitOutsideMainValidator = exports.ProjectRoleResolver = exports.StatePathViolation = exports.StatePathHit = exports.NoStatePathsInTemplatesValidator = exports.NoClientCreationOutsideServerOrClientValidator = exports.NoSymbolDiTokensValidator = exports.NoDirectApiResolverValidator = exports.NoDestructureValidator = exports.NoUnmanagedExceptionsValidator = exports.CatchErrorPatternValidator = exports.NoInlineTypeLiteralsValidator = exports.RequireReturnTypeValidator = exports.MaxFileLinesValidator = exports.runNewMethods = exports.runModifiedMethods = exports.MaxMethodLinesValidator = exports.NoImplicitAnyValidator = exports.NoAnyUnknownValidator = exports.CodeValidator = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
var code_validator_1 = require("./code-validator");
|
|
6
6
|
Object.defineProperty(exports, "CodeValidator", { enumerable: true, get: function () { return code_validator_1.CodeValidator; } });
|
|
@@ -48,6 +48,11 @@ var validate_framework_tag_1 = require("./validate-framework-tag");
|
|
|
48
48
|
Object.defineProperty(exports, "FrameworkTagValidator", { enumerable: true, get: function () { return validate_framework_tag_1.FrameworkTagValidator; } });
|
|
49
49
|
var validate_role_tag_1 = require("./validate-role-tag");
|
|
50
50
|
Object.defineProperty(exports, "RoleTagValidator", { enumerable: true, get: function () { return validate_role_tag_1.RoleTagValidator; } });
|
|
51
|
+
var validate_ensure_we_are_secure_1 = require("./validate-ensure-we-are-secure");
|
|
52
|
+
Object.defineProperty(exports, "EnsureWeAreSecureValidator", { enumerable: true, get: function () { return validate_ensure_we_are_secure_1.EnsureWeAreSecureValidator; } });
|
|
53
|
+
Object.defineProperty(exports, "SecurityContractViolation", { enumerable: true, get: function () { return validate_ensure_we_are_secure_1.SecurityContractViolation; } });
|
|
54
|
+
Object.defineProperty(exports, "auditSecurityContracts", { enumerable: true, get: function () { return validate_ensure_we_are_secure_1.auditSecurityContracts; } });
|
|
55
|
+
Object.defineProperty(exports, "findDirectlyChangedProjectRoots", { enumerable: true, get: function () { return validate_ensure_we_are_secure_1.findDirectlyChangedProjectRoots; } });
|
|
51
56
|
var validate_dtos_1 = require("./validate-dtos");
|
|
52
57
|
Object.defineProperty(exports, "PrismaValidateDtosValidator", { enumerable: true, get: function () { return validate_dtos_1.PrismaValidateDtosValidator; } });
|
|
53
58
|
var validate_prisma_converters_1 = require("./validate-prisma-converters");
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,mDAAiE;AAAxD,+GAAA,aAAa,OAAA;AAEtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,uEAAoE;AAA3D,kIAAA,sBAAsB,OAAA;AAC/B,yEAA0F;AAAjF,oIAAA,uBAAuB,OAAA;AAAE,+HAAA,kBAAkB,OAAA;AACpD,+DAAuD;AAA9C,qHAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,mIAAA,0BAA0B,OAAA;AACnC,uEAA2E;AAAlE,yIAAA,6BAA6B,OAAA;AACtC,+EAA4E;AAAnE,0IAAA,0BAA0B,OAAA;AACnC,uFAAoF;AAA3E,kJAAA,8BAA8B,OAAA;AACvC,qEAAmE;AAA1D,iIAAA,sBAAsB,OAAA;AAC/B,qFAAiF;AAAxE,+IAAA,4BAA4B,OAAA;AACrC,+EAA2E;AAAlE,yIAAA,yBAAyB,OAAA;AAClC,+HAAwH;AAA/G,sLAAA,8CAA8C,OAAA;AACvD,+
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,mDAAiE;AAAxD,+GAAA,aAAa,OAAA;AAEtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,uEAAoE;AAA3D,kIAAA,sBAAsB,OAAA;AAC/B,yEAA0F;AAAjF,oIAAA,uBAAuB,OAAA;AAAE,+HAAA,kBAAkB,OAAA;AACpD,+DAAuD;AAA9C,qHAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iEAAqE;AAA5D,mIAAA,0BAA0B,OAAA;AACnC,uEAA2E;AAAlE,yIAAA,6BAA6B,OAAA;AACtC,+EAA4E;AAAnE,0IAAA,0BAA0B,OAAA;AACnC,uFAAoF;AAA3E,kJAAA,8BAA8B,OAAA;AACvC,qEAAmE;AAA1D,iIAAA,sBAAsB,OAAA;AAC/B,qFAAiF;AAAxE,+IAAA,4BAA4B,OAAA;AACrC,+EAA2E;AAAlE,yIAAA,yBAAyB,OAAA;AAClC,+HAAwH;AAA/G,sLAAA,8CAA8C,OAAA;AACvD,+FAIgD;AAH5C,wJAAA,gCAAgC,OAAA;AAChC,oIAAA,YAAY,OAAA;AACZ,0IAAA,kBAAkB,OAAA;AAEtB,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAC5B,iGAA4F;AAAnF,0JAAA,iCAAiC,OAAA;AAC1C,2FAAuF;AAA9E,qJAAA,+BAA+B,OAAA;AACxC,uIAGoE;AAFhE,8LAAA,kDAAkD,OAAA;AAClD,uKAAA,2BAA2B,OAAA;AAE/B,mEAAiE;AAAxD,+HAAA,qBAAqB,OAAA;AAC9B,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,iFAKyC;AAJrC,2IAAA,0BAA0B,OAAA;AAC1B,0IAAA,yBAAyB,OAAA;AACzB,uIAAA,sBAAsB,OAAA;AACtB,gJAAA,+BAA+B,OAAA;AAEnC,iDAA8D;AAArD,4HAAA,2BAA2B,OAAA;AACpC,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA;AACjC,iDAA0D;AAAjD,sIAAA,OAAO,OAAgB;AAEhC,2EAAwE;AAA/D,sIAAA,wBAAwB,OAAA","sourcesContent":["export { CodeValidator, ExecutorResult } from './code-validator';\n\nexport { NoAnyUnknownValidator } from './validate-no-any-unknown';\nexport { NoImplicitAnyValidator } from './validate-no-implicit-any';\nexport { MaxMethodLinesValidator, runModifiedMethods } from './validate-modified-methods';\nexport { runNewMethods } from './validate-new-methods';\nexport { MaxFileLinesValidator } from './validate-modified-files';\nexport { RequireReturnTypeValidator } from './validate-return-types';\nexport { NoInlineTypeLiteralsValidator } from './validate-no-inline-types';\nexport { CatchErrorPatternValidator } from './validate-catch-error-pattern';\nexport { NoUnmanagedExceptionsValidator } from './validate-no-unmanaged-exceptions';\nexport { NoDestructureValidator } from './validate-no-destructure';\nexport { NoDirectApiResolverValidator } from './validate-no-direct-api-resolver';\nexport { NoSymbolDiTokensValidator } from './validate-no-symbol-di-tokens';\nexport { NoClientCreationOutsideServerOrClientValidator } from './validate-no-client-creation-outside-server-or-client';\nexport {\n NoStatePathsInTemplatesValidator,\n StatePathHit,\n StatePathViolation,\n} from './validate-no-state-paths-in-templates';\nexport { ProjectRoleResolver } from './project-role-resolver';\nexport { NoProcessExitOutsideMainValidator } from './validate-no-process-exit-outside-main';\nexport { NoFunctionOutsideClassValidator } from './validate-no-function-outside-class';\nexport {\n InjectAnnotationNotNeededForConcreteClassValidator,\n findRedundantInjectInSource,\n} from './validate-inject-annotation-not-needed-for-concrete-class';\nexport { FrameworkTagValidator } from './validate-framework-tag';\nexport { RoleTagValidator } from './validate-role-tag';\nexport {\n EnsureWeAreSecureValidator,\n SecurityContractViolation,\n auditSecurityContracts,\n findDirectlyChangedProjectRoots,\n} from './validate-ensure-we-are-secure';\nexport { PrismaValidateDtosValidator } from './validate-dtos';\nexport { PrismaConverterValidator } from './validate-prisma-converters';\nexport { default as validateCode } from './validate-code';\n\nexport { ReactNativeCompatibility } from './react-native-compatibility';\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EnsureWeAreSecureConfig, InformAiError, RuleFailError } from '@webpieces/rules-config';
|
|
2
|
+
import { CodeValidator, ExecutorResult } from './code-validator';
|
|
3
|
+
export declare class SecurityContractViolation {
|
|
4
|
+
readonly file: string;
|
|
5
|
+
readonly line: number;
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly message: string;
|
|
8
|
+
constructor(file: string, line: number, code: string, message: string);
|
|
9
|
+
}
|
|
10
|
+
export declare function findDirectlyChangedProjectRoots(workspaceRoot: string, changedFiles: string[]): string[];
|
|
11
|
+
export declare function auditSecurityContracts(workspaceRoot: string, projectRoots: string[]): SecurityContractViolation[];
|
|
12
|
+
export declare function securityContractsError(violations: readonly SecurityContractViolation[]): RuleFailError;
|
|
13
|
+
export declare function missingSecurityBaseError(): InformAiError;
|
|
14
|
+
export declare class EnsureWeAreSecureValidator extends CodeValidator<EnsureWeAreSecureConfig> {
|
|
15
|
+
constructor(config: EnsureWeAreSecureConfig);
|
|
16
|
+
run(workspaceRoot: string): Promise<ExecutorResult>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EnsureWeAreSecureValidator = exports.SecurityContractViolation = void 0;
|
|
4
|
+
exports.findDirectlyChangedProjectRoots = findDirectlyChangedProjectRoots;
|
|
5
|
+
exports.auditSecurityContracts = auditSecurityContracts;
|
|
6
|
+
exports.securityContractsError = securityContractsError;
|
|
7
|
+
exports.missingSecurityBaseError = missingSecurityBaseError;
|
|
8
|
+
const tslib_1 = require("tslib");
|
|
9
|
+
/** Audits explicit HTTP authentication and IPC-only contracts in directly changed projects. */
|
|
10
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
11
|
+
const path = tslib_1.__importStar(require("path"));
|
|
12
|
+
const ts = tslib_1.__importStar(require("typescript"));
|
|
13
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
14
|
+
const inversify_1 = require("inversify");
|
|
15
|
+
const code_validator_1 = require("./code-validator");
|
|
16
|
+
const HTTP_AUTH = new Set([
|
|
17
|
+
'WpAuthJwt',
|
|
18
|
+
'WpAuthOidc',
|
|
19
|
+
'WpAuthWebhook',
|
|
20
|
+
'WpAuthSharedSecret',
|
|
21
|
+
'WpAuthApiKey',
|
|
22
|
+
'WpAuthLocalOnly',
|
|
23
|
+
'WpAuthPublic',
|
|
24
|
+
]);
|
|
25
|
+
const CONTRACT_DECORATORS = new Set([
|
|
26
|
+
...HTTP_AUTH,
|
|
27
|
+
'ApiPath',
|
|
28
|
+
'Endpoint',
|
|
29
|
+
'WpInternal',
|
|
30
|
+
'WpIpcEndpoint',
|
|
31
|
+
]);
|
|
32
|
+
class SecurityContractViolation {
|
|
33
|
+
file;
|
|
34
|
+
line;
|
|
35
|
+
code;
|
|
36
|
+
message;
|
|
37
|
+
constructor(file, line, code, message) {
|
|
38
|
+
this.file = file;
|
|
39
|
+
this.line = line;
|
|
40
|
+
this.code = code;
|
|
41
|
+
this.message = message;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.SecurityContractViolation = SecurityContractViolation;
|
|
45
|
+
class DecoratorUse {
|
|
46
|
+
name;
|
|
47
|
+
decorator;
|
|
48
|
+
call;
|
|
49
|
+
constructor(name, decorator, call) {
|
|
50
|
+
this.name = name;
|
|
51
|
+
this.decorator = decorator;
|
|
52
|
+
this.call = call;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
class CanonicalImports {
|
|
56
|
+
named = new Map();
|
|
57
|
+
namespaces = new Set();
|
|
58
|
+
}
|
|
59
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
60
|
+
function isCanonicalModule(moduleName) {
|
|
61
|
+
return (moduleName === '@webpieces/core-util' ||
|
|
62
|
+
moduleName === '@webpieces/core-util/ipc' ||
|
|
63
|
+
moduleName.endsWith('/core-util/src/http/decorators') ||
|
|
64
|
+
moduleName.includes('/core-util/src/ipc/'));
|
|
65
|
+
}
|
|
66
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
67
|
+
function importsOf(source) {
|
|
68
|
+
const result = new CanonicalImports();
|
|
69
|
+
for (const statement of source.statements) {
|
|
70
|
+
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))
|
|
71
|
+
continue;
|
|
72
|
+
if (!isCanonicalModule(statement.moduleSpecifier.text))
|
|
73
|
+
continue;
|
|
74
|
+
const bindings = statement.importClause?.namedBindings;
|
|
75
|
+
if (bindings && ts.isNamedImports(bindings)) {
|
|
76
|
+
for (const item of bindings.elements) {
|
|
77
|
+
const exported = item.propertyName?.text ?? item.name.text;
|
|
78
|
+
if (CONTRACT_DECORATORS.has(exported))
|
|
79
|
+
result.named.set(item.name.text, exported);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else if (bindings && ts.isNamespaceImport(bindings)) {
|
|
83
|
+
result.namespaces.add(bindings.name.text);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
}
|
|
88
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
89
|
+
function decoratorUse(decorator, imports) {
|
|
90
|
+
const expr = decorator.expression;
|
|
91
|
+
const call = ts.isCallExpression(expr) ? expr : null;
|
|
92
|
+
const callee = call?.expression ?? expr;
|
|
93
|
+
if (ts.isIdentifier(callee)) {
|
|
94
|
+
const canonical = imports.named.get(callee.text);
|
|
95
|
+
return canonical ? new DecoratorUse(canonical, decorator, call) : null;
|
|
96
|
+
}
|
|
97
|
+
if (ts.isPropertyAccessExpression(callee) &&
|
|
98
|
+
ts.isIdentifier(callee.expression) &&
|
|
99
|
+
imports.namespaces.has(callee.expression.text)) {
|
|
100
|
+
return CONTRACT_DECORATORS.has(callee.name.text)
|
|
101
|
+
? new DecoratorUse(callee.name.text, decorator, call)
|
|
102
|
+
: null;
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
107
|
+
function decoratorsOf(node, imports) {
|
|
108
|
+
if (!ts.canHaveDecorators(node))
|
|
109
|
+
return [];
|
|
110
|
+
return (ts.getDecorators(node) ?? [])
|
|
111
|
+
.map((decorator) => decoratorUse(decorator, imports))
|
|
112
|
+
.filter((use) => use !== null);
|
|
113
|
+
}
|
|
114
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
115
|
+
function lineOf(source, node) {
|
|
116
|
+
return source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
|
|
117
|
+
}
|
|
118
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
119
|
+
function literalId(use) {
|
|
120
|
+
const arg = use.call?.arguments[0];
|
|
121
|
+
if (!arg || (!ts.isStringLiteral(arg) && !ts.isNoSubstitutionTemplateLiteral(arg)))
|
|
122
|
+
return null;
|
|
123
|
+
const value = arg.text.trim();
|
|
124
|
+
return value.length > 0 ? value : null;
|
|
125
|
+
}
|
|
126
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
127
|
+
function methodName(method) {
|
|
128
|
+
return ts.isIdentifier(method.name) || ts.isStringLiteral(method.name)
|
|
129
|
+
? method.name.text
|
|
130
|
+
: method.name.getText();
|
|
131
|
+
}
|
|
132
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
133
|
+
function hasPromiseReturn(method) {
|
|
134
|
+
const type = method.type;
|
|
135
|
+
return (type !== undefined &&
|
|
136
|
+
ts.isTypeReferenceNode(type) &&
|
|
137
|
+
ts.isIdentifier(type.typeName) &&
|
|
138
|
+
type.typeName.text === 'Promise');
|
|
139
|
+
}
|
|
140
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
141
|
+
function add(out, file, source, node, code, message) {
|
|
142
|
+
out.push(new SecurityContractViolation(file, lineOf(source, node), code, message));
|
|
143
|
+
}
|
|
144
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
145
|
+
function checkHttpMethod(file, source, method, uses, out) {
|
|
146
|
+
const endpoints = uses.filter((use) => use.name === 'Endpoint');
|
|
147
|
+
const auth = uses.filter((use) => HTTP_AUTH.has(use.name));
|
|
148
|
+
const ipc = uses.filter((use) => use.name === 'WpIpcEndpoint');
|
|
149
|
+
const label = methodName(method);
|
|
150
|
+
if (endpoints.length > 1)
|
|
151
|
+
add(out, file, source, method, 'HTTP_MULTIPLE_ENDPOINTS', `${label} has multiple @Endpoint decorators.`);
|
|
152
|
+
if (endpoints.length > 0 && auth.length !== 1) {
|
|
153
|
+
add(out, file, source, method, 'HTTP_AUTH_COUNT', `${label} must have exactly one method-level @WpAuth* decorator; found ${auth.length}. Prefer an authenticated mode; use @WpAuthPublic('reason') only for deliberate anonymous access.`);
|
|
154
|
+
}
|
|
155
|
+
if (endpoints.length === 0 && auth.length > 0)
|
|
156
|
+
add(out, file, source, auth[0].decorator, 'HTTP_ORPHAN_AUTH', `${label} has @WpAuth* but no @Endpoint.`);
|
|
157
|
+
for (const publicUse of auth.filter((use) => use.name === 'WpAuthPublic')) {
|
|
158
|
+
if (literalId(publicUse) === null)
|
|
159
|
+
add(out, file, source, publicUse.decorator, 'HTTP_PUBLIC_REASON', `${label} must use @WpAuthPublic('non-empty reason').`);
|
|
160
|
+
}
|
|
161
|
+
if (ipc.length > 0)
|
|
162
|
+
add(out, file, source, ipc[0].decorator, 'HTTP_IPC_MIX', `${label} mixes HTTP and IPC endpoint metadata.`);
|
|
163
|
+
}
|
|
164
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
165
|
+
function checkInternalMethod(file, source, method, uses, out, ids) {
|
|
166
|
+
const ipc = uses.filter((use) => use.name === 'WpIpcEndpoint');
|
|
167
|
+
const endpoint = uses.some((use) => use.name === 'Endpoint');
|
|
168
|
+
const auth = uses.some((use) => HTTP_AUTH.has(use.name));
|
|
169
|
+
const label = methodName(method);
|
|
170
|
+
if (ipc.length !== 1)
|
|
171
|
+
add(out, file, source, method, 'IPC_ENDPOINT_COUNT', `${label} must have exactly one @WpIpcEndpoint('stable-method-id'); found ${ipc.length}.`);
|
|
172
|
+
if (endpoint || auth)
|
|
173
|
+
add(out, file, source, method, 'IPC_HTTP_MIX', `${label} is internal and cannot use @Endpoint or @WpAuth*.`);
|
|
174
|
+
if (method.parameters.length !== 1)
|
|
175
|
+
add(out, file, source, method, 'IPC_DTO_COUNT', `${label} must accept exactly one DTO argument; found ${method.parameters.length}.`);
|
|
176
|
+
if (!hasPromiseReturn(method))
|
|
177
|
+
add(out, file, source, method, 'IPC_PROMISE_RETURN', `${label} must declare a Promise return type.`);
|
|
178
|
+
for (const use of ipc) {
|
|
179
|
+
const id = literalId(use);
|
|
180
|
+
if (id === null)
|
|
181
|
+
add(out, file, source, use.decorator, 'IPC_METHOD_ID', `${label} requires a non-empty inline stable method id.`);
|
|
182
|
+
else if (ids.has(id))
|
|
183
|
+
add(out, file, source, use.decorator, 'IPC_DUPLICATE_METHOD_ID', `${label} duplicates IPC method id '${id}'.`);
|
|
184
|
+
else
|
|
185
|
+
ids.add(id);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
class InternalApiId {
|
|
189
|
+
id;
|
|
190
|
+
file;
|
|
191
|
+
source;
|
|
192
|
+
node;
|
|
193
|
+
constructor(id, file, source, node) {
|
|
194
|
+
this.id = id;
|
|
195
|
+
this.file = file;
|
|
196
|
+
this.source = source;
|
|
197
|
+
this.node = node;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
class ApiClassAudit {
|
|
201
|
+
statement;
|
|
202
|
+
classUses;
|
|
203
|
+
methods;
|
|
204
|
+
methodUses;
|
|
205
|
+
apiPaths;
|
|
206
|
+
internals;
|
|
207
|
+
classAuth;
|
|
208
|
+
hasHttpMethod;
|
|
209
|
+
hasIpcMethod;
|
|
210
|
+
className;
|
|
211
|
+
constructor(statement, imports) {
|
|
212
|
+
this.statement = statement;
|
|
213
|
+
this.classUses = decoratorsOf(statement, imports);
|
|
214
|
+
this.methods = statement.members.filter(ts.isMethodDeclaration);
|
|
215
|
+
this.methodUses = this.methods.map((method) => decoratorsOf(method, imports));
|
|
216
|
+
this.apiPaths = this.classUses.filter((use) => use.name === 'ApiPath');
|
|
217
|
+
this.internals = this.classUses.filter((use) => use.name === 'WpInternal');
|
|
218
|
+
this.classAuth = this.classUses.filter((use) => HTTP_AUTH.has(use.name));
|
|
219
|
+
this.hasHttpMethod = this.methodUses.some((uses) => uses.some((use) => use.name === 'Endpoint'));
|
|
220
|
+
this.hasIpcMethod = this.methodUses.some((uses) => uses.some((use) => use.name === 'WpIpcEndpoint'));
|
|
221
|
+
this.className = statement.name?.text ?? '<anonymous class>';
|
|
222
|
+
}
|
|
223
|
+
get isHttp() {
|
|
224
|
+
return this.apiPaths.length > 0 || this.hasHttpMethod;
|
|
225
|
+
}
|
|
226
|
+
get isInternal() {
|
|
227
|
+
return this.internals.length > 0 || this.hasIpcMethod;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
231
|
+
function checkClassShape(file, source, audit, out) {
|
|
232
|
+
if (audit.hasHttpMethod && audit.apiPaths.length === 0)
|
|
233
|
+
add(out, file, source, audit.statement, 'HTTP_MISSING_API_PATH', `${audit.className} has @Endpoint methods but no @ApiPath.`);
|
|
234
|
+
if (audit.hasIpcMethod && audit.internals.length === 0)
|
|
235
|
+
add(out, file, source, audit.statement, 'IPC_MISSING_INTERNAL', `${audit.className} has @WpIpcEndpoint methods but no @WpInternal('stable-api-id').`);
|
|
236
|
+
if (audit.apiPaths.length > 1)
|
|
237
|
+
add(out, file, source, audit.statement, 'HTTP_API_PATH_COUNT', `${audit.className} has multiple @ApiPath decorators.`);
|
|
238
|
+
if (audit.internals.length > 1)
|
|
239
|
+
add(out, file, source, audit.statement, 'IPC_INTERNAL_COUNT', `${audit.className} has multiple @WpInternal decorators.`);
|
|
240
|
+
if (audit.classAuth.length > 0)
|
|
241
|
+
add(out, file, source, audit.classAuth[0].decorator, 'HTTP_CLASS_AUTH', `${audit.className} cannot declare class-level @WpAuth*; annotate every @Endpoint method.`);
|
|
242
|
+
if (audit.isHttp && audit.isInternal)
|
|
243
|
+
add(out, file, source, audit.statement, 'HTTP_IPC_CLASS_MIX', `${audit.className} mixes HTTP and internal IPC contract metadata.`);
|
|
244
|
+
}
|
|
245
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
246
|
+
function checkClassMethods(file, source, audit, out, apiIds) {
|
|
247
|
+
if (audit.internals.length === 0) {
|
|
248
|
+
if (audit.isHttp)
|
|
249
|
+
audit.methods.forEach((method, index) => checkHttpMethod(file, source, method, audit.methodUses[index], out));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const internal = audit.internals[0];
|
|
253
|
+
const id = literalId(internal);
|
|
254
|
+
if (id === null)
|
|
255
|
+
add(out, file, source, internal.decorator, 'IPC_API_ID', `${audit.className} requires @WpInternal('non-empty-stable-api-id').`);
|
|
256
|
+
else
|
|
257
|
+
apiIds.push(new InternalApiId(id, file, source, internal.decorator));
|
|
258
|
+
if (audit.apiPaths.length > 0)
|
|
259
|
+
add(out, file, source, audit.apiPaths[0].decorator, 'IPC_API_PATH', `${audit.className} is internal and cannot use @ApiPath.`);
|
|
260
|
+
const ids = new Set();
|
|
261
|
+
audit.methods.forEach((method, index) => checkInternalMethod(file, source, method, audit.methodUses[index], out, ids));
|
|
262
|
+
}
|
|
263
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
264
|
+
function auditFile(file, workspaceRoot, apiIds) {
|
|
265
|
+
const content = fs.readFileSync(path.join(workspaceRoot, file), 'utf-8');
|
|
266
|
+
const source = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);
|
|
267
|
+
const imports = importsOf(source);
|
|
268
|
+
const out = [];
|
|
269
|
+
for (const statement of source.statements) {
|
|
270
|
+
if (!ts.isClassDeclaration(statement))
|
|
271
|
+
continue;
|
|
272
|
+
const audit = new ApiClassAudit(statement, imports);
|
|
273
|
+
if (!audit.isHttp && !audit.isInternal && audit.classAuth.length === 0)
|
|
274
|
+
continue;
|
|
275
|
+
checkClassShape(file, source, audit, out);
|
|
276
|
+
checkClassMethods(file, source, audit, out, apiIds);
|
|
277
|
+
}
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
281
|
+
function owningProjectJson(workspaceRoot, relativeFile) {
|
|
282
|
+
let dir = path.dirname(relativeFile);
|
|
283
|
+
while (dir !== '.' && dir !== '') {
|
|
284
|
+
const candidate = path.join(dir, 'project.json');
|
|
285
|
+
if (fs.existsSync(path.join(workspaceRoot, candidate)))
|
|
286
|
+
return candidate;
|
|
287
|
+
const parent = path.dirname(dir);
|
|
288
|
+
if (parent === dir)
|
|
289
|
+
break;
|
|
290
|
+
dir = parent;
|
|
291
|
+
}
|
|
292
|
+
return fs.existsSync(path.join(workspaceRoot, 'project.json')) ? 'project.json' : null;
|
|
293
|
+
}
|
|
294
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
295
|
+
function findDirectlyChangedProjectRoots(workspaceRoot, changedFiles) {
|
|
296
|
+
const roots = new Set();
|
|
297
|
+
for (const file of changedFiles) {
|
|
298
|
+
const projectJson = owningProjectJson(workspaceRoot, file);
|
|
299
|
+
if (projectJson !== null)
|
|
300
|
+
roots.add(path.dirname(projectJson));
|
|
301
|
+
}
|
|
302
|
+
return [...roots].sort();
|
|
303
|
+
}
|
|
304
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
305
|
+
function projectFiles(workspaceRoot, projectRoot) {
|
|
306
|
+
const files = [];
|
|
307
|
+
const fullRoot = path.join(workspaceRoot, projectRoot);
|
|
308
|
+
const walk = (dir) => {
|
|
309
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
310
|
+
if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.git')
|
|
311
|
+
continue;
|
|
312
|
+
const full = path.join(dir, entry.name);
|
|
313
|
+
if (entry.isDirectory())
|
|
314
|
+
walk(full);
|
|
315
|
+
else if (/\.tsx?$/.test(entry.name) &&
|
|
316
|
+
!entry.name.endsWith('.d.ts') &&
|
|
317
|
+
!entry.name.endsWith('.spec.ts') &&
|
|
318
|
+
!entry.name.endsWith('.test.ts') &&
|
|
319
|
+
!entry.name.endsWith('.spec.tsx') &&
|
|
320
|
+
!entry.name.endsWith('.test.tsx')) {
|
|
321
|
+
const rel = path.relative(workspaceRoot, full);
|
|
322
|
+
const owner = owningProjectJson(workspaceRoot, rel);
|
|
323
|
+
if (owner !== null && path.dirname(owner) === projectRoot)
|
|
324
|
+
files.push(rel);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
if (fs.existsSync(fullRoot))
|
|
329
|
+
walk(fullRoot);
|
|
330
|
+
return files.sort();
|
|
331
|
+
}
|
|
332
|
+
// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper
|
|
333
|
+
function auditSecurityContracts(workspaceRoot, projectRoots) {
|
|
334
|
+
const apiIds = [];
|
|
335
|
+
const files = projectRoots.flatMap((root) => projectFiles(workspaceRoot, root));
|
|
336
|
+
const out = files.flatMap((file) => auditFile(file, workspaceRoot, apiIds));
|
|
337
|
+
const byId = new Map();
|
|
338
|
+
for (const api of apiIds)
|
|
339
|
+
byId.set(api.id, [...(byId.get(api.id) ?? []), api]);
|
|
340
|
+
for (const entry of byId.entries()) {
|
|
341
|
+
const id = entry[0];
|
|
342
|
+
const duplicates = entry[1];
|
|
343
|
+
if (duplicates.length < 2)
|
|
344
|
+
continue;
|
|
345
|
+
for (const duplicate of duplicates)
|
|
346
|
+
add(out, duplicate.file, duplicate.source, duplicate.node, 'IPC_DUPLICATE_API_ID', `Duplicate internal API id '${id}'.`);
|
|
347
|
+
}
|
|
348
|
+
return out.sort((a, b) => a.file.localeCompare(b.file) ||
|
|
349
|
+
a.line - b.line ||
|
|
350
|
+
a.code.localeCompare(b.code) ||
|
|
351
|
+
a.message.localeCompare(b.message));
|
|
352
|
+
}
|
|
353
|
+
// webpieces-disable no-function-outside-class -- one structured rule failure shared by tests and runner
|
|
354
|
+
function securityContractsError(violations) {
|
|
355
|
+
const details = violations
|
|
356
|
+
.map((violation) => ` ${violation.file}:${String(violation.line)} [${violation.code}] ${violation.message}`)
|
|
357
|
+
.join('\n');
|
|
358
|
+
const first = violations[0];
|
|
359
|
+
const message = 'API contracts in directly changed projects are not explicit and secure.\n' + details;
|
|
360
|
+
return new rules_config_1.RuleFailError(rules_config_1.RULE_NAMES.ENSURE_WE_ARE_SECURE, message, first?.line, first === undefined ? undefined : `[${first.code}] ${first.message}`, [
|
|
361
|
+
new rules_config_1.Option("Put exactly one authenticated method-level @WpAuth* decorator on every @Endpoint. Use @WpAuthPublic('non-empty reason') only when anonymous access is deliberate.", true),
|
|
362
|
+
new rules_config_1.Option("For IPC-only contracts, replace HTTP metadata with class-level @WpInternal('stable-api-id') and exactly one @WpIpcEndpoint('stable-method-id') per method."),
|
|
363
|
+
]);
|
|
364
|
+
}
|
|
365
|
+
// webpieces-disable no-function-outside-class -- one plumbing failure shared by tests and runner
|
|
366
|
+
function missingSecurityBaseError() {
|
|
367
|
+
return new rules_config_1.InformAiError('ensure-we-are-secure could not determine the comparison base. Set NX_BASE to the base branch or commit and retry.');
|
|
368
|
+
}
|
|
369
|
+
let EnsureWeAreSecureValidator = class EnsureWeAreSecureValidator extends code_validator_1.CodeValidator {
|
|
370
|
+
constructor(config) {
|
|
371
|
+
super(config, 'ensure-we-are-secure', 'ensure-we-are-secure');
|
|
372
|
+
}
|
|
373
|
+
async run(workspaceRoot) {
|
|
374
|
+
if ((this.config.mode ?? 'OFF') === 'OFF')
|
|
375
|
+
return { success: true };
|
|
376
|
+
const base = process.env['NX_BASE'] ?? (0, rules_config_1.detectBase)(workspaceRoot);
|
|
377
|
+
if (base === null || base === undefined) {
|
|
378
|
+
throw missingSecurityBaseError();
|
|
379
|
+
}
|
|
380
|
+
const changed = (0, rules_config_1.getChangedFiles)(workspaceRoot, base, process.env['NX_HEAD'], {
|
|
381
|
+
tsOnly: false,
|
|
382
|
+
});
|
|
383
|
+
const roots = findDirectlyChangedProjectRoots(workspaceRoot, changed);
|
|
384
|
+
const violations = auditSecurityContracts(workspaceRoot, roots);
|
|
385
|
+
if (violations.length === 0)
|
|
386
|
+
return { success: true };
|
|
387
|
+
throw securityContractsError(violations);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
exports.EnsureWeAreSecureValidator = EnsureWeAreSecureValidator;
|
|
391
|
+
exports.EnsureWeAreSecureValidator = EnsureWeAreSecureValidator = tslib_1.__decorate([
|
|
392
|
+
(0, inversify_1.injectable)(inversify_1.bindingScopeValues.Singleton),
|
|
393
|
+
tslib_1.__metadata("design:paramtypes", [rules_config_1.EnsureWeAreSecureConfig])
|
|
394
|
+
], EnsureWeAreSecureValidator);
|
|
395
|
+
//# sourceMappingURL=validate-ensure-we-are-secure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-ensure-we-are-secure.js","sourceRoot":"","sources":["../../../../../packages/tooling/code-rules/src/validate-ensure-we-are-secure.ts"],"names":[],"mappings":"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","sourcesContent":["/** Audits explicit HTTP authentication and IPC-only contracts in directly changed projects. */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as ts from 'typescript';\nimport {\n EnsureWeAreSecureConfig,\n getChangedFiles,\n detectBase,\n InformAiError,\n Option,\n RuleFailError,\n RULE_NAMES,\n} from '@webpieces/rules-config';\nimport { injectable, bindingScopeValues } from 'inversify';\nimport { CodeValidator, ExecutorResult } from './code-validator';\n\nconst HTTP_AUTH = new Set([\n 'WpAuthJwt',\n 'WpAuthOidc',\n 'WpAuthWebhook',\n 'WpAuthSharedSecret',\n 'WpAuthApiKey',\n 'WpAuthLocalOnly',\n 'WpAuthPublic',\n]);\nconst CONTRACT_DECORATORS = new Set([\n ...HTTP_AUTH,\n 'ApiPath',\n 'Endpoint',\n 'WpInternal',\n 'WpIpcEndpoint',\n]);\n\nexport class SecurityContractViolation {\n constructor(\n readonly file: string,\n readonly line: number,\n readonly code: string,\n readonly message: string,\n ) {}\n}\n\nclass DecoratorUse {\n constructor(\n readonly name: string,\n readonly decorator: ts.Decorator,\n readonly call: ts.CallExpression | null,\n ) {}\n}\n\nclass CanonicalImports {\n readonly named = new Map<string, string>();\n readonly namespaces = new Set<string>();\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction isCanonicalModule(moduleName: string): boolean {\n return (\n moduleName === '@webpieces/core-util' ||\n moduleName === '@webpieces/core-util/ipc' ||\n moduleName.endsWith('/core-util/src/http/decorators') ||\n moduleName.includes('/core-util/src/ipc/')\n );\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction importsOf(source: ts.SourceFile): CanonicalImports {\n const result = new CanonicalImports();\n for (const statement of source.statements) {\n if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier))\n continue;\n if (!isCanonicalModule(statement.moduleSpecifier.text)) continue;\n const bindings = statement.importClause?.namedBindings;\n if (bindings && ts.isNamedImports(bindings)) {\n for (const item of bindings.elements) {\n const exported = item.propertyName?.text ?? item.name.text;\n if (CONTRACT_DECORATORS.has(exported)) result.named.set(item.name.text, exported);\n }\n } else if (bindings && ts.isNamespaceImport(bindings)) {\n result.namespaces.add(bindings.name.text);\n }\n }\n return result;\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction decoratorUse(decorator: ts.Decorator, imports: CanonicalImports): DecoratorUse | null {\n const expr = decorator.expression;\n const call = ts.isCallExpression(expr) ? expr : null;\n const callee = call?.expression ?? expr;\n if (ts.isIdentifier(callee)) {\n const canonical = imports.named.get(callee.text);\n return canonical ? new DecoratorUse(canonical, decorator, call) : null;\n }\n if (\n ts.isPropertyAccessExpression(callee) &&\n ts.isIdentifier(callee.expression) &&\n imports.namespaces.has(callee.expression.text)\n ) {\n return CONTRACT_DECORATORS.has(callee.name.text)\n ? new DecoratorUse(callee.name.text, decorator, call)\n : null;\n }\n return null;\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction decoratorsOf(node: ts.Node, imports: CanonicalImports): DecoratorUse[] {\n if (!ts.canHaveDecorators(node)) return [];\n return (ts.getDecorators(node) ?? [])\n .map((decorator: ts.Decorator) => decoratorUse(decorator, imports))\n .filter((use): use is DecoratorUse => use !== null);\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction lineOf(source: ts.SourceFile, node: ts.Node): number {\n return source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction literalId(use: DecoratorUse): string | null {\n const arg = use.call?.arguments[0];\n if (!arg || (!ts.isStringLiteral(arg) && !ts.isNoSubstitutionTemplateLiteral(arg))) return null;\n const value = arg.text.trim();\n return value.length > 0 ? value : null;\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction methodName(method: ts.MethodDeclaration): string {\n return ts.isIdentifier(method.name) || ts.isStringLiteral(method.name)\n ? method.name.text\n : method.name.getText();\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction hasPromiseReturn(method: ts.MethodDeclaration): boolean {\n const type = method.type;\n return (\n type !== undefined &&\n ts.isTypeReferenceNode(type) &&\n ts.isIdentifier(type.typeName) &&\n type.typeName.text === 'Promise'\n );\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction add(\n out: SecurityContractViolation[],\n file: string,\n source: ts.SourceFile,\n node: ts.Node,\n code: string,\n message: string,\n): void {\n out.push(new SecurityContractViolation(file, lineOf(source, node), code, message));\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction checkHttpMethod(\n file: string,\n source: ts.SourceFile,\n method: ts.MethodDeclaration,\n uses: DecoratorUse[],\n out: SecurityContractViolation[],\n): void {\n const endpoints = uses.filter((use: DecoratorUse) => use.name === 'Endpoint');\n const auth = uses.filter((use: DecoratorUse) => HTTP_AUTH.has(use.name));\n const ipc = uses.filter((use: DecoratorUse) => use.name === 'WpIpcEndpoint');\n const label = methodName(method);\n if (endpoints.length > 1)\n add(\n out,\n file,\n source,\n method,\n 'HTTP_MULTIPLE_ENDPOINTS',\n `${label} has multiple @Endpoint decorators.`,\n );\n if (endpoints.length > 0 && auth.length !== 1) {\n add(\n out,\n file,\n source,\n method,\n 'HTTP_AUTH_COUNT',\n `${label} must have exactly one method-level @WpAuth* decorator; found ${auth.length}. Prefer an authenticated mode; use @WpAuthPublic('reason') only for deliberate anonymous access.`,\n );\n }\n if (endpoints.length === 0 && auth.length > 0)\n add(\n out,\n file,\n source,\n auth[0]!.decorator,\n 'HTTP_ORPHAN_AUTH',\n `${label} has @WpAuth* but no @Endpoint.`,\n );\n for (const publicUse of auth.filter((use: DecoratorUse) => use.name === 'WpAuthPublic')) {\n if (literalId(publicUse) === null)\n add(\n out,\n file,\n source,\n publicUse.decorator,\n 'HTTP_PUBLIC_REASON',\n `${label} must use @WpAuthPublic('non-empty reason').`,\n );\n }\n if (ipc.length > 0)\n add(\n out,\n file,\n source,\n ipc[0]!.decorator,\n 'HTTP_IPC_MIX',\n `${label} mixes HTTP and IPC endpoint metadata.`,\n );\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction checkInternalMethod(\n file: string,\n source: ts.SourceFile,\n method: ts.MethodDeclaration,\n uses: DecoratorUse[],\n out: SecurityContractViolation[],\n ids: Set<string>,\n): void {\n const ipc = uses.filter((use: DecoratorUse) => use.name === 'WpIpcEndpoint');\n const endpoint = uses.some((use: DecoratorUse) => use.name === 'Endpoint');\n const auth = uses.some((use: DecoratorUse) => HTTP_AUTH.has(use.name));\n const label = methodName(method);\n if (ipc.length !== 1)\n add(\n out,\n file,\n source,\n method,\n 'IPC_ENDPOINT_COUNT',\n `${label} must have exactly one @WpIpcEndpoint('stable-method-id'); found ${ipc.length}.`,\n );\n if (endpoint || auth)\n add(\n out,\n file,\n source,\n method,\n 'IPC_HTTP_MIX',\n `${label} is internal and cannot use @Endpoint or @WpAuth*.`,\n );\n if (method.parameters.length !== 1)\n add(\n out,\n file,\n source,\n method,\n 'IPC_DTO_COUNT',\n `${label} must accept exactly one DTO argument; found ${method.parameters.length}.`,\n );\n if (!hasPromiseReturn(method))\n add(\n out,\n file,\n source,\n method,\n 'IPC_PROMISE_RETURN',\n `${label} must declare a Promise return type.`,\n );\n for (const use of ipc) {\n const id = literalId(use);\n if (id === null)\n add(\n out,\n file,\n source,\n use.decorator,\n 'IPC_METHOD_ID',\n `${label} requires a non-empty inline stable method id.`,\n );\n else if (ids.has(id))\n add(\n out,\n file,\n source,\n use.decorator,\n 'IPC_DUPLICATE_METHOD_ID',\n `${label} duplicates IPC method id '${id}'.`,\n );\n else ids.add(id);\n }\n}\n\nclass InternalApiId {\n constructor(\n readonly id: string,\n readonly file: string,\n readonly source: ts.SourceFile,\n readonly node: ts.Node,\n ) {}\n}\n\nclass ApiClassAudit {\n readonly classUses: DecoratorUse[];\n readonly methods: ts.MethodDeclaration[];\n readonly methodUses: DecoratorUse[][];\n readonly apiPaths: DecoratorUse[];\n readonly internals: DecoratorUse[];\n readonly classAuth: DecoratorUse[];\n readonly hasHttpMethod: boolean;\n readonly hasIpcMethod: boolean;\n readonly className: string;\n\n constructor(\n readonly statement: ts.ClassDeclaration,\n imports: CanonicalImports,\n ) {\n this.classUses = decoratorsOf(statement, imports);\n this.methods = statement.members.filter(ts.isMethodDeclaration);\n this.methodUses = this.methods.map((method: ts.MethodDeclaration) =>\n decoratorsOf(method, imports),\n );\n this.apiPaths = this.classUses.filter((use: DecoratorUse) => use.name === 'ApiPath');\n this.internals = this.classUses.filter((use: DecoratorUse) => use.name === 'WpInternal');\n this.classAuth = this.classUses.filter((use: DecoratorUse) => HTTP_AUTH.has(use.name));\n this.hasHttpMethod = this.methodUses.some((uses: DecoratorUse[]) =>\n uses.some((use: DecoratorUse) => use.name === 'Endpoint'),\n );\n this.hasIpcMethod = this.methodUses.some((uses: DecoratorUse[]) =>\n uses.some((use: DecoratorUse) => use.name === 'WpIpcEndpoint'),\n );\n this.className = statement.name?.text ?? '<anonymous class>';\n }\n\n get isHttp(): boolean {\n return this.apiPaths.length > 0 || this.hasHttpMethod;\n }\n\n get isInternal(): boolean {\n return this.internals.length > 0 || this.hasIpcMethod;\n }\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction checkClassShape(\n file: string,\n source: ts.SourceFile,\n audit: ApiClassAudit,\n out: SecurityContractViolation[],\n): void {\n if (audit.hasHttpMethod && audit.apiPaths.length === 0)\n add(\n out,\n file,\n source,\n audit.statement,\n 'HTTP_MISSING_API_PATH',\n `${audit.className} has @Endpoint methods but no @ApiPath.`,\n );\n if (audit.hasIpcMethod && audit.internals.length === 0)\n add(\n out,\n file,\n source,\n audit.statement,\n 'IPC_MISSING_INTERNAL',\n `${audit.className} has @WpIpcEndpoint methods but no @WpInternal('stable-api-id').`,\n );\n if (audit.apiPaths.length > 1)\n add(\n out,\n file,\n source,\n audit.statement,\n 'HTTP_API_PATH_COUNT',\n `${audit.className} has multiple @ApiPath decorators.`,\n );\n if (audit.internals.length > 1)\n add(\n out,\n file,\n source,\n audit.statement,\n 'IPC_INTERNAL_COUNT',\n `${audit.className} has multiple @WpInternal decorators.`,\n );\n if (audit.classAuth.length > 0)\n add(\n out,\n file,\n source,\n audit.classAuth[0]!.decorator,\n 'HTTP_CLASS_AUTH',\n `${audit.className} cannot declare class-level @WpAuth*; annotate every @Endpoint method.`,\n );\n if (audit.isHttp && audit.isInternal)\n add(\n out,\n file,\n source,\n audit.statement,\n 'HTTP_IPC_CLASS_MIX',\n `${audit.className} mixes HTTP and internal IPC contract metadata.`,\n );\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction checkClassMethods(\n file: string,\n source: ts.SourceFile,\n audit: ApiClassAudit,\n out: SecurityContractViolation[],\n apiIds: InternalApiId[],\n): void {\n if (audit.internals.length === 0) {\n if (audit.isHttp)\n audit.methods.forEach((method: ts.MethodDeclaration, index: number) =>\n checkHttpMethod(file, source, method, audit.methodUses[index]!, out),\n );\n return;\n }\n const internal = audit.internals[0]!;\n const id = literalId(internal);\n if (id === null)\n add(\n out,\n file,\n source,\n internal.decorator,\n 'IPC_API_ID',\n `${audit.className} requires @WpInternal('non-empty-stable-api-id').`,\n );\n else apiIds.push(new InternalApiId(id, file, source, internal.decorator));\n if (audit.apiPaths.length > 0)\n add(\n out,\n file,\n source,\n audit.apiPaths[0]!.decorator,\n 'IPC_API_PATH',\n `${audit.className} is internal and cannot use @ApiPath.`,\n );\n const ids = new Set<string>();\n audit.methods.forEach((method: ts.MethodDeclaration, index: number) =>\n checkInternalMethod(file, source, method, audit.methodUses[index]!, out, ids),\n );\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction auditFile(\n file: string,\n workspaceRoot: string,\n apiIds: InternalApiId[],\n): SecurityContractViolation[] {\n const content = fs.readFileSync(path.join(workspaceRoot, file), 'utf-8');\n const source = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);\n const imports = importsOf(source);\n const out: SecurityContractViolation[] = [];\n for (const statement of source.statements) {\n if (!ts.isClassDeclaration(statement)) continue;\n const audit = new ApiClassAudit(statement, imports);\n if (!audit.isHttp && !audit.isInternal && audit.classAuth.length === 0) continue;\n checkClassShape(file, source, audit, out);\n checkClassMethods(file, source, audit, out, apiIds);\n }\n return out;\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction owningProjectJson(workspaceRoot: string, relativeFile: string): string | null {\n let dir = path.dirname(relativeFile);\n while (dir !== '.' && dir !== '') {\n const candidate = path.join(dir, 'project.json');\n if (fs.existsSync(path.join(workspaceRoot, candidate))) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return fs.existsSync(path.join(workspaceRoot, 'project.json')) ? 'project.json' : null;\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nexport function findDirectlyChangedProjectRoots(\n workspaceRoot: string,\n changedFiles: string[],\n): string[] {\n const roots = new Set<string>();\n for (const file of changedFiles) {\n const projectJson = owningProjectJson(workspaceRoot, file);\n if (projectJson !== null) roots.add(path.dirname(projectJson));\n }\n return [...roots].sort();\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nfunction projectFiles(workspaceRoot: string, projectRoot: string): string[] {\n const files: string[] = [];\n const fullRoot = path.join(workspaceRoot, projectRoot);\n const walk = (dir: string): void => {\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.git')\n continue;\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) walk(full);\n else if (\n /\\.tsx?$/.test(entry.name) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.endsWith('.spec.ts') &&\n !entry.name.endsWith('.test.ts') &&\n !entry.name.endsWith('.spec.tsx') &&\n !entry.name.endsWith('.test.tsx')\n ) {\n const rel = path.relative(workspaceRoot, full);\n const owner = owningProjectJson(workspaceRoot, rel);\n if (owner !== null && path.dirname(owner) === projectRoot) files.push(rel);\n }\n }\n };\n if (fs.existsSync(fullRoot)) walk(fullRoot);\n return files.sort();\n}\n\n// webpieces-disable no-function-outside-class -- stateless TypeScript AST security validator helper\nexport function auditSecurityContracts(\n workspaceRoot: string,\n projectRoots: string[],\n): SecurityContractViolation[] {\n const apiIds: InternalApiId[] = [];\n const files = projectRoots.flatMap((root: string) => projectFiles(workspaceRoot, root));\n const out = files.flatMap((file: string) => auditFile(file, workspaceRoot, apiIds));\n const byId = new Map<string, InternalApiId[]>();\n for (const api of apiIds) byId.set(api.id, [...(byId.get(api.id) ?? []), api]);\n for (const entry of byId.entries()) {\n const id = entry[0];\n const duplicates = entry[1];\n if (duplicates.length < 2) continue;\n for (const duplicate of duplicates)\n add(\n out,\n duplicate.file,\n duplicate.source,\n duplicate.node,\n 'IPC_DUPLICATE_API_ID',\n `Duplicate internal API id '${id}'.`,\n );\n }\n return out.sort(\n (a: SecurityContractViolation, b: SecurityContractViolation) =>\n a.file.localeCompare(b.file) ||\n a.line - b.line ||\n a.code.localeCompare(b.code) ||\n a.message.localeCompare(b.message),\n );\n}\n\n// webpieces-disable no-function-outside-class -- one structured rule failure shared by tests and runner\nexport function securityContractsError(\n violations: readonly SecurityContractViolation[],\n): RuleFailError {\n const details = violations\n .map(\n (violation: SecurityContractViolation): string =>\n ` ${violation.file}:${String(violation.line)} [${violation.code}] ${violation.message}`,\n )\n .join('\\n');\n const first = violations[0];\n const message =\n 'API contracts in directly changed projects are not explicit and secure.\\n' + details;\n return new RuleFailError(\n RULE_NAMES.ENSURE_WE_ARE_SECURE,\n message,\n first?.line,\n first === undefined ? undefined : `[${first.code}] ${first.message}`,\n [\n new Option(\n \"Put exactly one authenticated method-level @WpAuth* decorator on every @Endpoint. Use @WpAuthPublic('non-empty reason') only when anonymous access is deliberate.\",\n true,\n ),\n new Option(\n \"For IPC-only contracts, replace HTTP metadata with class-level @WpInternal('stable-api-id') and exactly one @WpIpcEndpoint('stable-method-id') per method.\",\n ),\n ],\n );\n}\n\n// webpieces-disable no-function-outside-class -- one plumbing failure shared by tests and runner\nexport function missingSecurityBaseError(): InformAiError {\n return new InformAiError(\n 'ensure-we-are-secure could not determine the comparison base. Set NX_BASE to the base branch or commit and retry.',\n );\n}\n\n@injectable(bindingScopeValues.Singleton)\nexport class EnsureWeAreSecureValidator extends CodeValidator<EnsureWeAreSecureConfig> {\n constructor(config: EnsureWeAreSecureConfig) {\n super(config, 'ensure-we-are-secure', 'ensure-we-are-secure');\n }\n\n async run(workspaceRoot: string): Promise<ExecutorResult> {\n if ((this.config.mode ?? 'OFF') === 'OFF') return { success: true };\n const base = process.env['NX_BASE'] ?? detectBase(workspaceRoot);\n if (base === null || base === undefined) {\n throw missingSecurityBaseError();\n }\n const changed = getChangedFiles(workspaceRoot, base, process.env['NX_HEAD'], {\n tsOnly: false,\n });\n const roots = findDirectlyChangedProjectRoots(workspaceRoot, changed);\n const violations = auditSecurityContracts(workspaceRoot, roots);\n if (violations.length === 0) return { success: true };\n throw securityContractsError(violations);\n }\n}\n"]}
|