@apiposture/cli 1.0.1

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.
Files changed (110) hide show
  1. package/.apiposture.json.example +56 -0
  2. package/.github/workflows/publish.yml +38 -0
  3. package/.github/workflows/test.yml +42 -0
  4. package/LICENSE +21 -0
  5. package/README.md +156 -0
  6. package/dist/cli/commands/license/activate.d.ts +3 -0
  7. package/dist/cli/commands/license/activate.js +35 -0
  8. package/dist/cli/commands/license/deactivate.d.ts +3 -0
  9. package/dist/cli/commands/license/deactivate.js +28 -0
  10. package/dist/cli/commands/license/status.d.ts +3 -0
  11. package/dist/cli/commands/license/status.js +36 -0
  12. package/dist/cli/commands/scan.d.ts +3 -0
  13. package/dist/cli/commands/scan.js +211 -0
  14. package/dist/cli/options.d.ts +27 -0
  15. package/dist/cli/options.js +30 -0
  16. package/dist/core/analysis/project-analyzer.d.ts +16 -0
  17. package/dist/core/analysis/project-analyzer.js +54 -0
  18. package/dist/core/analysis/source-file-loader.d.ts +32 -0
  19. package/dist/core/analysis/source-file-loader.js +155 -0
  20. package/dist/core/authorization/authorization-extractor.d.ts +11 -0
  21. package/dist/core/authorization/authorization-extractor.js +2 -0
  22. package/dist/core/authorization/express-auth-extractor.d.ts +10 -0
  23. package/dist/core/authorization/express-auth-extractor.js +106 -0
  24. package/dist/core/authorization/global-auth-analyzer.d.ts +12 -0
  25. package/dist/core/authorization/global-auth-analyzer.js +74 -0
  26. package/dist/core/authorization/nestjs-auth-extractor.d.ts +13 -0
  27. package/dist/core/authorization/nestjs-auth-extractor.js +142 -0
  28. package/dist/core/configuration/config-loader.d.ts +27 -0
  29. package/dist/core/configuration/config-loader.js +72 -0
  30. package/dist/core/configuration/suppression-matcher.d.ts +14 -0
  31. package/dist/core/configuration/suppression-matcher.js +79 -0
  32. package/dist/core/discovery/discoverer-interface.d.ts +7 -0
  33. package/dist/core/discovery/discoverer-interface.js +2 -0
  34. package/dist/core/discovery/express-discoverer.d.ts +20 -0
  35. package/dist/core/discovery/express-discoverer.js +223 -0
  36. package/dist/core/discovery/fastify-discoverer.d.ts +19 -0
  37. package/dist/core/discovery/fastify-discoverer.js +249 -0
  38. package/dist/core/discovery/framework-detector.d.ts +9 -0
  39. package/dist/core/discovery/framework-detector.js +61 -0
  40. package/dist/core/discovery/index.d.ts +8 -0
  41. package/dist/core/discovery/index.js +8 -0
  42. package/dist/core/discovery/koa-discoverer.d.ts +16 -0
  43. package/dist/core/discovery/koa-discoverer.js +151 -0
  44. package/dist/core/discovery/nestjs-discoverer.d.ts +16 -0
  45. package/dist/core/discovery/nestjs-discoverer.js +180 -0
  46. package/dist/core/discovery/route-group-registry.d.ts +18 -0
  47. package/dist/core/discovery/route-group-registry.js +50 -0
  48. package/dist/core/licensing/license-context.d.ts +17 -0
  49. package/dist/core/licensing/license-context.js +15 -0
  50. package/dist/core/licensing/license-features.d.ts +14 -0
  51. package/dist/core/licensing/license-features.js +47 -0
  52. package/dist/core/models/authorization-info.d.ts +13 -0
  53. package/dist/core/models/authorization-info.js +25 -0
  54. package/dist/core/models/endpoint-type.d.ts +8 -0
  55. package/dist/core/models/endpoint-type.js +12 -0
  56. package/dist/core/models/endpoint.d.ts +16 -0
  57. package/dist/core/models/endpoint.js +16 -0
  58. package/dist/core/models/finding.d.ts +19 -0
  59. package/dist/core/models/finding.js +8 -0
  60. package/dist/core/models/http-method.d.ts +14 -0
  61. package/dist/core/models/http-method.js +25 -0
  62. package/dist/core/models/index.d.ts +10 -0
  63. package/dist/core/models/index.js +10 -0
  64. package/dist/core/models/scan-result.d.ts +21 -0
  65. package/dist/core/models/scan-result.js +35 -0
  66. package/dist/core/models/security-classification.d.ts +8 -0
  67. package/dist/core/models/security-classification.js +12 -0
  68. package/dist/core/models/severity.d.ts +11 -0
  69. package/dist/core/models/severity.js +23 -0
  70. package/dist/core/models/source-location.d.ts +7 -0
  71. package/dist/core/models/source-location.js +4 -0
  72. package/dist/index.d.ts +3 -0
  73. package/dist/index.js +23 -0
  74. package/dist/licensing/license-manager.d.ts +38 -0
  75. package/dist/licensing/license-manager.js +184 -0
  76. package/dist/output/accessibility-helper.d.ts +22 -0
  77. package/dist/output/accessibility-helper.js +98 -0
  78. package/dist/output/formatter-interface.d.ts +11 -0
  79. package/dist/output/formatter-interface.js +2 -0
  80. package/dist/output/index.d.ts +6 -0
  81. package/dist/output/index.js +6 -0
  82. package/dist/output/json-formatter.d.ts +7 -0
  83. package/dist/output/json-formatter.js +72 -0
  84. package/dist/output/markdown-formatter.d.ts +10 -0
  85. package/dist/output/markdown-formatter.js +114 -0
  86. package/dist/output/terminal-formatter.d.ts +12 -0
  87. package/dist/output/terminal-formatter.js +82 -0
  88. package/dist/rules/consistency/controller-action-conflict.d.ts +19 -0
  89. package/dist/rules/consistency/controller-action-conflict.js +40 -0
  90. package/dist/rules/consistency/missing-auth-on-writes.d.ts +21 -0
  91. package/dist/rules/consistency/missing-auth-on-writes.js +59 -0
  92. package/dist/rules/exposure/allow-anonymous-on-write.d.ts +20 -0
  93. package/dist/rules/exposure/allow-anonymous-on-write.js +42 -0
  94. package/dist/rules/exposure/public-without-explicit-intent.d.ts +20 -0
  95. package/dist/rules/exposure/public-without-explicit-intent.js +58 -0
  96. package/dist/rules/index.d.ts +11 -0
  97. package/dist/rules/index.js +11 -0
  98. package/dist/rules/privilege/excessive-role-access.d.ts +20 -0
  99. package/dist/rules/privilege/excessive-role-access.js +36 -0
  100. package/dist/rules/privilege/weak-role-naming.d.ts +20 -0
  101. package/dist/rules/privilege/weak-role-naming.js +50 -0
  102. package/dist/rules/rule-engine.d.ts +15 -0
  103. package/dist/rules/rule-engine.js +52 -0
  104. package/dist/rules/rule-interface.d.ts +16 -0
  105. package/dist/rules/rule-interface.js +2 -0
  106. package/dist/rules/surface/sensitive-route-keywords.d.ts +20 -0
  107. package/dist/rules/surface/sensitive-route-keywords.js +63 -0
  108. package/dist/rules/surface/unprotected-endpoint.d.ts +20 -0
  109. package/dist/rules/surface/unprotected-endpoint.js +61 -0
  110. package/package.json +60 -0
