docguard-cli 0.21.0 → 0.21.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/cli/commands/init.mjs +13 -8
- package/cli/ensure-skills.mjs +50 -8
- package/extensions/spec-kit-docguard/extension.yml +1 -1
- package/extensions/spec-kit-docguard/skills/docguard-fix/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-guard/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-review/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-score/SKILL.md +2 -2
- package/extensions/spec-kit-docguard/skills/docguard-sync/SKILL.md +1 -1
- package/package.json +1 -1
package/cli/commands/init.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
15
15
|
import { createInterface } from 'node:readline';
|
|
16
16
|
import { execSync } from 'node:child_process';
|
|
17
17
|
import { c, PROFILES } from '../shared.mjs';
|
|
18
|
-
import { ensureSkills, detectAgentMode, detectAIAgent, isSpecKitAvailable, isSpecKitInitialized, getDetectedAgent } from '../ensure-skills.mjs';
|
|
18
|
+
import { ensureSkills, detectAgentMode, detectAIAgent, isSpecKitAvailable, isSpecKitInitialized, getDetectedAgent, safeSpawnSpecify } from '../ensure-skills.mjs';
|
|
19
19
|
|
|
20
20
|
// v0.20: scaffolder names that can be passed via `init --with <name>` and
|
|
21
21
|
// dispatched to the corresponding standalone runner. Each name maps to its
|
|
@@ -378,17 +378,22 @@ poetry.lock
|
|
|
378
378
|
} else if (specKitAvailable && !specKitInitialized) {
|
|
379
379
|
console.log(`\n ${c.bold}🌱 Spec Kit Integration${c.reset}`);
|
|
380
380
|
|
|
381
|
-
// Detect which AI agent is in use (matches spec-kit's --ai flag)
|
|
381
|
+
// Detect which AI agent is in use (matches spec-kit's --ai flag).
|
|
382
|
+
// v0.21.1 (issue #190): the returned value is allowlist-validated inside
|
|
383
|
+
// getDetectedAgent, so an attacker-controlled `.specify/init-options.json`
|
|
384
|
+
// can no longer inject shell metacharacters here.
|
|
382
385
|
const detectedAgent = detectAIAgent(projectDir);
|
|
383
|
-
const
|
|
384
|
-
?
|
|
385
|
-
: '--ai generic --ai-commands-dir .agent/commands/';
|
|
386
|
+
const aiArgs = detectedAgent
|
|
387
|
+
? ['--ai', detectedAgent]
|
|
388
|
+
: ['--ai', 'generic', '--ai-commands-dir', '.agent/commands/'];
|
|
386
389
|
|
|
387
390
|
console.log(` ${c.dim}Running specify init (agent: ${detectedAgent || 'generic'})...${c.reset}`);
|
|
388
391
|
try {
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
+
// v0.21.1 (issue #190): execFileSync via safeSpawnSpecify — args pass
|
|
393
|
+
// through as an array, no shell interpolation.
|
|
394
|
+
const scriptArgs = process.platform === 'win32' ? ['--script', 'ps'] : ['--script', 'sh'];
|
|
395
|
+
safeSpawnSpecify(
|
|
396
|
+
['init', '--here', '--force', ...aiArgs, '--ai-skills', '--ignore-agent-tools', '--no-git', ...scriptArgs],
|
|
392
397
|
{ cwd: projectDir, encoding: 'utf-8', stdio: 'pipe', timeout: 30000 }
|
|
393
398
|
);
|
|
394
399
|
console.log(` ${c.green}✅${c.reset} Spec Kit initialized ${c.dim}(.specify/, spec-kit skills, agent: ${detectedAgent || 'generic'})${c.reset}`);
|
package/cli/ensure-skills.mjs
CHANGED
|
@@ -13,9 +13,32 @@
|
|
|
13
13
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
|
|
14
14
|
import { resolve, dirname } from 'node:path';
|
|
15
15
|
import { fileURLToPath } from 'node:url';
|
|
16
|
-
import { execSync } from 'node:child_process';
|
|
16
|
+
import { execSync, execFileSync } from 'node:child_process';
|
|
17
17
|
import { c } from './shared.mjs';
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* v0.21.1 (security): cross-platform safe spawn for the `specify` CLI.
|
|
21
|
+
*
|
|
22
|
+
* On POSIX, runs the `specify` binary directly with argv passed as an array
|
|
23
|
+
* — no shell interpolation possible. On Windows, the equivalent is via
|
|
24
|
+
* `cmd.exe /c specify.cmd ...` since `specify` is shipped as a .cmd shim by
|
|
25
|
+
* `pip install`. Args are still passed as an array so cmd.exe doesn't
|
|
26
|
+
* re-parse them.
|
|
27
|
+
*
|
|
28
|
+
* Replaces the pre-v0.21.1 pattern of `execSync(\`specify init ... \${flag} ...\`)`
|
|
29
|
+
* which was shell-interpolated and vulnerable to command injection via
|
|
30
|
+
* `.specify/init-options.json`'s `ai` field (issue #190).
|
|
31
|
+
*/
|
|
32
|
+
export function safeSpawnSpecify(args, opts) {
|
|
33
|
+
if (!Array.isArray(args)) {
|
|
34
|
+
throw new TypeError('safeSpawnSpecify(args, opts): args must be an array');
|
|
35
|
+
}
|
|
36
|
+
if (process.platform === 'win32') {
|
|
37
|
+
return execFileSync('cmd.exe', ['/c', 'specify.cmd', ...args], opts);
|
|
38
|
+
}
|
|
39
|
+
return execFileSync('specify', args, opts);
|
|
40
|
+
}
|
|
41
|
+
|
|
19
42
|
const __filename = fileURLToPath(import.meta.url);
|
|
20
43
|
const __dirname = dirname(__filename);
|
|
21
44
|
|
|
@@ -77,12 +100,27 @@ export function detectAgentMode(projectDir) {
|
|
|
77
100
|
* @param {string} projectDir - The project root directory
|
|
78
101
|
* @returns {string | null}
|
|
79
102
|
*/
|
|
103
|
+
// v0.21.1 (security): allowlist for the spec-kit --ai flag value. Source
|
|
104
|
+
// values come from `.specify/init-options.json` which is attacker-writable
|
|
105
|
+
// in any compromised project. Without this filter, a value like
|
|
106
|
+
// `"claude; touch /tmp/pwned;"` would shell-execute on every `docguard init`.
|
|
107
|
+
//
|
|
108
|
+
// Set conservatively from spec-kit's published agent list. New agents
|
|
109
|
+
// require a code change to be accepted — by design.
|
|
110
|
+
const VALID_AI_AGENT = /^[a-zA-Z0-9_-]{1,32}$/;
|
|
111
|
+
|
|
80
112
|
export function getDetectedAgent(projectDir) {
|
|
81
113
|
const initOptions = resolve(projectDir, '.specify', 'init-options.json');
|
|
82
114
|
if (existsSync(initOptions)) {
|
|
83
115
|
try {
|
|
84
116
|
const opts = JSON.parse(readFileSync(initOptions, 'utf-8'));
|
|
85
|
-
|
|
117
|
+
const ai = opts.ai;
|
|
118
|
+
if (typeof ai !== 'string') return null;
|
|
119
|
+
// v0.21.1 (issue #190): reject anything outside the allowlist. Without
|
|
120
|
+
// this, a malicious `.specify/init-options.json` could inject shell
|
|
121
|
+
// metacharacters through to the `specify init` exec call.
|
|
122
|
+
if (!VALID_AI_AGENT.test(ai)) return null;
|
|
123
|
+
return ai;
|
|
86
124
|
} catch { /* ignore */ }
|
|
87
125
|
}
|
|
88
126
|
return null;
|
|
@@ -193,13 +231,17 @@ export function ensureSpecKit(projectDir, flags = {}) {
|
|
|
193
231
|
console.log(` ${c.cyan}🌱 Spec Kit detected — auto-initializing SDD workflow...${c.reset}`);
|
|
194
232
|
}
|
|
195
233
|
try {
|
|
234
|
+
// v0.21.1 (issue #190): switched from shell-interpolated execSync to
|
|
235
|
+
// execFileSync via safeSpawnSpecify. detectAIAgent now also enforces
|
|
236
|
+
// the [a-zA-Z0-9_-]{1,32} allowlist on values read from .specify/
|
|
237
|
+
// init-options.json — defense in depth.
|
|
196
238
|
const detectedAgent = detectAIAgent(projectDir);
|
|
197
|
-
const
|
|
198
|
-
?
|
|
199
|
-
: '--ai generic --ai-commands-dir .agent/commands/';
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
239
|
+
const aiArgs = detectedAgent
|
|
240
|
+
? ['--ai', detectedAgent]
|
|
241
|
+
: ['--ai', 'generic', '--ai-commands-dir', '.agent/commands/'];
|
|
242
|
+
const scriptArgs = process.platform === 'win32' ? ['--script', 'ps'] : ['--script', 'sh'];
|
|
243
|
+
safeSpawnSpecify(
|
|
244
|
+
['init', '--here', '--force', ...aiArgs, '--ai-skills', '--ignore-agent-tools', '--no-git', ...scriptArgs],
|
|
203
245
|
{ cwd: projectDir, encoding: 'utf-8', stdio: 'pipe', timeout: 30000 }
|
|
204
246
|
);
|
|
205
247
|
if (!silent) {
|
|
@@ -3,7 +3,7 @@ schema_version: "1.0"
|
|
|
3
3
|
extension:
|
|
4
4
|
id: "docguard"
|
|
5
5
|
name: "DocGuard — CDD Enforcement"
|
|
6
|
-
version: "0.21.
|
|
6
|
+
version: "0.21.1"
|
|
7
7
|
description: "Canonical-Driven Development enforcement as a true spec-kit extension. LLM-first design with 19 automated validators, 4 AI behavior skills, spec-kit skill chaining, and workflow hooks. Zero NPM runtime dependencies."
|
|
8
8
|
author: "Ricardo Accioly"
|
|
9
9
|
repository: "https://github.com/raccioly/docguard"
|
|
@@ -6,10 +6,10 @@ description: AI-driven documentation repair with structured research workflow, t
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.21.
|
|
9
|
+
version: 0.21.1
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-fix
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.21.
|
|
12
|
+
<!-- docguard:version: 0.21.1 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Fix Skill
|
|
15
15
|
|
|
@@ -7,10 +7,10 @@ description: Run DocGuard guard validation against Canonical-Driven Development
|
|
|
7
7
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
8
8
|
metadata:
|
|
9
9
|
author: docguard
|
|
10
|
-
version: 0.21.
|
|
10
|
+
version: 0.21.1
|
|
11
11
|
source: extensions/spec-kit-docguard/skills/docguard-guard
|
|
12
12
|
---
|
|
13
|
-
<!-- docguard:version: 0.21.
|
|
13
|
+
<!-- docguard:version: 0.21.1 -->
|
|
14
14
|
|
|
15
15
|
# DocGuard Guard Skill
|
|
16
16
|
|
|
@@ -6,10 +6,10 @@ description: Cross-document consistency analysis and quality assessment. Perform
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.21.
|
|
9
|
+
version: 0.21.1
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-review
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.21.
|
|
12
|
+
<!-- docguard:version: 0.21.1 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Review Skill
|
|
15
15
|
|
|
@@ -6,10 +6,10 @@ description: CDD maturity assessment with category-aware improvement roadmap. Ru
|
|
|
6
6
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
7
7
|
metadata:
|
|
8
8
|
author: docguard
|
|
9
|
-
version: 0.21.
|
|
9
|
+
version: 0.21.1
|
|
10
10
|
source: extensions/spec-kit-docguard/skills/docguard-score
|
|
11
11
|
---
|
|
12
|
-
<!-- docguard:version: 0.21.
|
|
12
|
+
<!-- docguard:version: 0.21.1 -->
|
|
13
13
|
|
|
14
14
|
# DocGuard Score Skill
|
|
15
15
|
|
|
@@ -4,7 +4,7 @@ description: Keep canonical documentation ALWAYS UP TO DATE. Refreshes code-trut
|
|
|
4
4
|
compatibility: Requires DocGuard CLI installed (npm i -g docguard-cli or npx docguard-cli)
|
|
5
5
|
metadata:
|
|
6
6
|
author: docguard
|
|
7
|
-
version: 0.21.
|
|
7
|
+
version: 0.21.1
|
|
8
8
|
source: extensions/spec-kit-docguard/skills/docguard-sync
|
|
9
9
|
---
|
|
10
10
|
|
package/package.json
CHANGED