@hstm-labs/forge-security-generator 0.1.11
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/README.md +39 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/security-generate-stage.d.ts +58 -0
- package/dist/security-generate-stage.d.ts.map +1 -0
- package/dist/security-generate-stage.js +195 -0
- package/dist/security-generate-stage.js.map +1 -0
- package/dist/types.d.ts +95 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/validator.d.ts +47 -0
- package/dist/validator.d.ts.map +1 -0
- package/dist/validator.js +219 -0
- package/dist/validator.js.map +1 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# @hstm-labs/forge-security-generator
|
|
2
|
+
|
|
3
|
+
Security layer generation stage for Forge — produces authentication models, RBAC configurations, security middleware, input validation rules, and security headers from architecture output.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @hstm-labs/forge-security-generator
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Public API
|
|
12
|
+
|
|
13
|
+
### Types
|
|
14
|
+
|
|
15
|
+
- `SecurityArtifact` — complete security layer output
|
|
16
|
+
- `AuthModel` — authentication strategy and configuration
|
|
17
|
+
- `RbacConfig`, `RoleDefinition`, `PermissionMapping` — role-based access control
|
|
18
|
+
- `SecurityMiddleware` — middleware implementation
|
|
19
|
+
- `InputValidationRule` — input validation definitions
|
|
20
|
+
- `SecurityHeadersConfig`, `SecurityHeader` — HTTP security headers
|
|
21
|
+
|
|
22
|
+
### Classes
|
|
23
|
+
|
|
24
|
+
- `SecurityGenerateStage` — pipeline stage implementing `PipelineStage` interface
|
|
25
|
+
- `SecurityOutputValidator` — validates LLM-produced security output (includes hardcoded secret scanning)
|
|
26
|
+
|
|
27
|
+
## Usage
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { SecurityGenerateStage } from '@hstm-labs/forge-security-generator';
|
|
31
|
+
|
|
32
|
+
const stage = new SecurityGenerateStage();
|
|
33
|
+
const result = await stage.execute(input);
|
|
34
|
+
// result.data contains SecurityArtifact
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## License
|
|
38
|
+
|
|
39
|
+
[MIT](../../LICENSE)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type { SecurityArtifact, AuthModel, RbacConfig, RoleDefinition, PermissionMapping, SecurityMiddleware, InputValidationRule, SecurityHeadersConfig, SecurityHeader, } from './types.js';
|
|
2
|
+
export { SecurityGenerateStage } from './security-generate-stage.js';
|
|
3
|
+
export { SecurityOutputValidator } from './validator.js';
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security generation pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Produces authentication middleware, RBAC rules, security middleware,
|
|
5
|
+
* input validation schemas, and security headers configuration from
|
|
6
|
+
* specification requirements, API endpoints, architecture, and
|
|
7
|
+
* technology profile. Follows the stage implementation pattern
|
|
8
|
+
* established by prior generation stages.
|
|
9
|
+
*
|
|
10
|
+
* In API mode, renders prompt templates, calls the LLM via
|
|
11
|
+
* {@link ApiStageExecutor}, validates the output, unpacks individual
|
|
12
|
+
* files, and returns the parsed {@link SecurityArtifact} in the
|
|
13
|
+
* stage output data.
|
|
14
|
+
*
|
|
15
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
16
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
17
|
+
*/
|
|
18
|
+
import type { StageName } from '@hstm-labs/forge-common';
|
|
19
|
+
import type { PipelineStage, PipelineStageInput, PipelineStageOutput, PipelineContext, AgentPromptContext } from '@hstm-labs/forge-core';
|
|
20
|
+
/**
|
|
21
|
+
* Pipeline stage that generates the security layer.
|
|
22
|
+
*
|
|
23
|
+
* Produces a {@link SecurityArtifact} containing auth model, RBAC
|
|
24
|
+
* configuration, security middleware, input validation, and security
|
|
25
|
+
* headers. Each element is written as a separate file artifact.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* const stage = new SecurityGenerateStage();
|
|
30
|
+
* const output = await stage.execute(input, context);
|
|
31
|
+
* const security = output.data?.security as SecurityArtifact;
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare class SecurityGenerateStage implements PipelineStage {
|
|
35
|
+
readonly name: StageName;
|
|
36
|
+
readonly dependsOn: StageName[];
|
|
37
|
+
readonly requiresLLM = true;
|
|
38
|
+
/**
|
|
39
|
+
* Return rendered agent-mode prompt context using `security-generate-agent.hbs`.
|
|
40
|
+
*
|
|
41
|
+
* @param input - Input from prior stages
|
|
42
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
43
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
44
|
+
*/
|
|
45
|
+
getAgentContext(input: PipelineStageInput, context: PipelineContext): AgentPromptContext;
|
|
46
|
+
/**
|
|
47
|
+
* Execute the security generation stage.
|
|
48
|
+
*
|
|
49
|
+
* @param input - Input from prior stages (expects validate + architect + services-generate output)
|
|
50
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
51
|
+
* @returns Stage output with security artifacts and parsed SecurityArtifact in data
|
|
52
|
+
* @throws {ForgeError} FORGE-PIPE-003 if dependency stage output is missing
|
|
53
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
54
|
+
* @throws {ForgeError} FORGE-GEN-003 if max retries exhausted
|
|
55
|
+
*/
|
|
56
|
+
execute(input: PipelineStageInput, context: PipelineContext): Promise<PipelineStageOutput>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=security-generate-stage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-generate-stage.d.ts","sourceRoot":"","sources":["../src/security-generate-stage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,KAAK,EACV,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EAEf,kBAAkB,EACnB,MAAM,uBAAuB,CAAC;AAW/B;;;;;;;;;;;;;GAaG;AACH,qBAAa,qBAAsB,YAAW,aAAa;IACzD,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAuB;IAC/C,QAAQ,CAAC,SAAS,EAAE,SAAS,EAAE,CAAsC;IACrE,QAAQ,CAAC,WAAW,QAAQ;IAE5B;;;;;;OAMG;IACH,eAAe,CACb,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,kBAAkB;IA4CrB;;;;;;;;;OASG;IACG,OAAO,CACX,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,mBAAmB,CAAC;CAoLhC"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security generation pipeline stage.
|
|
3
|
+
*
|
|
4
|
+
* Produces authentication middleware, RBAC rules, security middleware,
|
|
5
|
+
* input validation schemas, and security headers configuration from
|
|
6
|
+
* specification requirements, API endpoints, architecture, and
|
|
7
|
+
* technology profile. Follows the stage implementation pattern
|
|
8
|
+
* established by prior generation stages.
|
|
9
|
+
*
|
|
10
|
+
* In API mode, renders prompt templates, calls the LLM via
|
|
11
|
+
* {@link ApiStageExecutor}, validates the output, unpacks individual
|
|
12
|
+
* files, and returns the parsed {@link SecurityArtifact} in the
|
|
13
|
+
* stage output data.
|
|
14
|
+
*
|
|
15
|
+
* In agent mode, the pipeline runner intercepts before calling
|
|
16
|
+
* `execute()` and exports a prompt via {@link AgentStageExecutor}.
|
|
17
|
+
*/
|
|
18
|
+
import { join, dirname } from 'node:path';
|
|
19
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
20
|
+
import { ForgeError, ErrorCodes, hashContent } from '@hstm-labs/forge-common';
|
|
21
|
+
import { ApiStageExecutor } from '@hstm-labs/forge-core';
|
|
22
|
+
import { loadProfile } from '@hstm-labs/forge-profiles';
|
|
23
|
+
import { loadTemplate, renderTemplate } from '@hstm-labs/forge-templates';
|
|
24
|
+
import { SecurityOutputValidator } from './validator.js';
|
|
25
|
+
/**
|
|
26
|
+
* Pipeline stage that generates the security layer.
|
|
27
|
+
*
|
|
28
|
+
* Produces a {@link SecurityArtifact} containing auth model, RBAC
|
|
29
|
+
* configuration, security middleware, input validation, and security
|
|
30
|
+
* headers. Each element is written as a separate file artifact.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* const stage = new SecurityGenerateStage();
|
|
35
|
+
* const output = await stage.execute(input, context);
|
|
36
|
+
* const security = output.data?.security as SecurityArtifact;
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export class SecurityGenerateStage {
|
|
40
|
+
name = 'security-generate';
|
|
41
|
+
dependsOn = ['architect', 'services-generate'];
|
|
42
|
+
requiresLLM = true;
|
|
43
|
+
/**
|
|
44
|
+
* Return rendered agent-mode prompt context using `security-generate-agent.hbs`.
|
|
45
|
+
*
|
|
46
|
+
* @param input - Input from prior stages
|
|
47
|
+
* @param context - Pipeline context with config, workspace, and runId
|
|
48
|
+
* @returns Agent prompt context with rendered template and output schema
|
|
49
|
+
*/
|
|
50
|
+
getAgentContext(input, context) {
|
|
51
|
+
const parsedSpec = input['validate']?.data?.['parsedSpec'];
|
|
52
|
+
const architecture = input['architect']?.data?.['architecture'];
|
|
53
|
+
const api = input['services-generate']?.data?.['api'];
|
|
54
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
55
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'security-generate', 'artifacts');
|
|
56
|
+
const agentTemplate = loadTemplate('security-generate-agent', context.workspace.rootDir);
|
|
57
|
+
const rendered = renderTemplate(agentTemplate, {
|
|
58
|
+
spec: (parsedSpec ?? {}),
|
|
59
|
+
profile: profile,
|
|
60
|
+
architecture: (architecture ?? {}),
|
|
61
|
+
api: (api ?? {}),
|
|
62
|
+
stage: { outputDir },
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
prompt: rendered.content,
|
|
66
|
+
outputSchema: { format: 'json' },
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Execute the security generation stage.
|
|
71
|
+
*
|
|
72
|
+
* @param input - Input from prior stages (expects validate + architect + services-generate output)
|
|
73
|
+
* @param context - Pipeline context with config, workspace, and adapter
|
|
74
|
+
* @returns Stage output with security artifacts and parsed SecurityArtifact in data
|
|
75
|
+
* @throws {ForgeError} FORGE-PIPE-003 if dependency stage output is missing
|
|
76
|
+
* @throws {ForgeError} FORGE-PIPE-001 if adapter is missing
|
|
77
|
+
* @throws {ForgeError} FORGE-GEN-003 if max retries exhausted
|
|
78
|
+
*/
|
|
79
|
+
async execute(input, context) {
|
|
80
|
+
// 1. Get parsed spec from validate stage output
|
|
81
|
+
const validateOutput = input['validate'];
|
|
82
|
+
if (validateOutput === undefined) {
|
|
83
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Security generate stage requires 'validate' stage output, but it was not found. " +
|
|
84
|
+
'Ensure the validate stage runs before the security-generate stage.');
|
|
85
|
+
}
|
|
86
|
+
const parsedSpec = validateOutput.data?.['parsedSpec'];
|
|
87
|
+
if (parsedSpec === undefined) {
|
|
88
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Validate stage did not produce a parsed specification in its output data. ' +
|
|
89
|
+
'Ensure the validate stage includes parsedSpec in its data output.');
|
|
90
|
+
}
|
|
91
|
+
// 2. Get architecture from architect stage output
|
|
92
|
+
const architectOutput = input['architect'];
|
|
93
|
+
if (architectOutput === undefined) {
|
|
94
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Security generate stage requires 'architect' stage output, but it was not found. " +
|
|
95
|
+
'Ensure the architect stage runs before the security-generate stage.');
|
|
96
|
+
}
|
|
97
|
+
const architecture = architectOutput.data?.['architecture'];
|
|
98
|
+
if (architecture === undefined) {
|
|
99
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Architect stage did not produce an architecture in its output data. ' +
|
|
100
|
+
'Ensure the architect stage includes architecture in its data output.');
|
|
101
|
+
}
|
|
102
|
+
// 3. Get API artifact from services-generate stage output
|
|
103
|
+
const servicesGenerateOutput = input['services-generate'];
|
|
104
|
+
if (servicesGenerateOutput === undefined) {
|
|
105
|
+
throw new ForgeError(ErrorCodes.PIPE.DEPENDENCY_UNMET, "Security generate stage requires 'services-generate' stage output, but it was not found. " +
|
|
106
|
+
'Ensure the services-generate stage runs before the security-generate stage.');
|
|
107
|
+
}
|
|
108
|
+
const api = servicesGenerateOutput.data?.['api'];
|
|
109
|
+
if (api === undefined) {
|
|
110
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Services generate stage did not produce an API artifact in its output data. ' +
|
|
111
|
+
'Ensure the services-generate stage includes api in its data output.');
|
|
112
|
+
}
|
|
113
|
+
// 4. Load profile
|
|
114
|
+
const profile = loadProfile(context.config.profileName, context.workspace.rootDir);
|
|
115
|
+
// 5. Compute output directory
|
|
116
|
+
const outputDir = join(context.workspace.forgeDir, 'runs', context.runId, 'stages', 'security-generate', 'artifacts');
|
|
117
|
+
// 6. Load and render templates
|
|
118
|
+
const systemTemplate = loadTemplate('security-generate-system', context.workspace.rootDir);
|
|
119
|
+
const userTemplate = loadTemplate('security-generate-user', context.workspace.rootDir);
|
|
120
|
+
const templateContext = {
|
|
121
|
+
spec: parsedSpec,
|
|
122
|
+
profile: profile,
|
|
123
|
+
architecture: architecture,
|
|
124
|
+
api: api,
|
|
125
|
+
stage: {
|
|
126
|
+
outputDir,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
const systemPrompt = renderTemplate(systemTemplate, templateContext);
|
|
130
|
+
const userPrompt = renderTemplate(userTemplate, templateContext);
|
|
131
|
+
// 7. Execute via API mode StageExecutor
|
|
132
|
+
if (context.adapter === undefined) {
|
|
133
|
+
throw new ForgeError(ErrorCodes.PIPE.STAGE_FAILURE, 'Security generate stage requires an LLM adapter in API mode, but none was provided. ' +
|
|
134
|
+
'Configure an LLM provider in forge.config.json or use agent mode.');
|
|
135
|
+
}
|
|
136
|
+
const entityNames = architecture.dataModel.map((e) => e.name);
|
|
137
|
+
const validator = new SecurityOutputValidator(entityNames);
|
|
138
|
+
const executor = new ApiStageExecutor({
|
|
139
|
+
adapter: context.adapter,
|
|
140
|
+
validator,
|
|
141
|
+
retryPolicy: {
|
|
142
|
+
maxRetries: 3,
|
|
143
|
+
backoffMs: 1000,
|
|
144
|
+
includeErrorInRetry: true,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
const result = await executor.execute({
|
|
148
|
+
prompt: userPrompt.content,
|
|
149
|
+
systemPrompt: systemPrompt.content,
|
|
150
|
+
stageName: 'security-generate',
|
|
151
|
+
outputDir,
|
|
152
|
+
runId: context.runId,
|
|
153
|
+
mode: 'api',
|
|
154
|
+
outputSchema: { format: 'json' },
|
|
155
|
+
});
|
|
156
|
+
// 8. Parse SecurityArtifact from LLM output
|
|
157
|
+
const rawContent = result.artifacts[0]?.content ?? '';
|
|
158
|
+
const securityArtifact = JSON.parse(rawContent);
|
|
159
|
+
// 9. Write individual files to artifacts directory
|
|
160
|
+
const artifacts = [];
|
|
161
|
+
const writeArtifact = (filePath, content) => {
|
|
162
|
+
const fullPath = join(outputDir, filePath);
|
|
163
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
164
|
+
writeFileSync(fullPath, content, 'utf-8');
|
|
165
|
+
artifacts.push({
|
|
166
|
+
filePath,
|
|
167
|
+
content,
|
|
168
|
+
contentHash: hashContent(content),
|
|
169
|
+
sizeBytes: Buffer.byteLength(content, 'utf-8'),
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
// Auth model
|
|
173
|
+
writeArtifact(securityArtifact.authModel.fileName, securityArtifact.authModel.content);
|
|
174
|
+
// RBAC middleware
|
|
175
|
+
writeArtifact(securityArtifact.rbac.fileName, securityArtifact.rbac.content);
|
|
176
|
+
// Security middleware files
|
|
177
|
+
for (const mw of securityArtifact.middleware) {
|
|
178
|
+
writeArtifact(mw.fileName, mw.content);
|
|
179
|
+
}
|
|
180
|
+
// Input validation files
|
|
181
|
+
for (const rule of securityArtifact.inputValidation) {
|
|
182
|
+
writeArtifact(rule.fileName, rule.content);
|
|
183
|
+
}
|
|
184
|
+
// Security headers config
|
|
185
|
+
writeArtifact(securityArtifact.securityHeaders.fileName, securityArtifact.securityHeaders.content);
|
|
186
|
+
// 10. Return PipelineStageOutput with all artifacts and data
|
|
187
|
+
return {
|
|
188
|
+
artifacts,
|
|
189
|
+
data: {
|
|
190
|
+
security: securityArtifact,
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=security-generate-stage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-generate-stage.js","sourceRoot":"","sources":["../src/security-generate-stage.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security artifact type definitions for the Forge security generation stage.
|
|
3
|
+
*
|
|
4
|
+
* Defines the output schema produced by the security generation stage,
|
|
5
|
+
* including authentication, RBAC, middleware, input validation, and
|
|
6
|
+
* security headers.
|
|
7
|
+
*/
|
|
8
|
+
/** Complete security layer output produced by the security-generate stage. */
|
|
9
|
+
export interface SecurityArtifact {
|
|
10
|
+
/** Authentication model. */
|
|
11
|
+
authModel: AuthModel;
|
|
12
|
+
/** Role-based access control definitions. */
|
|
13
|
+
rbac: RbacConfig;
|
|
14
|
+
/** Security middleware implementations. */
|
|
15
|
+
middleware: SecurityMiddleware[];
|
|
16
|
+
/** Input validation schemas/rules. */
|
|
17
|
+
inputValidation: InputValidationRule[];
|
|
18
|
+
/** Security headers configuration. */
|
|
19
|
+
securityHeaders: SecurityHeadersConfig;
|
|
20
|
+
}
|
|
21
|
+
/** Authentication model configuration. */
|
|
22
|
+
export interface AuthModel {
|
|
23
|
+
/** Auth strategy (e.g., "jwt", "session", "oauth2", "none"). */
|
|
24
|
+
strategy: string;
|
|
25
|
+
/** Auth implementation file. */
|
|
26
|
+
fileName: string;
|
|
27
|
+
/** File content (source code). */
|
|
28
|
+
content: string;
|
|
29
|
+
/** Description. */
|
|
30
|
+
description: string;
|
|
31
|
+
}
|
|
32
|
+
/** Role-based access control configuration. */
|
|
33
|
+
export interface RbacConfig {
|
|
34
|
+
/** Defined roles. */
|
|
35
|
+
roles: RoleDefinition[];
|
|
36
|
+
/** Permission mappings (role → endpoint permissions). */
|
|
37
|
+
permissions: PermissionMapping[];
|
|
38
|
+
/** RBAC middleware/guard file. */
|
|
39
|
+
fileName: string;
|
|
40
|
+
/** File content. */
|
|
41
|
+
content: string;
|
|
42
|
+
}
|
|
43
|
+
/** A role definition. */
|
|
44
|
+
export interface RoleDefinition {
|
|
45
|
+
/** Role name (e.g., "admin", "user", "guest"). */
|
|
46
|
+
name: string;
|
|
47
|
+
/** Description. */
|
|
48
|
+
description: string;
|
|
49
|
+
}
|
|
50
|
+
/** A permission mapping from role to allowed endpoints. */
|
|
51
|
+
export interface PermissionMapping {
|
|
52
|
+
/** Role name. */
|
|
53
|
+
role: string;
|
|
54
|
+
/** Allowed endpoint patterns (e.g., "GET /api/pets", "* /api/admin/*"). */
|
|
55
|
+
allowedEndpoints: string[];
|
|
56
|
+
}
|
|
57
|
+
/** A security middleware implementation. */
|
|
58
|
+
export interface SecurityMiddleware {
|
|
59
|
+
/** Middleware name (e.g., "authGuard", "rateLimiter", "cors"). */
|
|
60
|
+
name: string;
|
|
61
|
+
/** File name. */
|
|
62
|
+
fileName: string;
|
|
63
|
+
/** File content. */
|
|
64
|
+
content: string;
|
|
65
|
+
/** Description. */
|
|
66
|
+
description: string;
|
|
67
|
+
}
|
|
68
|
+
/** An input validation rule for an entity or endpoint. */
|
|
69
|
+
export interface InputValidationRule {
|
|
70
|
+
/** Entity or endpoint this validation covers. */
|
|
71
|
+
target: string;
|
|
72
|
+
/** Validation type (e.g., "request-body", "query-params", "path-params"). */
|
|
73
|
+
type: string;
|
|
74
|
+
/** File name. */
|
|
75
|
+
fileName: string;
|
|
76
|
+
/** File content. */
|
|
77
|
+
content: string;
|
|
78
|
+
}
|
|
79
|
+
/** Security headers configuration. */
|
|
80
|
+
export interface SecurityHeadersConfig {
|
|
81
|
+
/** Headers configuration file. */
|
|
82
|
+
fileName: string;
|
|
83
|
+
/** File content. */
|
|
84
|
+
content: string;
|
|
85
|
+
/** Headers applied. */
|
|
86
|
+
headers: SecurityHeader[];
|
|
87
|
+
}
|
|
88
|
+
/** A single security header definition. */
|
|
89
|
+
export interface SecurityHeader {
|
|
90
|
+
/** Header name. */
|
|
91
|
+
name: string;
|
|
92
|
+
/** Header value. */
|
|
93
|
+
value: string;
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,8EAA8E;AAC9E,MAAM,WAAW,gBAAgB;IAC/B,4BAA4B;IAC5B,SAAS,EAAE,SAAS,CAAC;IACrB,6CAA6C;IAC7C,IAAI,EAAE,UAAU,CAAC;IACjB,2CAA2C;IAC3C,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,sCAAsC;IACtC,eAAe,EAAE,mBAAmB,EAAE,CAAC;IACvC,sCAAsC;IACtC,eAAe,EAAE,qBAAqB,CAAC;CACxC;AAMD,0CAA0C;AAC1C,MAAM,WAAW,SAAS;IACxB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,+CAA+C;AAC/C,MAAM,WAAW,UAAU;IACzB,qBAAqB;IACrB,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,yDAAyD;IACzD,WAAW,EAAE,iBAAiB,EAAE,CAAC;IACjC,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,yBAAyB;AACzB,MAAM,WAAW,cAAc;IAC7B,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,iBAAiB;IAChC,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAMD,4CAA4C;AAC5C,MAAM,WAAW,kBAAkB;IACjC,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAMD,0DAA0D;AAC1D,MAAM,WAAW,mBAAmB;IAClC,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,sCAAsC;AACtC,MAAM,WAAW,qBAAqB;IACpC,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,2CAA2C;AAC3C,MAAM,WAAW,cAAc;IAC7B,mBAAmB;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;CACf"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security artifact type definitions for the Forge security generation stage.
|
|
3
|
+
*
|
|
4
|
+
* Defines the output schema produced by the security generation stage,
|
|
5
|
+
* including authentication, RBAC, middleware, input validation, and
|
|
6
|
+
* security headers.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security output validator for the Forge security generation stage.
|
|
3
|
+
*
|
|
4
|
+
* Validates LLM-generated security output against the
|
|
5
|
+
* {@link SecurityArtifact} schema with structural, semantic, and
|
|
6
|
+
* security checks.
|
|
7
|
+
*/
|
|
8
|
+
import type { OutputValidator, OutputSchema, ValidationResult } from '@hstm-labs/forge-core';
|
|
9
|
+
/**
|
|
10
|
+
* Validator for security generation stage output.
|
|
11
|
+
*
|
|
12
|
+
* Performs the following checks:
|
|
13
|
+
* 1. JSON parse validation
|
|
14
|
+
* 2. Required top-level keys
|
|
15
|
+
* 3. AuthModel has strategy, fileName, content
|
|
16
|
+
* 4. RBAC has non-empty roles and permissions arrays
|
|
17
|
+
* 5. Middleware is non-empty array
|
|
18
|
+
* 6. Security check: scan content for hardcoded secrets
|
|
19
|
+
* 7. SecurityHeaders has non-empty headers array
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const validator = new SecurityOutputValidator(['User', 'Post']);
|
|
24
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare class SecurityOutputValidator implements OutputValidator {
|
|
28
|
+
private readonly entityNames;
|
|
29
|
+
/**
|
|
30
|
+
* @param entityNames - Entity names for endpoint coverage reference
|
|
31
|
+
*/
|
|
32
|
+
constructor(entityNames: string[]);
|
|
33
|
+
/**
|
|
34
|
+
* Validate security output against the SecurityArtifact schema.
|
|
35
|
+
*
|
|
36
|
+
* @param output - Raw LLM output text
|
|
37
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
38
|
+
* @returns Validation result with descriptive errors
|
|
39
|
+
*/
|
|
40
|
+
validate(output: string, _schema: OutputSchema): ValidationResult;
|
|
41
|
+
/**
|
|
42
|
+
* Recursively scan all string fields for hardcoded secret patterns.
|
|
43
|
+
* Best-effort check, not exhaustive.
|
|
44
|
+
*/
|
|
45
|
+
private scanForSecrets;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EACZ,gBAAgB,EAEjB,MAAM,uBAAuB,CAAC;AA0B/B;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,uBAAwB,YAAW,eAAe;IAC7D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAW;IAEvC;;OAEG;gBACS,WAAW,EAAE,MAAM,EAAE;IAIjC;;;;;;OAMG;IAEH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,gBAAgB;IAyIjE;;;OAGG;IACH,OAAO,CAAC,cAAc;CA2BvB"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security output validator for the Forge security generation stage.
|
|
3
|
+
*
|
|
4
|
+
* Validates LLM-generated security output against the
|
|
5
|
+
* {@link SecurityArtifact} schema with structural, semantic, and
|
|
6
|
+
* security checks.
|
|
7
|
+
*/
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Constants
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
const REQUIRED_TOP_LEVEL_KEYS = [
|
|
12
|
+
'authModel',
|
|
13
|
+
'rbac',
|
|
14
|
+
'middleware',
|
|
15
|
+
'inputValidation',
|
|
16
|
+
'securityHeaders',
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Best-effort regex patterns to detect hardcoded secrets in content.
|
|
20
|
+
* Matches values that look like real credentials rather than placeholders.
|
|
21
|
+
*/
|
|
22
|
+
const HARDCODED_SECRET_PATTERNS = [
|
|
23
|
+
/(?:password|passwd|secret|api_key|apikey|token|auth)\s*[=:]\s*["']?(?!(\$\{|<|CHANGEME|changeme|placeholder|your_|TODO|xxx|process\.env))[A-Za-z0-9!@#$%^&*]{8,}["']?/i,
|
|
24
|
+
];
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Implementation
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
/**
|
|
29
|
+
* Validator for security generation stage output.
|
|
30
|
+
*
|
|
31
|
+
* Performs the following checks:
|
|
32
|
+
* 1. JSON parse validation
|
|
33
|
+
* 2. Required top-level keys
|
|
34
|
+
* 3. AuthModel has strategy, fileName, content
|
|
35
|
+
* 4. RBAC has non-empty roles and permissions arrays
|
|
36
|
+
* 5. Middleware is non-empty array
|
|
37
|
+
* 6. Security check: scan content for hardcoded secrets
|
|
38
|
+
* 7. SecurityHeaders has non-empty headers array
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* const validator = new SecurityOutputValidator(['User', 'Post']);
|
|
43
|
+
* const result = validator.validate(llmOutput, { format: 'json' });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export class SecurityOutputValidator {
|
|
47
|
+
entityNames;
|
|
48
|
+
/**
|
|
49
|
+
* @param entityNames - Entity names for endpoint coverage reference
|
|
50
|
+
*/
|
|
51
|
+
constructor(entityNames) {
|
|
52
|
+
this.entityNames = entityNames;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate security output against the SecurityArtifact schema.
|
|
56
|
+
*
|
|
57
|
+
* @param output - Raw LLM output text
|
|
58
|
+
* @param _schema - Output schema (format expected to be 'json')
|
|
59
|
+
* @returns Validation result with descriptive errors
|
|
60
|
+
*/
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
62
|
+
validate(output, _schema) {
|
|
63
|
+
const errors = [];
|
|
64
|
+
// 1. Parse as JSON
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = JSON.parse(output);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
errors.push({
|
|
71
|
+
message: 'Security output is not valid JSON. Ensure the LLM produces only raw JSON without markdown fences or explanatory text.',
|
|
72
|
+
severity: 'error',
|
|
73
|
+
});
|
|
74
|
+
return { valid: false, errors };
|
|
75
|
+
}
|
|
76
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
77
|
+
errors.push({
|
|
78
|
+
message: 'Security output must be a JSON object, not an array or primitive.',
|
|
79
|
+
severity: 'error',
|
|
80
|
+
});
|
|
81
|
+
return { valid: false, errors };
|
|
82
|
+
}
|
|
83
|
+
const obj = parsed;
|
|
84
|
+
// 2. Verify required top-level keys
|
|
85
|
+
for (const key of REQUIRED_TOP_LEVEL_KEYS) {
|
|
86
|
+
if (!(key in obj)) {
|
|
87
|
+
errors.push({
|
|
88
|
+
message: `Missing required top-level key: '${key}'.`,
|
|
89
|
+
path: key,
|
|
90
|
+
severity: 'error',
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (errors.length > 0) {
|
|
95
|
+
return { valid: false, errors };
|
|
96
|
+
}
|
|
97
|
+
// 3. Verify authModel has strategy, fileName, content
|
|
98
|
+
const authModel = obj['authModel'];
|
|
99
|
+
if (typeof authModel !== 'object' || authModel === null || Array.isArray(authModel)) {
|
|
100
|
+
errors.push({
|
|
101
|
+
message: "Key 'authModel' must be an object.",
|
|
102
|
+
path: 'authModel',
|
|
103
|
+
severity: 'error',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
const auth = authModel;
|
|
108
|
+
if (typeof auth['strategy'] !== 'string' || auth['strategy'].length === 0) {
|
|
109
|
+
errors.push({
|
|
110
|
+
message: "authModel is missing required field 'strategy'.",
|
|
111
|
+
path: 'authModel.strategy',
|
|
112
|
+
severity: 'error',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (typeof auth['fileName'] !== 'string' || auth['fileName'].length === 0) {
|
|
116
|
+
errors.push({
|
|
117
|
+
message: "authModel is missing required field 'fileName'.",
|
|
118
|
+
path: 'authModel.fileName',
|
|
119
|
+
severity: 'error',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (typeof auth['content'] !== 'string' || auth['content'].length === 0) {
|
|
123
|
+
errors.push({
|
|
124
|
+
message: "authModel is missing required field 'content'.",
|
|
125
|
+
path: 'authModel.content',
|
|
126
|
+
severity: 'error',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// 4. Verify RBAC has non-empty roles and permissions
|
|
131
|
+
const rbac = obj['rbac'];
|
|
132
|
+
if (typeof rbac !== 'object' || rbac === null || Array.isArray(rbac)) {
|
|
133
|
+
errors.push({
|
|
134
|
+
message: "Key 'rbac' must be an object.",
|
|
135
|
+
path: 'rbac',
|
|
136
|
+
severity: 'error',
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
const r = rbac;
|
|
141
|
+
if (!Array.isArray(r['roles']) || r['roles'].length === 0) {
|
|
142
|
+
errors.push({
|
|
143
|
+
message: "RBAC 'roles' must be a non-empty array.",
|
|
144
|
+
path: 'rbac.roles',
|
|
145
|
+
severity: 'error',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (!Array.isArray(r['permissions']) || r['permissions'].length === 0) {
|
|
149
|
+
errors.push({
|
|
150
|
+
message: "RBAC 'permissions' must be a non-empty array.",
|
|
151
|
+
path: 'rbac.permissions',
|
|
152
|
+
severity: 'error',
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// 5. Verify middleware is non-empty array
|
|
157
|
+
const middleware = obj['middleware'];
|
|
158
|
+
if (!Array.isArray(middleware) || middleware.length === 0) {
|
|
159
|
+
errors.push({
|
|
160
|
+
message: "Key 'middleware' must be a non-empty array — at least an auth guard is required.",
|
|
161
|
+
path: 'middleware',
|
|
162
|
+
severity: 'error',
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// 6. Security check: scan content for hardcoded secrets
|
|
166
|
+
this.scanForSecrets(obj, errors);
|
|
167
|
+
// 7. Verify securityHeaders has non-empty headers array
|
|
168
|
+
const securityHeaders = obj['securityHeaders'];
|
|
169
|
+
if (typeof securityHeaders !== 'object' || securityHeaders === null || Array.isArray(securityHeaders)) {
|
|
170
|
+
errors.push({
|
|
171
|
+
message: "Key 'securityHeaders' must be an object.",
|
|
172
|
+
path: 'securityHeaders',
|
|
173
|
+
severity: 'error',
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const sh = securityHeaders;
|
|
178
|
+
if (!Array.isArray(sh['headers']) || sh['headers'].length === 0) {
|
|
179
|
+
errors.push({
|
|
180
|
+
message: "securityHeaders 'headers' must be a non-empty array.",
|
|
181
|
+
path: 'securityHeaders.headers',
|
|
182
|
+
severity: 'error',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return { valid: errors.length === 0, errors };
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Recursively scan all string fields for hardcoded secret patterns.
|
|
190
|
+
* Best-effort check, not exhaustive.
|
|
191
|
+
*/
|
|
192
|
+
scanForSecrets(obj, errors) {
|
|
193
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
194
|
+
if (typeof value === 'string') {
|
|
195
|
+
for (const pattern of HARDCODED_SECRET_PATTERNS) {
|
|
196
|
+
if (pattern.test(value)) {
|
|
197
|
+
errors.push({
|
|
198
|
+
message: `Potential hardcoded secret detected in field '${key}'. Use environment variable references (process.env.*) instead.`,
|
|
199
|
+
path: key,
|
|
200
|
+
severity: 'error',
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (Array.isArray(value)) {
|
|
207
|
+
for (const item of value) {
|
|
208
|
+
if (typeof item === 'object' && item !== null && !Array.isArray(item)) {
|
|
209
|
+
this.scanForSecrets(item, errors);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
else if (typeof value === 'object' && value !== null) {
|
|
214
|
+
this.scanForSecrets(value, errors);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=validator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.js","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hstm-labs/forge-security-generator",
|
|
3
|
+
"version": "0.1.11",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"test": "vitest run",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@hstm-labs/forge-common": "0.1.11",
|
|
20
|
+
"@hstm-labs/forge-core": "0.1.11",
|
|
21
|
+
"@hstm-labs/forge-architect": "0.1.11",
|
|
22
|
+
"@hstm-labs/forge-services-generator": "0.1.11",
|
|
23
|
+
"@hstm-labs/forge-spec-parser": "0.1.11",
|
|
24
|
+
"@hstm-labs/forge-profiles": "0.1.11",
|
|
25
|
+
"@hstm-labs/forge-templates": "0.1.11"
|
|
26
|
+
}
|
|
27
|
+
}
|