@@ -0,0 +1,180 @@
1
+ import * as ts from 'typescript';
2
+ import { getLineAndColumn, getDecorators, getDecoratorName, getDecoratorArguments, getStringLiteralValue, } from '../analysis/source-file-loader.js';
3
+ import { createEndpoint } from '../models/endpoint.js';
4
+ import { EndpointType } from '../models/endpoint-type.js';
5
+ import { HttpMethod } from '../models/http-method.js';
6
+ import { NestJSAuthExtractor } from '../authorization/nestjs-auth-extractor.js';
7
+ const NESTJS_HTTP_DECORATORS = new Map([
8
+ ['Get', HttpMethod.GET],
9
+ ['Post', HttpMethod.POST],
10
+ ['Put', HttpMethod.PUT],
11
+ ['Delete', HttpMethod.DELETE],
12
+ ['Patch', HttpMethod.PATCH],
13
+ ['Options', HttpMethod.OPTIONS],
14
+ ['Head', HttpMethod.HEAD],
15
+ ['All', HttpMethod.ALL],
16
+ ]);
17
+ export class NestJSDiscoverer {
18
+ name = 'NestJS';
19
+ authExtractor;
20
+ constructor() {
21
+ this.authExtractor = new NestJSAuthExtractor();
22
+ }
23
+ async discover(file) {
24
+ const endpoints = [];
25
+ const { sourceFile, filePath } = file;
26
+ // Find all classes with @Controller decorator
27
+ ts.forEachChild(sourceFile, (node) => {
28
+ if (ts.isClassDeclaration(node)) {
29
+ const classEndpoints = this.processClass(node, sourceFile, filePath);
30
+ endpoints.push(...classEndpoints);
31
+ }
32
+ });
33
+ return endpoints;
34
+ }
35
+ processClass(classNode, sourceFile, filePath) {
36
+ const endpoints = [];
37
+ const decorators = getDecorators(classNode);
38
+ // Find @Controller decorator
39
+ const controllerDecorator = decorators.find((d) => getDecoratorName(d) === 'Controller');
40
+ if (!controllerDecorator) {
41
+ return endpoints;
42
+ }
43
+ // Get controller path
44
+ const controllerPath = this.extractControllerPath(controllerDecorator);
45
+ const controllerName = classNode.name?.text ?? 'UnknownController';
46
+ // Extract class-level auth decorators
47
+ const classAuth = this.extractClassAuthInfo(decorators);
48
+ // Process all methods in the class
49
+ for (const member of classNode.members) {
50
+ if (ts.isMethodDeclaration(member)) {
51
+ const endpoint = this.processMethod(member, sourceFile, filePath, controllerPath, controllerName, classAuth);
52
+ if (endpoint) {
53
+ endpoints.push(endpoint);
54
+ }
55
+ }
56
+ }
57
+ return endpoints;
58
+ }
59
+ processMethod(methodNode, sourceFile, filePath, controllerPath, controllerName, classAuth) {
60
+ const decorators = getDecorators(methodNode);
61
+ const methodName = methodNode.name
62
+ ? ts.isIdentifier(methodNode.name)
63
+ ? methodNode.name.text
64
+ : methodNode.name.getText(sourceFile)
65
+ : 'unknown';
66
+ // Find HTTP method decorator
67
+ let httpMethod = null;
68
+ let routePath = '';
69
+ for (const decorator of decorators) {
70
+ const decoratorName = getDecoratorName(decorator);
71
+ if (decoratorName && NESTJS_HTTP_DECORATORS.has(decoratorName)) {
72
+ httpMethod = NESTJS_HTTP_DECORATORS.get(decoratorName);
73
+ routePath = this.extractRoutePath(decorator);
74
+ break;
75
+ }
76
+ }
77
+ if (!httpMethod) {
78
+ return null;
79
+ }
80
+ // Build full route
81
+ const fullRoute = this.buildRoute(controllerPath, routePath);
82
+ // Get location
83
+ const location = getLineAndColumn(sourceFile, methodNode.getStart(sourceFile));
84
+ // Extract authorization info
85
+ const authorization = this.authExtractor.extract(decorators, {
86
+ classGuards: classAuth.guards,
87
+ classRoles: classAuth.roles,
88
+ isPublic: classAuth.isPublic,
89
+ });
90
+ return createEndpoint({
91
+ route: fullRoute,
92
+ method: httpMethod,
93
+ handlerName: methodName,
94
+ controllerName,
95
+ type: EndpointType.NestJS,
96
+ location: {
97
+ filePath,
98
+ line: location.line,
99
+ column: location.column,
100
+ },
101
+ authorization,
102
+ });
103
+ }
104
+ extractControllerPath(decorator) {
105
+ const args = getDecoratorArguments(decorator);
106
+ if (args.length === 0) {
107
+ return '';
108
+ }
109
+ const firstArg = args[0];
110
+ const path = getStringLiteralValue(firstArg);
111
+ return path ?? '';
112
+ }
113
+ extractRoutePath(decorator) {
114
+ const args = getDecoratorArguments(decorator);
115
+ if (args.length === 0) {
116
+ return '';
117
+ }
118
+ const firstArg = args[0];
119
+ const path = getStringLiteralValue(firstArg);
120
+ return path ?? '';
121
+ }
122
+ buildRoute(controllerPath, methodPath) {
123
+ let route = '';
124
+ if (controllerPath) {
125
+ route = controllerPath.startsWith('/') ? controllerPath : '/' + controllerPath;
126
+ }
127
+ else {
128
+ route = '/';
129
+ }
130
+ if (methodPath) {
131
+ const normalizedMethodPath = methodPath.startsWith('/')
132
+ ? methodPath
133
+ : '/' + methodPath;
134
+ route = route === '/' ? normalizedMethodPath : route + normalizedMethodPath;
135
+ }
136
+ // Normalize multiple slashes
137
+ route = route.replace(/\/+/g, '/');
138
+ // Ensure single leading slash
139
+ if (!route.startsWith('/')) {
140
+ route = '/' + route;
141
+ }
142
+ // Remove trailing slash (except for root)
143
+ if (route.length > 1 && route.endsWith('/')) {
144
+ route = route.slice(0, -1);
145
+ }
146
+ return route;
147
+ }
148
+ extractClassAuthInfo(decorators) {
149
+ const info = {
150
+ guards: [],
151
+ roles: [],
152
+ isPublic: false,
153
+ };
154
+ for (const decorator of decorators) {
155
+ const name = getDecoratorName(decorator);
156
+ if (name === 'UseGuards') {
157
+ const args = getDecoratorArguments(decorator);
158
+ for (const arg of args) {
159
+ if (ts.isIdentifier(arg)) {
160
+ info.guards.push(arg.text);
161
+ }
162
+ }
163
+ }
164
+ if (name === 'Roles') {
165
+ const args = getDecoratorArguments(decorator);
166
+ for (const arg of args) {
167
+ const value = getStringLiteralValue(arg);
168
+ if (value) {
169
+ info.roles.push(value);
170
+ }
171
+ }
172
+ }
173
+ if (name === 'Public' || name === 'AllowAnonymous' || name === 'SkipAuth') {
174
+ info.isPublic = true;
175
+ }
176
+ }
177
+ return info;
178
+ }
179
+ }
180
+ //# sourceMappingURL=nestjs-discoverer.js.map
@@ -0,0 +1,18 @@
1
+ export interface RouteGroup {
2
+ prefix: string;
3
+ middlewares: string[];
4
+ variableName: string;
5
+ filePath: string;
6
+ }
7
+ export declare class RouteGroupRegistry {
8
+ private groups;
9
+ private routerMounts;
10
+ registerGroup(filePath: string, variableName: string, prefix?: string, middlewares?: string[]): void;
11
+ registerRouterMount(filePath: string, appVariableName: string, prefix: string, routerName: string): void;
12
+ getGroup(filePath: string, variableName: string): RouteGroup | undefined;
13
+ getRouterPrefix(filePath: string, routerName: string): string;
14
+ getAllMiddlewares(filePath: string, variableName: string): string[];
15
+ clear(): void;
16
+ private makeKey;
17
+ }
18
+ //# sourceMappingURL=route-group-registry.d.ts.map
@@ -0,0 +1,50 @@
1
+ export class RouteGroupRegistry {
2
+ groups = new Map();
3
+ routerMounts = new Map();
4
+ registerGroup(filePath, variableName, prefix = '', middlewares = []) {
5
+ const key = this.makeKey(filePath, variableName);
6
+ const group = {
7
+ prefix,
8
+ middlewares,
9
+ variableName,
10
+ filePath,
11
+ };
12
+ if (!this.groups.has(key)) {
13
+ this.groups.set(key, []);
14
+ }
15
+ this.groups.get(key).push(group);
16
+ }
17
+ registerRouterMount(filePath, appVariableName, prefix, routerName) {
18
+ const key = this.makeKey(filePath, appVariableName);
19
+ if (!this.routerMounts.has(key)) {
20
+ this.routerMounts.set(key, []);
21
+ }
22
+ this.routerMounts.get(key).push({ prefix, routerName });
23
+ }
24
+ getGroup(filePath, variableName) {
25
+ const key = this.makeKey(filePath, variableName);
26
+ const groups = this.groups.get(key);
27
+ return groups?.[groups.length - 1];
28
+ }
29
+ getRouterPrefix(filePath, routerName) {
30
+ for (const [, mounts] of this.routerMounts) {
31
+ const mount = mounts.find((m) => m.routerName === routerName);
32
+ if (mount) {
33
+ return mount.prefix;
34
+ }
35
+ }
36
+ return '';
37
+ }
38
+ getAllMiddlewares(filePath, variableName) {
39
+ const group = this.getGroup(filePath, variableName);
40
+ return group?.middlewares ?? [];
41
+ }
42
+ clear() {
43
+ this.groups.clear();
44
+ this.routerMounts.clear();
45
+ }
46
+ makeKey(filePath, variableName) {
47
+ return `${filePath}::${variableName}`;
48
+ }
49
+ }
50
+ //# sourceMappingURL=route-group-registry.js.map
@@ -0,0 +1,17 @@
1
+ import { LicenseFeature } from './license-features.js';
2
+ export interface LicenseInfo {
3
+ key: string;
4
+ type: 'community' | 'pro' | 'enterprise';
5
+ features: LicenseFeature[];
6
+ expiresAt: Date | null;
7
+ activatedAt: Date;
8
+ machineId?: string;
9
+ }
10
+ export interface LicenseContext {
11
+ isLicensed: boolean;
12
+ info: LicenseInfo | null;
13
+ hasFeature(feature: LicenseFeature): boolean;
14
+ }
15
+ export declare function createCommunityContext(): LicenseContext;
16
+ export declare function createLicensedContext(info: LicenseInfo): LicenseContext;
17
+ //# sourceMappingURL=license-context.d.ts.map
@@ -0,0 +1,15 @@
1
+ export function createCommunityContext() {
2
+ return {
3
+ isLicensed: false,
4
+ info: null,
5
+ hasFeature: () => false,
6
+ };
7
+ }
8
+ export function createLicensedContext(info) {
9
+ return {
10
+ isLicensed: true,
11
+ info,
12
+ hasFeature: (feature) => info.features.includes(feature),
13
+ };
14
+ }
15
+ //# sourceMappingURL=license-context.js.map
@@ -0,0 +1,14 @@
1
+ export declare enum LicenseFeature {
2
+ DiffMode = "diff-mode",
3
+ HistoricalTracking = "historical-tracking",
4
+ RiskScoring = "risk-scoring",
5
+ AdvancedOwaspRules = "advanced-owasp-rules",
6
+ SecretsScanning = "secrets-scanning",
7
+ CustomRules = "custom-rules",
8
+ TeamReporting = "team-reporting",
9
+ CiCdIntegration = "ci-cd-integration"
10
+ }
11
+ export declare const proFeatures: LicenseFeature[];
12
+ export declare const enterpriseFeatures: LicenseFeature[];
13
+ export declare function getFeatureDescription(feature: LicenseFeature): string;
14
+ //# sourceMappingURL=license-features.d.ts.map
@@ -0,0 +1,47 @@
1
+ export var LicenseFeature;
2
+ (function (LicenseFeature) {
3
+ LicenseFeature["DiffMode"] = "diff-mode";
4
+ LicenseFeature["HistoricalTracking"] = "historical-tracking";
5
+ LicenseFeature["RiskScoring"] = "risk-scoring";
6
+ LicenseFeature["AdvancedOwaspRules"] = "advanced-owasp-rules";
7
+ LicenseFeature["SecretsScanning"] = "secrets-scanning";
8
+ LicenseFeature["CustomRules"] = "custom-rules";
9
+ LicenseFeature["TeamReporting"] = "team-reporting";
10
+ LicenseFeature["CiCdIntegration"] = "ci-cd-integration";
11
+ })(LicenseFeature || (LicenseFeature = {}));
12
+ export const proFeatures = [
13
+ LicenseFeature.DiffMode,
14
+ LicenseFeature.HistoricalTracking,
15
+ LicenseFeature.RiskScoring,
16
+ LicenseFeature.AdvancedOwaspRules,
17
+ ];
18
+ export const enterpriseFeatures = [
19
+ ...proFeatures,
20
+ LicenseFeature.SecretsScanning,
21
+ LicenseFeature.CustomRules,
22
+ LicenseFeature.TeamReporting,
23
+ LicenseFeature.CiCdIntegration,
24
+ ];
25
+ export function getFeatureDescription(feature) {
26
+ switch (feature) {
27
+ case LicenseFeature.DiffMode:
28
+ return 'Compare scans to track changes over time';
29
+ case LicenseFeature.HistoricalTracking:
30
+ return 'Store and analyze historical scan data';
31
+ case LicenseFeature.RiskScoring:
32
+ return 'Advanced risk scoring and prioritization';
33
+ case LicenseFeature.AdvancedOwaspRules:
34
+ return 'Additional OWASP security rules';
35
+ case LicenseFeature.SecretsScanning:
36
+ return 'Detect hardcoded secrets and credentials';
37
+ case LicenseFeature.CustomRules:
38
+ return 'Create custom security rules';
39
+ case LicenseFeature.TeamReporting:
40
+ return 'Team collaboration and reporting';
41
+ case LicenseFeature.CiCdIntegration:
42
+ return 'Advanced CI/CD pipeline integration';
43
+ default:
44
+ return feature;
45
+ }
46
+ }
47
+ //# sourceMappingURL=license-features.js.map
@@ -0,0 +1,13 @@
1
+ import { SecurityClassification } from './security-classification.js';
2
+ export interface AuthorizationInfo {
3
+ isAuthenticated: boolean;
4
+ isExplicitlyPublic: boolean;
5
+ roles: string[];
6
+ policies: string[];
7
+ middlewareChain: string[];
8
+ guardNames: string[];
9
+ classification: SecurityClassification;
10
+ }
11
+ export declare function createDefaultAuthorizationInfo(): AuthorizationInfo;
12
+ export declare function determineClassification(auth: Omit<AuthorizationInfo, 'classification'>): SecurityClassification;
13
+ //# sourceMappingURL=authorization-info.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { SecurityClassification } from './security-classification.js';
2
+ export function createDefaultAuthorizationInfo() {
3
+ return {
4
+ isAuthenticated: false,
5
+ isExplicitlyPublic: false,
6
+ roles: [],
7
+ policies: [],
8
+ middlewareChain: [],
9
+ guardNames: [],
10
+ classification: SecurityClassification.Public,
11
+ };
12
+ }
13
+ export function determineClassification(auth) {
14
+ if (auth.policies.length > 0) {
15
+ return SecurityClassification.PolicyRestricted;
16
+ }
17
+ if (auth.roles.length > 0) {
18
+ return SecurityClassification.RoleRestricted;
19
+ }
20
+ if (auth.isAuthenticated) {
21
+ return SecurityClassification.Authenticated;
22
+ }
23
+ return SecurityClassification.Public;
24
+ }
25
+ //# sourceMappingURL=authorization-info.js.map
@@ -0,0 +1,8 @@
1
+ export declare enum EndpointType {
2
+ Express = "express",
3
+ NestJS = "nestjs",
4
+ Fastify = "fastify",
5
+ Koa = "koa"
6
+ }
7
+ export declare function parseEndpointType(value: string): EndpointType | undefined;
8
+ //# sourceMappingURL=endpoint-type.d.ts.map
@@ -0,0 +1,12 @@
1
+ export var EndpointType;
2
+ (function (EndpointType) {
3
+ EndpointType["Express"] = "express";
4
+ EndpointType["NestJS"] = "nestjs";
5
+ EndpointType["Fastify"] = "fastify";
6
+ EndpointType["Koa"] = "koa";
7
+ })(EndpointType || (EndpointType = {}));
8
+ export function parseEndpointType(value) {
9
+ const normalized = value.toLowerCase();
10
+ return Object.values(EndpointType).find((t) => t === normalized);
11
+ }
12
+ //# sourceMappingURL=endpoint-type.js.map
@@ -0,0 +1,16 @@
1
+ import { AuthorizationInfo } from './authorization-info.js';
2
+ import { EndpointType } from './endpoint-type.js';
3
+ import { HttpMethod } from './http-method.js';
4
+ import { SourceLocation } from './source-location.js';
5
+ export interface Endpoint {
6
+ route: string;
7
+ method: HttpMethod;
8
+ handlerName: string;
9
+ controllerName?: string;
10
+ type: EndpointType;
11
+ location: SourceLocation;
12
+ authorization: AuthorizationInfo;
13
+ }
14
+ export declare function createEndpoint(partial: Partial<Endpoint> & Pick<Endpoint, 'route' | 'method' | 'type' | 'location'>): Endpoint;
15
+ export declare function formatEndpoint(endpoint: Endpoint): string;
16
+ //# sourceMappingURL=endpoint.d.ts.map
@@ -0,0 +1,16 @@
1
+ import { createDefaultAuthorizationInfo } from './authorization-info.js';
2
+ export function createEndpoint(partial) {
3
+ return {
4
+ route: partial.route,
5
+ method: partial.method,
6
+ handlerName: partial.handlerName ?? 'anonymous',
7
+ controllerName: partial.controllerName,
8
+ type: partial.type,
9
+ location: partial.location,
10
+ authorization: partial.authorization ?? createDefaultAuthorizationInfo(),
11
+ };
12
+ }
13
+ export function formatEndpoint(endpoint) {
14
+ return `${endpoint.method} ${endpoint.route}`;
15
+ }
16
+ //# sourceMappingURL=endpoint.js.map
@@ -0,0 +1,19 @@
1
+ import { Endpoint } from './endpoint.js';
2
+ import { Severity } from './severity.js';
3
+ import { SourceLocation } from './source-location.js';
4
+ export interface Finding {
5
+ ruleId: string;
6
+ ruleName: string;
7
+ severity: Severity;
8
+ message: string;
9
+ endpoint: Endpoint;
10
+ location: SourceLocation;
11
+ recommendation: string;
12
+ suppressed: boolean;
13
+ suppressionReason?: string;
14
+ }
15
+ export declare function createFinding(partial: Omit<Finding, 'suppressed' | 'suppressionReason'> & {
16
+ suppressed?: boolean;
17
+ suppressionReason?: string;
18
+ }): Finding;
19
+ //# sourceMappingURL=finding.d.ts.map
@@ -0,0 +1,8 @@
1
+ export function createFinding(partial) {
2
+ return {
3
+ ...partial,
4
+ suppressed: partial.suppressed ?? false,
5
+ suppressionReason: partial.suppressionReason,
6
+ };
7
+ }
8
+ //# sourceMappingURL=finding.js.map
@@ -0,0 +1,14 @@
1
+ export declare enum HttpMethod {
2
+ GET = "GET",
3
+ POST = "POST",
4
+ PUT = "PUT",
5
+ DELETE = "DELETE",
6
+ PATCH = "PATCH",
7
+ OPTIONS = "OPTIONS",
8
+ HEAD = "HEAD",
9
+ ALL = "ALL"
10
+ }
11
+ export declare const writeMethods: Set<HttpMethod>;
12
+ export declare function isWriteMethod(method: HttpMethod): boolean;
13
+ export declare function parseHttpMethod(value: string): HttpMethod | undefined;
14
+ //# sourceMappingURL=http-method.d.ts.map
@@ -0,0 +1,25 @@
1
+ export var HttpMethod;
2
+ (function (HttpMethod) {
3
+ HttpMethod["GET"] = "GET";
4
+ HttpMethod["POST"] = "POST";
5
+ HttpMethod["PUT"] = "PUT";
6
+ HttpMethod["DELETE"] = "DELETE";
7
+ HttpMethod["PATCH"] = "PATCH";
8
+ HttpMethod["OPTIONS"] = "OPTIONS";
9
+ HttpMethod["HEAD"] = "HEAD";
10
+ HttpMethod["ALL"] = "ALL";
11
+ })(HttpMethod || (HttpMethod = {}));
12
+ export const writeMethods = new Set([
13
+ HttpMethod.POST,
14
+ HttpMethod.PUT,
15
+ HttpMethod.DELETE,
16
+ HttpMethod.PATCH,
17
+ ]);
18
+ export function isWriteMethod(method) {
19
+ return writeMethods.has(method);
20
+ }
21
+ export function parseHttpMethod(value) {
22
+ const normalized = value.toUpperCase();
23
+ return Object.values(HttpMethod).find((m) => m === normalized);
24
+ }
25
+ //# sourceMappingURL=http-method.js.map
@@ -0,0 +1,10 @@
1
+ export * from './authorization-info.js';
2
+ export * from './endpoint.js';
3
+ export * from './endpoint-type.js';
4
+ export * from './finding.js';
5
+ export * from './http-method.js';
6
+ export * from './scan-result.js';
7
+ export * from './security-classification.js';
8
+ export * from './severity.js';
9
+ export * from './source-location.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,10 @@
1
+ export * from './authorization-info.js';
2
+ export * from './endpoint.js';
3
+ export * from './endpoint-type.js';
4
+ export * from './finding.js';
5
+ export * from './http-method.js';
6
+ export * from './scan-result.js';
7
+ export * from './security-classification.js';
8
+ export * from './severity.js';
9
+ export * from './source-location.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,21 @@
1
+ import { Endpoint } from './endpoint.js';
2
+ import { Finding } from './finding.js';
3
+ import { Severity } from './severity.js';
4
+ export interface ScanResult {
5
+ projectPath: string;
6
+ scanDate: Date;
7
+ endpoints: Endpoint[];
8
+ findings: Finding[];
9
+ filesScanned: number;
10
+ scanDurationMs: number;
11
+ }
12
+ export interface ScanSummary {
13
+ totalEndpoints: number;
14
+ totalFindings: number;
15
+ findingsBySeverity: Record<Severity, number>;
16
+ suppressedFindings: number;
17
+ }
18
+ export declare function createScanResult(partial: Partial<ScanResult> & Pick<ScanResult, 'projectPath'>): ScanResult;
19
+ export declare function getScanSummary(result: ScanResult): ScanSummary;
20
+ export declare function getHighestSeverity(result: ScanResult): Severity | null;
21
+ //# sourceMappingURL=scan-result.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { Severity, severityOrder } from './severity.js';
2
+ export function createScanResult(partial) {
3
+ return {
4
+ projectPath: partial.projectPath,
5
+ scanDate: partial.scanDate ?? new Date(),
6
+ endpoints: partial.endpoints ?? [],
7
+ findings: partial.findings ?? [],
8
+ filesScanned: partial.filesScanned ?? 0,
9
+ scanDurationMs: partial.scanDurationMs ?? 0,
10
+ };
11
+ }
12
+ export function getScanSummary(result) {
13
+ const activeFindings = result.findings.filter((f) => !f.suppressed);
14
+ const findingsBySeverity = Object.values(Severity).reduce((acc, severity) => {
15
+ acc[severity] = activeFindings.filter((f) => f.severity === severity).length;
16
+ return acc;
17
+ }, {});
18
+ return {
19
+ totalEndpoints: result.endpoints.length,
20
+ totalFindings: activeFindings.length,
21
+ findingsBySeverity,
22
+ suppressedFindings: result.findings.filter((f) => f.suppressed).length,
23
+ };
24
+ }
25
+ export function getHighestSeverity(result) {
26
+ const activeFindings = result.findings.filter((f) => !f.suppressed);
27
+ if (activeFindings.length === 0)
28
+ return null;
29
+ return activeFindings.reduce((highest, finding) => {
30
+ return severityOrder[finding.severity] > severityOrder[highest]
31
+ ? finding.severity
32
+ : highest;
33
+ }, activeFindings[0].severity);
34
+ }
35
+ //# sourceMappingURL=scan-result.js.map
@@ -0,0 +1,8 @@
1
+ export declare enum SecurityClassification {
2
+ Public = "public",
3
+ Authenticated = "authenticated",
4
+ RoleRestricted = "role-restricted",
5
+ PolicyRestricted = "policy-restricted"
6
+ }
7
+ export declare function parseSecurityClassification(value: string): SecurityClassification | undefined;
8
+ //# sourceMappingURL=security-classification.d.ts.map
@@ -0,0 +1,12 @@
1
+ export var SecurityClassification;
2
+ (function (SecurityClassification) {
3
+ SecurityClassification["Public"] = "public";
4
+ SecurityClassification["Authenticated"] = "authenticated";
5
+ SecurityClassification["RoleRestricted"] = "role-restricted";
6
+ SecurityClassification["PolicyRestricted"] = "policy-restricted";
7
+ })(SecurityClassification || (SecurityClassification = {}));
8
+ export function parseSecurityClassification(value) {
9
+ const normalized = value.toLowerCase();
10
+ return Object.values(SecurityClassification).find((c) => c === normalized);
11
+ }
12
+ //# sourceMappingURL=security-classification.js.map
@@ -0,0 +1,11 @@
1
+ export declare enum Severity {
2
+ Info = "info",
3
+ Low = "low",
4
+ Medium = "medium",
5
+ High = "high",
6
+ Critical = "critical"
7
+ }
8
+ export declare const severityOrder: Record<Severity, number>;
9
+ export declare function compareSeverity(a: Severity, b: Severity): number;
10
+ export declare function parseSeverity(value: string): Severity | undefined;
11
+ //# sourceMappingURL=severity.d.ts.map
@@ -0,0 +1,23 @@
1
+ export var Severity;
2
+ (function (Severity) {
3
+ Severity["Info"] = "info";
4
+ Severity["Low"] = "low";
5
+ Severity["Medium"] = "medium";
6
+ Severity["High"] = "high";
7
+ Severity["Critical"] = "critical";
8
+ })(Severity || (Severity = {}));
9
+ export const severityOrder = {
10
+ [Severity.Info]: 0,
11
+ [Severity.Low]: 1,
12
+ [Severity.Medium]: 2,
13
+ [Severity.High]: 3,
14
+ [Severity.Critical]: 4,
15
+ };
16
+ export function compareSeverity(a, b) {
17
+ return severityOrder[a] - severityOrder[b];
18
+ }
19
+ export function parseSeverity(value) {
20
+ const normalized = value.toLowerCase();
21
+ return Object.values(Severity).find((s) => s === normalized);
22
+ }
23
+ //# sourceMappingURL=severity.js.map