pi-gsd 2.0.1 → 2.0.3
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/dist/pi-gsd-hooks.js +1533 -0
- package/dist/pi-gsd-tools.js +53 -52
- package/package.json +3 -5
- package/.gsd/extensions/pi-gsd-hooks.ts +0 -973
- package/src/cli.ts +0 -644
- package/src/commands/base.ts +0 -67
- package/src/commands/commit.ts +0 -22
- package/src/commands/config.ts +0 -71
- package/src/commands/frontmatter.ts +0 -51
- package/src/commands/index.ts +0 -76
- package/src/commands/init.ts +0 -43
- package/src/commands/milestone.ts +0 -37
- package/src/commands/phase.ts +0 -92
- package/src/commands/progress.ts +0 -71
- package/src/commands/roadmap.ts +0 -40
- package/src/commands/scaffold.ts +0 -19
- package/src/commands/state.ts +0 -102
- package/src/commands/template.ts +0 -52
- package/src/commands/verify.ts +0 -70
- package/src/commands/workstream.ts +0 -98
- package/src/commands/wxp.ts +0 -65
- package/src/lib/commands.ts +0 -1040
- package/src/lib/config.ts +0 -385
- package/src/lib/core.ts +0 -1167
- package/src/lib/frontmatter.ts +0 -462
- package/src/lib/init.ts +0 -517
- package/src/lib/milestone.ts +0 -290
- package/src/lib/model-profiles.ts +0 -272
- package/src/lib/phase.ts +0 -1012
- package/src/lib/profile-output.ts +0 -237
- package/src/lib/profile-pipeline.ts +0 -556
- package/src/lib/roadmap.ts +0 -378
- package/src/lib/schemas.ts +0 -290
- package/src/lib/security.ts +0 -176
- package/src/lib/state.ts +0 -1175
- package/src/lib/template.ts +0 -246
- package/src/lib/uat.ts +0 -289
- package/src/lib/verify.ts +0 -879
- package/src/lib/workstream.ts +0 -524
- package/src/output.ts +0 -45
- package/src/schemas/pi-gsd-settings.schema.json +0 -80
- package/src/schemas/wxp.xsd +0 -619
- package/src/schemas/wxp.zod.ts +0 -318
- package/src/wxp/__tests__/arguments.test.ts +0 -86
- package/src/wxp/__tests__/conditions.test.ts +0 -106
- package/src/wxp/__tests__/executor.test.ts +0 -95
- package/src/wxp/__tests__/helpers.ts +0 -26
- package/src/wxp/__tests__/integration.test.ts +0 -166
- package/src/wxp/__tests__/new-features.test.ts +0 -222
- package/src/wxp/__tests__/parser.test.ts +0 -159
- package/src/wxp/__tests__/paste.test.ts +0 -66
- package/src/wxp/__tests__/schema.test.ts +0 -120
- package/src/wxp/__tests__/security.test.ts +0 -87
- package/src/wxp/__tests__/shell.test.ts +0 -85
- package/src/wxp/__tests__/string-ops.test.ts +0 -25
- package/src/wxp/__tests__/variables.test.ts +0 -65
- package/src/wxp/arguments.ts +0 -89
- package/src/wxp/conditions.ts +0 -78
- package/src/wxp/executor.ts +0 -191
- package/src/wxp/index.ts +0 -191
- package/src/wxp/parser.ts +0 -198
- package/src/wxp/paste.ts +0 -51
- package/src/wxp/security.ts +0 -102
- package/src/wxp/shell.ts +0 -81
- package/src/wxp/string-ops.ts +0 -44
- package/src/wxp/variables.ts +0 -109
package/src/lib/security.ts
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* security.ts - Prompt injection scanning and path/field validation.
|
|
3
|
-
*
|
|
4
|
-
* Ported from lib/security.cjs. Provides sanitization for agent-generated content
|
|
5
|
-
* written to .planning/ files.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import path from "path";
|
|
9
|
-
|
|
10
|
-
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
export interface SafeJsonResult<T> {
|
|
13
|
-
ok: true;
|
|
14
|
-
value: T;
|
|
15
|
-
}
|
|
16
|
-
export interface SafeJsonError {
|
|
17
|
-
ok: false;
|
|
18
|
-
error: string;
|
|
19
|
-
}
|
|
20
|
-
export type SafeJsonParseResult<T> = SafeJsonResult<T> | SafeJsonError;
|
|
21
|
-
|
|
22
|
-
export interface PathValidationResult {
|
|
23
|
-
safe: boolean;
|
|
24
|
-
resolved: string;
|
|
25
|
-
error?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface FieldValidationResult {
|
|
29
|
-
valid: boolean;
|
|
30
|
-
error?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// ─── Injection patterns ───────────────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
const INJECTION_PATTERNS: RegExp[] = [
|
|
36
|
-
/ignore\s+(?:all\s+)?previous\s+instructions?/i,
|
|
37
|
-
/forget\s+(?:all\s+)?previous/i,
|
|
38
|
-
/\bsystem\s+prompt\b/i,
|
|
39
|
-
/\bdisregard\s+(?:all\s+)?previous\b/i,
|
|
40
|
-
/\byou\s+are\s+now\b/i,
|
|
41
|
-
/\bact\s+as\s+(?:a\s+)?(?:new\s+)?(?:AI|assistant|gpt|claude|llm)\b/i,
|
|
42
|
-
/\bpretend\s+(?:you\s+are|to\s+be)\b/i,
|
|
43
|
-
/\boverride\s+(?:all\s+)?(?:previous\s+)?(?:instructions?|directives?|rules?|constraints?)\b/i,
|
|
44
|
-
/\bdo\s+not\s+follow\s+(?:the\s+)?(?:previous\s+)?instructions?\b/i,
|
|
45
|
-
/\bnew\s+instructions?:\s/i,
|
|
46
|
-
/\bignore\s+(?:all\s+)?(?:previous\s+)?(?:context|history|instructions?)\b/i,
|
|
47
|
-
/\bdeveloper\s+mode\b/i,
|
|
48
|
-
/\bDAN\s+mode\b/i,
|
|
49
|
-
];
|
|
50
|
-
|
|
51
|
-
const MAX_SAFE_LENGTH = 10000;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Scan content for prompt-injection patterns.
|
|
55
|
-
* Returns an array of matched patterns (empty = safe).
|
|
56
|
-
*/
|
|
57
|
-
export function scanForInjection(content: string): string[] {
|
|
58
|
-
const matches: string[] = [];
|
|
59
|
-
for (const pattern of INJECTION_PATTERNS) {
|
|
60
|
-
if (pattern.test(content)) {
|
|
61
|
-
matches.push(pattern.source);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return matches;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Sanitize a string for safe use in agent prompts.
|
|
69
|
-
* Strips invisible characters and known injection markers.
|
|
70
|
-
*/
|
|
71
|
-
export function sanitizeForPrompt(text: string): string {
|
|
72
|
-
if (!text) return text;
|
|
73
|
-
// Remove null bytes and other invisible control characters
|
|
74
|
-
// eslint-disable-next-line no-control-regex
|
|
75
|
-
let sanitized = text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
|
|
76
|
-
// Truncate to safe length
|
|
77
|
-
if (sanitized.length > MAX_SAFE_LENGTH) {
|
|
78
|
-
sanitized = sanitized.slice(0, MAX_SAFE_LENGTH) + "... [truncated]";
|
|
79
|
-
}
|
|
80
|
-
return sanitized;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Safely parse JSON with a human-readable error on failure.
|
|
85
|
-
*/
|
|
86
|
-
export function safeJsonParse<T = unknown>(
|
|
87
|
-
raw: string,
|
|
88
|
-
opts: { label?: string } = {},
|
|
89
|
-
): SafeJsonParseResult<T> {
|
|
90
|
-
try {
|
|
91
|
-
return { ok: true, value: JSON.parse(raw) as T };
|
|
92
|
-
} catch (err) {
|
|
93
|
-
const label = opts.label ?? "JSON";
|
|
94
|
-
return { ok: false, error: `Invalid ${label}: ${(err as Error).message}` };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Validate a file path is safe (no null bytes, no path traversal to sensitive dirs).
|
|
100
|
-
*/
|
|
101
|
-
export function validatePath(
|
|
102
|
-
filePath: string,
|
|
103
|
-
cwd: string,
|
|
104
|
-
opts: { allowAbsolute?: boolean } = {},
|
|
105
|
-
): PathValidationResult {
|
|
106
|
-
if (filePath.includes("\0")) {
|
|
107
|
-
return {
|
|
108
|
-
safe: false,
|
|
109
|
-
resolved: filePath,
|
|
110
|
-
error: "Path contains null bytes",
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const resolved = path.isAbsolute(filePath)
|
|
115
|
-
? filePath
|
|
116
|
-
: path.resolve(cwd, filePath);
|
|
117
|
-
|
|
118
|
-
// Prevent traversal above home directory
|
|
119
|
-
const home = process.env["HOME"] ?? "/";
|
|
120
|
-
if (!resolved.startsWith(home) && !resolved.startsWith(cwd)) {
|
|
121
|
-
return { safe: false, resolved, error: "Path traversal rejected" };
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if (!opts.allowAbsolute && path.isAbsolute(filePath)) {
|
|
125
|
-
return { safe: false, resolved, error: "Absolute paths not allowed" };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return { safe: true, resolved };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Validate a file path and throw on traversal attempt.
|
|
133
|
-
*/
|
|
134
|
-
export function requireSafePath(
|
|
135
|
-
filePath: string,
|
|
136
|
-
baseDir: string,
|
|
137
|
-
label: string,
|
|
138
|
-
opts: { allowAbsolute?: boolean } = {},
|
|
139
|
-
): string {
|
|
140
|
-
const result = validatePath(filePath, baseDir, opts);
|
|
141
|
-
if (!result.safe)
|
|
142
|
-
throw new Error(`${label || "Path"} validation failed: ${result.error}`);
|
|
143
|
-
return result.resolved;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Sanitize text for display back to the user.
|
|
148
|
-
*/
|
|
149
|
-
export function sanitizeForDisplay(text: string): string {
|
|
150
|
-
if (!text || typeof text !== "string") return text;
|
|
151
|
-
let s = text.replace(/[\u200B-\u200F\u2028-\u202F\uFEFF\u00AD]/g, "");
|
|
152
|
-
s = s.replace(
|
|
153
|
-
/<(\/?)(?:system|assistant|human)>/gi,
|
|
154
|
-
(_, sl) => `\uFF1C${sl || ""}system-text\uFF1E`,
|
|
155
|
-
);
|
|
156
|
-
s = s.replace(/\[(SYSTEM|INST)\]/gi, "[$1-TEXT]");
|
|
157
|
-
s = s.replace(/<<\s*SYS\s*>>/gi, "\u00ABSYS-TEXT\u00BB");
|
|
158
|
-
return s;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Validate a STATE.md field name to prevent regex injection via crafted field names.
|
|
163
|
-
* Field names must be human-readable: letters, spaces, hyphens, underscores only.
|
|
164
|
-
*/
|
|
165
|
-
export function validateFieldName(fieldName: string): FieldValidationResult {
|
|
166
|
-
if (!fieldName || typeof fieldName !== "string") {
|
|
167
|
-
return { valid: false, error: "Field name must be a non-empty string" };
|
|
168
|
-
}
|
|
169
|
-
if (!/^[a-zA-Z][a-zA-Z0-9 _-]*$/.test(fieldName)) {
|
|
170
|
-
return {
|
|
171
|
-
valid: false,
|
|
172
|
-
error: `Field name "${fieldName}" contains invalid characters. Only letters, digits, spaces, hyphens, and underscores are allowed.`,
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
return { valid: true };
|
|
176
|
-
}
|