@webpieces/code-rules 0.4.759 → 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/code-rules",
3
- "version": "0.4.759",
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.759",
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
- [rules_config_1.InjectAnnotationNotNeededForConcreteClassConfig, 'inject-annotation-not-needed-for-concrete-class'],
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,0DAwBiC;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,CAAC,8DAA+C,EAAE,iDAAiD,CAAC;IACpG,CAAC,iCAAkB,EAAE,eAAe,CAAC;IACrC,CAAC,4BAAa,EAAE,UAAU,CAAC;CAC9B,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} 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 [InjectAnnotationNotNeededForConcreteClassConfig, 'inject-annotation-not-needed-for-concrete-class'],\n [FrameworkTagConfig, 'framework-tag'],\n [RoleTagConfig, 'role-tag'],\n];\n"]}
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
- 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);
59
- /** The 21 injected built-in validators, in run order. */
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
@@ -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 21 injected built-in validators, in run order. */
94
+ /** The injected built-in validators, in run order. */
92
95
  builtIns() {
93
96
  return [
94
- this.maxMethodLines, this.maxFileLines, this.requireReturnType, this.noInlineTypeLiterals,
95
- this.noAnyUnknown, this.noImplicitAny, this.prismaValidateDtos, this.prismaConverter,
96
- this.noDestructure, this.catchErrorPattern, this.noUnmanagedExceptions, this.noDirectApiResolver,
97
- this.noSymbolDiTokens, this.noClientCreationOutsideServerOrClient, this.noCustomCss,
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, this.noFunctionOutsideClass,
100
- this.injectAnnotationNotNeeded, this.frameworkTag, this.roleTag,
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 ? '\n✅ All code validations passed\n' : '\n❌ Some code validations failed\n');
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,+FAA4H;AAAnH,wJAAA,gCAAgC,OAAA;AAAE,oIAAA,YAAY,OAAA;AAAE,0IAAA,kBAAkB,OAAA;AAC3E,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAC5B,iGAA4F;AAAnF,0JAAA,iCAAiC,OAAA;AAC1C,2FAAuF;AAA9E,qJAAA,+BAA+B,OAAA;AACxC,uIAA6J;AAApJ,8LAAA,kDAAkD,OAAA;AAAE,uKAAA,2BAA2B,OAAA;AACxF,mEAAiE;AAAxD,+HAAA,qBAAqB,OAAA;AAC9B,yDAAuD;AAA9C,qHAAA,gBAAgB,OAAA;AACzB,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 { NoStatePathsInTemplatesValidator, StatePathHit, StatePathViolation } 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 { InjectAnnotationNotNeededForConcreteClassValidator, findRedundantInjectInSource } from './validate-inject-annotation-not-needed-for-concrete-class';\nexport { FrameworkTagValidator } from './validate-framework-tag';\nexport { RoleTagValidator } from './validate-role-tag';\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"]}
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"]}