safepropel 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.
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # SafePropel Framework
2
+
3
+ Unified Protection System for AI Workflow Execution with 4-layer security architecture.
4
+
5
+ ## Features
6
+
7
+ - **4-Layer Security Architecture**
8
+ - Approach 1: Prompt Compilation (binary format)
9
+ - Approach 2: Encryption (AES-256-GCM)
10
+ - Approach 3: Runtime Prompt Engine (access control)
11
+ - Approach 4: Prompt Firewall (injection/extraction protection)
12
+
13
+ - **35+ Built-in Workflows**
14
+ - Specification & Requirements (create-spec, create-user-stories, create-epics)
15
+ - Architecture & Design (design-architecture, design-model)
16
+ - Development Planning (plan-development-tasks, plan-unit-test, plan-bug-resolution)
17
+ - DevOps & Infrastructure (plan-cicd-pipeline, plan-cloud-infrastructure, create-iac)
18
+ - Testing & Validation (create-test-plan, generate-playwright-scripts, validation-agent)
19
+ - Code Review & Quality (review-code, review-devops-security, analyze-codebase)
20
+ - Agent Orchestration (discovery-agent, backlog-agent, build-feature-agent, bug-fixing-agent)
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install -g safepropel
26
+ ```
27
+
28
+ ## Quick Start
29
+
30
+ ### Basic Usage
31
+
32
+ ```bash
33
+ # Run a workflow
34
+ safepropel create-spec BRD.txt
35
+
36
+ # Run without input file
37
+ safepropel design-architecture
38
+
39
+ # Show help
40
+ safepropel --help
41
+ ```
42
+
43
+ ### With Encrypted Bundle
44
+
45
+ If using an encrypted bundle, provide a license key:
46
+
47
+ ```bash
48
+ # Via command line
49
+ safepropel create-spec BRD.txt --license-key=your-key-here
50
+
51
+ # Via environment variable
52
+ export SAFEPROPEL_LICENSE_KEY=your-key-here
53
+ safepropel create-spec BRD.txt
54
+ ```
55
+
56
+ ### Custom Bundle Path
57
+
58
+ ```bash
59
+ # Via command line
60
+ safepropel create-spec BRD.txt --bundle=./custom/bundle.enc
61
+
62
+ # Via environment variable
63
+ export SAFEPROPEL_BUNDLE_PATH=./custom/bundle.enc
64
+ safepropel create-spec BRD.txt
65
+ ```
66
+
67
+ ## Available Workflows
68
+
69
+ ### Specification & Requirements
70
+ - `create-spec` - Generate functional requirements from feature specifications
71
+ - `create-figma-spec` - Generate UX requirements and screen specifications
72
+ - `create-user-stories` - Generate detailed user stories from epics
73
+ - `create-epics` - Generate epic decomposition from specifications
74
+ - `create-project-plan` - Generate comprehensive project plan
75
+ - `create-sprint-plan` - Generate sprint plan with dependency mapping
76
+
77
+ ### Architecture & Design
78
+ - `design-architecture` - Generate architecture design documents
79
+ - `design-model` - Generate UML diagrams and architectural models
80
+ - `analyze-codebase` - Comprehensive codebase analysis
81
+ - `analyze-implementation` - Review completed code changes
82
+ - `analyze-ux` - UI/UX analysis with Playwright automation
83
+
84
+ ### Development Planning
85
+ - `plan-development-tasks` - Generate implementation tasks
86
+ - `plan-unit-test` - Generate unit test plans
87
+ - `plan-bug-resolution` - Bug triage and resolution planning
88
+ - `implement-tasks` - Execute development tasks
89
+
90
+ ### DevOps & Infrastructure
91
+ - `plan-cicd-pipeline` - Design CI/CD pipeline architecture
92
+ - `plan-cloud-infrastructure` - Generate infrastructure specifications
93
+ - `create-iac` - Generate Terraform Infrastructure as Code
94
+ - `create-pipeline-scripts` - Generate CI/CD pipeline scripts
95
+ - `devops-agent` - Orchestrate DevOps phase
96
+
97
+ ### Testing & Validation
98
+ - `create-test-plan` - Generate comprehensive test plans
99
+ - `create-automation-test` - Create test workflow specifications
100
+ - `generate-playwright-scripts` - Generate Playwright automation scripts
101
+ - `validation-agent` - Orchestrate test automation
102
+
103
+ ### Code Review & Quality
104
+ - `review-code` - Comprehensive code review
105
+ - `review-devops-security` - Security review of DevOps artifacts
106
+ - `pull-request` - Create pull requests with validation
107
+
108
+ ### Prototyping & Design
109
+ - `build-prototype` - Transform business hypotheses into prototypes
110
+ - `generate-figma` - Generate Figma artifacts
111
+ - `generate-wireframe` - Generate wireframes from requirements
112
+
113
+ ### Agent Orchestration
114
+ - `discovery-agent` - Orchestrate technical discovery
115
+ - `backlog-agent` - Transform specs into backlog
116
+ - `build-feature-agent` - Orchestrate implementation phase
117
+ - `bug-fixing-agent` - Orchestrate bug resolution
118
+
119
+ ### Evaluation
120
+ - `evaluate-output` - Validate workflow outputs
121
+
122
+ ## Environment Variables
123
+
124
+ - `SAFEPROPEL_LICENSE_KEY` - License key for encrypted bundles
125
+ - `SAFEPROPEL_BUNDLE_PATH` - Custom bundle path (default: ./engine/prompt_bundle.enc)
126
+
127
+ ## Security Features
128
+
129
+ ### Prompt Protection
130
+ - Prompts never exposed in readable form
131
+ - Binary compilation prevents reverse engineering
132
+ - AES-256-GCM encryption for additional security
133
+ - Runtime access control and audit logging
134
+
135
+ ### Input Validation
136
+ - Prompt injection detection and prevention
137
+ - Extraction attempt blocking
138
+ - Input sanitization with configurable strictness
139
+ - Threat pattern recognition
140
+
141
+ ### Access Control
142
+ - Controlled content access through runtime engine
143
+ - Audit logging for all bundle accesses
144
+ - License-based access for encrypted bundles
145
+
146
+ ## Output
147
+
148
+ Workflows generate output in `.propel/context/docs/` directory with structured documentation following predefined templates.
149
+
150
+ ## Requirements
151
+
152
+ - Node.js >= 14.0.0
153
+ - No external dependencies (uses only Node.js built-in modules)
154
+
155
+ ## License
156
+
157
+ ISC
158
+
159
+ ## Support
160
+
161
+ For issues, questions, or feature requests, please contact the maintainer.
Binary file
@@ -0,0 +1,406 @@
1
+ /**
2
+ * SafePropel Runtime Prompt Engine
3
+ *
4
+ * Loads a compiled prompt bundle (.bin) and reconstructs prompts
5
+ * in memory on demand. Prompts never touch disk in readable form.
6
+ *
7
+ * IP PROTECTION: By default, only metadata (paths) is exposed.
8
+ * Content is accessible only through explicit getContent() call.
9
+ *
10
+ * Usage:
11
+ * const runtime = new PromptRuntime();
12
+ * runtime.load('path/to/prompt_bundle.bin');
13
+ *
14
+ * // Get metadata only (safe)
15
+ * const meta = runtime.get('.windsurf/workflows/create-spec.md');
16
+ * console.log(meta.path); // OK - path only
17
+ *
18
+ * // Get content only when needed (keep in memory)
19
+ * const content = runtime.getContent('.windsurf/workflows/create-spec.md');
20
+ * // NEVER log or expose content
21
+ */
22
+
23
+ 'use strict';
24
+
25
+ const fs = require('fs');
26
+ const zlib = require('zlib');
27
+ const crypto = require('crypto');
28
+
29
+ // --- Integrated Encryption (Approach 2) ---
30
+ const ENCRYPTION_ALGORITHM = 'aes-256-gcm';
31
+ const KEY_LENGTH = 32;
32
+ const IV_LENGTH = 12;
33
+ const AUTH_TAG_LENGTH = 16;
34
+ const SALT_LENGTH = 32;
35
+ const PBKDF2_ITERATIONS = 100000;
36
+
37
+ // --- Integrated Firewall (Approach 4) ---
38
+ const INJECTION_PATTERNS = [
39
+ /ignore\s+(all\s+)?(previous|prior|above|system)\s+(instructions?|prompts?|rules?)/i,
40
+ /disregard\s+(all\s+)?(previous|prior|above|system)\s+(instructions?|prompts?|rules?)/i,
41
+ /forget\s+(all\s+)?(previous|prior|above|system)\s+(instructions?|prompts?|rules?)/i,
42
+ /you\s+are\s+now\s+(a|an)\s+/i,
43
+ /act\s+as\s+(if\s+)?(you\s+are|a|an)\s+/i,
44
+ /show\s+(me\s+)?(your|the)\s+(system\s+)?(prompt|instruction|rule)/i,
45
+ /print\s+(your|the)\s+(system\s+)?(prompt|instruction|rule)/i,
46
+ /reveal\s+(your|the)\s+(system\s+)?(prompt|instruction|rule)/i,
47
+ /repeat\s+(everything|all)\s+(above|before)/i,
48
+ ];
49
+
50
+ // --- Constants ---------------------------------------------------------------
51
+
52
+ const MAGIC_COMPILED = Buffer.from('SPBL');
53
+ const MAGIC_ENCRYPTED = Buffer.from('SPEC');
54
+ const HEADER_SIZE_COMPILED = 16; // 4 magic + 4 version + 4 count + 4 payload size
55
+
56
+ // --- Runtime Engine ----------------------------------------------------------
57
+
58
+ class PromptRuntime {
59
+ constructor(options = {}) {
60
+ this._manifest = null;
61
+ this._index = new Map(); // path → entry
62
+ this._typeIndex = new Map(); // type → [entry]
63
+ this._licenseKey = null;
64
+ this._isEncrypted = false;
65
+
66
+ // Integrated Firewall (Approach 4)
67
+ this.firewallEnabled = options.firewallEnabled !== false;
68
+ this.firewallStrictMode = options.firewallStrictMode || false;
69
+ this.maxInputLength = options.maxInputLength || 50000;
70
+
71
+ // Access Control (Approach 3)
72
+ this.accessControl = {
73
+ allowContentAccess: options.allowContentAccess !== false,
74
+ logAccess: options.logAccess !== false
75
+ };
76
+
77
+ // Security metrics
78
+ this.metrics = {
79
+ bundleLoads: 0,
80
+ contentAccesses: 0,
81
+ firewallBlocks: 0,
82
+ decryptionAttempts: 0
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Derives encryption key from license key.
88
+ */
89
+ _deriveKey(licenseKey, salt) {
90
+ return crypto.pbkdf2Sync(
91
+ licenseKey,
92
+ salt,
93
+ PBKDF2_ITERATIONS,
94
+ KEY_LENGTH,
95
+ 'sha256'
96
+ );
97
+ }
98
+
99
+ /**
100
+ * Decrypts encrypted bundle (Approach 2).
101
+ */
102
+ _decryptBundle(encryptedData, licenseKey) {
103
+ let offset = 0;
104
+
105
+ const magic = encryptedData.subarray(offset, offset + 4);
106
+ offset += 4;
107
+ if (!magic.equals(MAGIC_ENCRYPTED)) {
108
+ throw new Error('Invalid encrypted bundle: bad magic marker');
109
+ }
110
+
111
+ const version = encryptedData.readUInt32LE(offset);
112
+ offset += 4;
113
+
114
+ const saltLength = encryptedData.readUInt32LE(offset);
115
+ offset += 4;
116
+ const salt = encryptedData.subarray(offset, offset + saltLength);
117
+ offset += saltLength;
118
+
119
+ const ivLength = encryptedData.readUInt32LE(offset);
120
+ offset += 4;
121
+ const iv = encryptedData.subarray(offset, offset + ivLength);
122
+ offset += ivLength;
123
+
124
+ const authTagLength = encryptedData.readUInt32LE(offset);
125
+ offset += 4;
126
+ const authTag = encryptedData.subarray(offset, offset + authTagLength);
127
+ offset += authTagLength;
128
+
129
+ const payloadLength = encryptedData.readUInt32LE(offset);
130
+ offset += 4;
131
+ const encrypted = encryptedData.subarray(offset, offset + payloadLength);
132
+
133
+ const key = this._deriveKey(licenseKey, salt);
134
+ const decipher = crypto.createDecipheriv(ENCRYPTION_ALGORITHM, key, iv);
135
+ decipher.setAuthTag(authTag);
136
+
137
+ try {
138
+ return Buffer.concat([
139
+ decipher.update(encrypted),
140
+ decipher.final()
141
+ ]);
142
+ } catch (error) {
143
+ throw new Error('Decryption failed: invalid license key or corrupted bundle');
144
+ }
145
+ }
146
+
147
+ /**
148
+ * Validates input through integrated firewall (Approach 4).
149
+ */
150
+ validateInput(input) {
151
+ if (!this.firewallEnabled) {
152
+ return { allowed: true, sanitized: input, threats: [] };
153
+ }
154
+
155
+ const threats = [];
156
+ let sanitized = input;
157
+
158
+ // Length check
159
+ if (input.length > this.maxInputLength) {
160
+ threats.push('input_too_long');
161
+ if (this.firewallStrictMode) {
162
+ this.metrics.firewallBlocks++;
163
+ return {
164
+ allowed: false,
165
+ sanitized: '',
166
+ threats,
167
+ message: `Input exceeds maximum length of ${this.maxInputLength} characters.`
168
+ };
169
+ }
170
+ sanitized = sanitized.substring(0, this.maxInputLength);
171
+ }
172
+
173
+ // Injection/extraction detection
174
+ for (const pattern of INJECTION_PATTERNS) {
175
+ if (pattern.test(sanitized)) {
176
+ threats.push('prompt_injection_or_extraction');
177
+ if (this.firewallStrictMode) {
178
+ this.metrics.firewallBlocks++;
179
+ return {
180
+ allowed: false,
181
+ sanitized: '',
182
+ threats,
183
+ message: 'Potential prompt injection/extraction detected. Request blocked.'
184
+ };
185
+ }
186
+ // Sanitize in permissive mode
187
+ sanitized = sanitized.replace(pattern, '[REMOVED]');
188
+ break;
189
+ }
190
+ }
191
+
192
+ return {
193
+ allowed: true,
194
+ sanitized,
195
+ threats,
196
+ message: threats.length > 0 ? 'Input sanitized for security.' : 'Input validated successfully.'
197
+ };
198
+ }
199
+
200
+ /**
201
+ * Loads and validates a bundle (encrypted or compiled).
202
+ * Automatically detects format and decrypts if needed.
203
+ * Prompts are decompressed into memory — nothing is written to disk.
204
+ *
205
+ * @param {string} bundlePath Absolute path to the bundle file.
206
+ * @param {string} [licenseKey] License key for encrypted bundles.
207
+ */
208
+ load(bundlePath, licenseKey = null) {
209
+ const raw = fs.readFileSync(bundlePath);
210
+
211
+ // Detect bundle format
212
+ const magic = raw.subarray(0, 4);
213
+ let compiledData;
214
+
215
+ if (magic.equals(MAGIC_ENCRYPTED)) {
216
+ // Encrypted bundle - decrypt first (Approach 2)
217
+ if (!licenseKey) {
218
+ throw new Error('License key required for encrypted bundle');
219
+ }
220
+ this._licenseKey = licenseKey;
221
+ this._isEncrypted = true;
222
+ this.metrics.decryptionAttempts++;
223
+
224
+ compiledData = this._decryptBundle(raw, licenseKey);
225
+ } else if (magic.equals(MAGIC_COMPILED)) {
226
+ // Compiled bundle - use directly
227
+ compiledData = raw;
228
+ this._isEncrypted = false;
229
+ } else {
230
+ throw new Error('Invalid bundle format: unrecognized magic marker');
231
+ }
232
+
233
+ // Load compiled bundle (Approach 1)
234
+ this._loadCompiled(compiledData);
235
+ this.metrics.bundleLoads++;
236
+ }
237
+
238
+ /**
239
+ * Loads compiled bundle data.
240
+ */
241
+ _loadCompiled(raw) {
242
+
243
+ // --- Validate header ---
244
+ if (raw.length < HEADER_SIZE_COMPILED) {
245
+ throw new Error('Invalid bundle: file too small.');
246
+ }
247
+
248
+ const magic = raw.subarray(0, 4);
249
+ if (!magic.equals(MAGIC_COMPILED)) {
250
+ throw new Error('Invalid bundle: bad magic marker.');
251
+ }
252
+
253
+ const version = raw.readUInt32LE(4);
254
+ const entryCount = raw.readUInt32LE(8);
255
+ const payloadSize = raw.readUInt32LE(12);
256
+
257
+ if (raw.length < HEADER_SIZE_COMPILED + payloadSize) {
258
+ throw new Error('Invalid bundle: truncated payload.');
259
+ }
260
+
261
+ // --- Decompress ---
262
+ const compressed = raw.subarray(HEADER_SIZE_COMPILED, HEADER_SIZE_COMPILED + payloadSize);
263
+ const jsonBuf = zlib.inflateSync(compressed);
264
+ const manifest = JSON.parse(jsonBuf.toString('utf-8'));
265
+
266
+ if (manifest.entry_count !== entryCount) {
267
+ throw new Error('Bundle integrity error: entry count mismatch.');
268
+ }
269
+
270
+ this._manifest = manifest;
271
+
272
+ // --- Build indexes ---
273
+ this._index.clear();
274
+ this._typeIndex.clear();
275
+
276
+ for (const entry of manifest.entries) {
277
+ this._index.set(entry.path, entry);
278
+
279
+ if (!this._typeIndex.has(entry.type)) {
280
+ this._typeIndex.set(entry.type, []);
281
+ }
282
+ this._typeIndex.get(entry.type).push(entry);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Returns bundle metadata (without exposing prompt contents).
288
+ */
289
+ info() {
290
+ if (!this._manifest) throw new Error('No bundle loaded.');
291
+ return {
292
+ version: this._manifest.version,
293
+ compiled_at: this._manifest.compiled_at,
294
+ framework: this._manifest.framework_root,
295
+ entry_count: this._manifest.entry_count,
296
+ encrypted: this._isEncrypted,
297
+ firewallEnabled: this.firewallEnabled,
298
+ types: Object.fromEntries(
299
+ [...this._typeIndex.entries()].map(([t, arr]) => [t, arr.length])
300
+ ),
301
+ };
302
+ }
303
+
304
+ /**
305
+ * Lists all entries in the bundle.
306
+ * Returns metadata only (paths, types) - NO CONTENT.
307
+ *
308
+ * @param {string} [type] Optional filter by entry type.
309
+ * @returns {Array<{ path: string, type: string, id: string }>}
310
+ */
311
+ list(type) {
312
+ if (!this._manifest) throw new Error('No bundle loaded.');
313
+
314
+ let entries = this._manifest.entries;
315
+ if (type) {
316
+ entries = this._typeIndex.get(type) || [];
317
+ }
318
+ // Return metadata only - NO CONTENT
319
+ return entries.map((e) => ({ path: e.path, type: e.type, id: e.id }));
320
+ }
321
+
322
+ /**
323
+ * Retrieves a single prompt/instruction by its relative path.
324
+ * Content is reconstructed in memory from the compiled bundle.
325
+ * Includes access control (Approach 3).
326
+ *
327
+ * @param {string} relativePath e.g. ".windsurf/workflows/create-spec.md"
328
+ * @returns {{ path: string, type: string, frontmatter: string|null, content: string }}
329
+ */
330
+ get(relativePath) {
331
+ if (!this._manifest) throw new Error('No bundle loaded.');
332
+
333
+ // Access control check
334
+ if (!this.accessControl.allowContentAccess) {
335
+ throw new Error('Content access is disabled');
336
+ }
337
+
338
+ const entry = this._index.get(relativePath);
339
+ if (!entry) {
340
+ throw new Error('Entry not found in bundle: ' + relativePath);
341
+ }
342
+
343
+ // Reconstruct content from base64 — exists only in memory.
344
+ const content = Buffer.from(entry.content, 'base64').toString('utf-8');
345
+
346
+ this.metrics.contentAccesses++;
347
+
348
+ return {
349
+ path: entry.path,
350
+ type: entry.type,
351
+ frontmatter: entry.frontmatter,
352
+ content,
353
+ };
354
+ }
355
+
356
+ /**
357
+ * Retrieves all entries of a given type with their reconstructed content.
358
+ *
359
+ * @param {string} type One of: agent, instruction, prompt, template, context, config
360
+ * @returns {Array<{ path: string, type: string, frontmatter: string|null, content: string }>}
361
+ */
362
+ getByType(type) {
363
+ if (!this._manifest) throw new Error('No bundle loaded.');
364
+
365
+ const entries = this._typeIndex.get(type) || [];
366
+ return entries.map((entry) => ({
367
+ path: entry.path,
368
+ type: entry.type,
369
+ frontmatter: entry.frontmatter,
370
+ content: Buffer.from(entry.content, 'base64').toString('utf-8'),
371
+ }));
372
+ }
373
+
374
+ /**
375
+ * Verifies bundle integrity by comparing stored entry count with actual.
376
+ * @returns {{ valid: boolean, encrypted: boolean, details: string }}
377
+ */
378
+ verify() {
379
+ if (!this._manifest) throw new Error('No bundle loaded.');
380
+
381
+ const expected = this._manifest.entry_count;
382
+ const actual = this._manifest.entries.length;
383
+ const valid = expected === actual;
384
+
385
+ return {
386
+ valid,
387
+ encrypted: this._isEncrypted,
388
+ details: valid
389
+ ? `Bundle OK — ${actual} entries verified.`
390
+ : `MISMATCH — header says ${expected}, manifest has ${actual}.`,
391
+ };
392
+ }
393
+
394
+ /**
395
+ * Gets security metrics.
396
+ */
397
+ getMetrics() {
398
+ return {
399
+ ...this.metrics,
400
+ firewallEnabled: this.firewallEnabled,
401
+ firewallStrictMode: this.firewallStrictMode
402
+ };
403
+ }
404
+ }
405
+
406
+ module.exports = { PromptRuntime };