@yemi33/minions 0.1.1157 → 0.1.1159
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/CHANGELOG.md +110 -0
- package/dashboard.js +125 -4
- package/engine/shared.js +52 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,115 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1159 (2026-04-18)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
- Header gate — Origin allowlist, Content-Type enforcement, security response headers (#1336)
|
|
7
|
+
- Header gate — Origin allowlist, Content-Type enforcement, security response headers (#1327)
|
|
8
|
+
- escapeHtml helper, hotspot XSS migration, regression gate (#1323)
|
|
9
|
+
- Prototype pollution guard in readBody (SEC-13) (#1300)
|
|
10
|
+
- redact ADO tokens and JWTs from engine/log.json writes (#1297)
|
|
11
|
+
- SEC-02 — replace curl shell-out in ado.js with adoFetch (#1296)
|
|
12
|
+
- validate project name and path on POST /api/projects/add (SEC-04, SEC-05) (#1298)
|
|
13
|
+
- seed realActivityMap at spawn time, stamp pid in live-output (#1200)
|
|
14
|
+
|
|
15
|
+
### Fixes
|
|
16
|
+
- ado merge-commit mismatch preserves prior buildStatus (closes #1233) (#1326)
|
|
17
|
+
- scheduler substitutes {{date}} in title and description (#1275)
|
|
18
|
+
- prevent archived plans from re-triggering via orphaned .backup restore
|
|
19
|
+
- pinned context optimistic save (closes #1295) (#1316)
|
|
20
|
+
- re-check throttle state per-PR iteration to avoid stale fixThrottled
|
|
21
|
+
- preserve buildErrorLog through transient states, persist poll time (#1273)
|
|
22
|
+
- auto-fetch PR title on link-pr (closes #1283) (#1299)
|
|
23
|
+
- scheduler double-fire within same cron minute (#1277)
|
|
24
|
+
- gate auto-fix dispatch on throttle state to prevent stale-data spurious fixes
|
|
25
|
+
- preserve buildErrorLog through transient build states (#1232) (#1274)
|
|
26
|
+
- annotate fast-exit empty-output failures with diagnostic hint (#1276)
|
|
27
|
+
- invalidate PRD cache on pr-links.json change + guard aggregate PRs (#1220) (#1272)
|
|
28
|
+
- pass --add-dir for minions + ~/.claude to agents (#1271)
|
|
29
|
+
- preserve VERDICT marker by tail-slicing agent output (#1234) (#1270)
|
|
30
|
+
- PRD info cache staleness and aggregate PR bleed-through (#1222)
|
|
31
|
+
- avoid no-op work item writes
|
|
32
|
+
- resilient claude binary resolution + surface spawn errors
|
|
33
|
+
- resolve native claude.exe from npm wrapper on Windows
|
|
34
|
+
- cap temp-agent creation at maxConcurrent per tick (#1219)
|
|
35
|
+
- reassign pending items from unspawned temp agents to idle named agents (#1204) (#1212)
|
|
36
|
+
|
|
37
|
+
### Other
|
|
38
|
+
- refactor: hoist fixThrottled before PR loop and drop underscore prefix
|
|
39
|
+
- docs(skill): add substitute-scheduler-template-vars (#1278)
|
|
40
|
+
- Clarify PR poll labels
|
|
41
|
+
- Prevent modal opens on text selection
|
|
42
|
+
- refactor: clarify settings page section structure and PR polling dependencies
|
|
43
|
+
- refactor: extract _probeClaudePackage helper, use shared.log for spawn errors
|
|
44
|
+
- Make work item descriptions scrollable
|
|
45
|
+
- Use PAT for publish merges
|
|
46
|
+
- test(queries): add unit tests for invalidateDispatchCache/getInbox/getAgentCharter (#1214)
|
|
47
|
+
- test(shared): add unit tests for truncateTextBytes/tailTextBytes/execSilent/trackReviewMetric/parseCanonicalPrId (#1215)
|
|
48
|
+
- Fix publish workflow merge
|
|
49
|
+
- chore: raise default meeting round timeout
|
|
50
|
+
- Harden prompt context handling
|
|
51
|
+
- Harden loop watch conversion
|
|
52
|
+
- Add watches sidebar activity badge
|
|
53
|
+
- test(cli): add unit tests for handleCommand, start, stop, kill, spawn (#1191)
|
|
54
|
+
- chore: untrack pipeline files — local config only
|
|
55
|
+
- restore: recover daily-arch-improvement and weekly-dead-code-cleanup pipelines
|
|
56
|
+
- Harden CC stream resilience
|
|
57
|
+
|
|
58
|
+
## 0.1.1158 (2026-04-18)
|
|
59
|
+
|
|
60
|
+
### Features
|
|
61
|
+
- Header gate — Origin allowlist, Content-Type enforcement, security response headers (#1336)
|
|
62
|
+
- Header gate — Origin allowlist, Content-Type enforcement, security response headers (#1327)
|
|
63
|
+
- escapeHtml helper, hotspot XSS migration, regression gate (#1323)
|
|
64
|
+
- Prototype pollution guard in readBody (SEC-13) (#1300)
|
|
65
|
+
- redact ADO tokens and JWTs from engine/log.json writes (#1297)
|
|
66
|
+
- SEC-02 — replace curl shell-out in ado.js with adoFetch (#1296)
|
|
67
|
+
- validate project name and path on POST /api/projects/add (SEC-04, SEC-05) (#1298)
|
|
68
|
+
- seed realActivityMap at spawn time, stamp pid in live-output (#1200)
|
|
69
|
+
|
|
70
|
+
### Fixes
|
|
71
|
+
- ado merge-commit mismatch preserves prior buildStatus (closes #1233) (#1326)
|
|
72
|
+
- scheduler substitutes {{date}} in title and description (#1275)
|
|
73
|
+
- prevent archived plans from re-triggering via orphaned .backup restore
|
|
74
|
+
- pinned context optimistic save (closes #1295) (#1316)
|
|
75
|
+
- re-check throttle state per-PR iteration to avoid stale fixThrottled
|
|
76
|
+
- preserve buildErrorLog through transient states, persist poll time (#1273)
|
|
77
|
+
- auto-fetch PR title on link-pr (closes #1283) (#1299)
|
|
78
|
+
- scheduler double-fire within same cron minute (#1277)
|
|
79
|
+
- gate auto-fix dispatch on throttle state to prevent stale-data spurious fixes
|
|
80
|
+
- preserve buildErrorLog through transient build states (#1232) (#1274)
|
|
81
|
+
- annotate fast-exit empty-output failures with diagnostic hint (#1276)
|
|
82
|
+
- invalidate PRD cache on pr-links.json change + guard aggregate PRs (#1220) (#1272)
|
|
83
|
+
- pass --add-dir for minions + ~/.claude to agents (#1271)
|
|
84
|
+
- preserve VERDICT marker by tail-slicing agent output (#1234) (#1270)
|
|
85
|
+
- PRD info cache staleness and aggregate PR bleed-through (#1222)
|
|
86
|
+
- avoid no-op work item writes
|
|
87
|
+
- resilient claude binary resolution + surface spawn errors
|
|
88
|
+
- resolve native claude.exe from npm wrapper on Windows
|
|
89
|
+
- cap temp-agent creation at maxConcurrent per tick (#1219)
|
|
90
|
+
- reassign pending items from unspawned temp agents to idle named agents (#1204) (#1212)
|
|
91
|
+
|
|
92
|
+
### Other
|
|
93
|
+
- refactor: hoist fixThrottled before PR loop and drop underscore prefix
|
|
94
|
+
- docs(skill): add substitute-scheduler-template-vars (#1278)
|
|
95
|
+
- Clarify PR poll labels
|
|
96
|
+
- Prevent modal opens on text selection
|
|
97
|
+
- refactor: clarify settings page section structure and PR polling dependencies
|
|
98
|
+
- refactor: extract _probeClaudePackage helper, use shared.log for spawn errors
|
|
99
|
+
- Make work item descriptions scrollable
|
|
100
|
+
- Use PAT for publish merges
|
|
101
|
+
- test(queries): add unit tests for invalidateDispatchCache/getInbox/getAgentCharter (#1214)
|
|
102
|
+
- test(shared): add unit tests for truncateTextBytes/tailTextBytes/execSilent/trackReviewMetric/parseCanonicalPrId (#1215)
|
|
103
|
+
- Fix publish workflow merge
|
|
104
|
+
- chore: raise default meeting round timeout
|
|
105
|
+
- Harden prompt context handling
|
|
106
|
+
- Harden loop watch conversion
|
|
107
|
+
- Add watches sidebar activity badge
|
|
108
|
+
- test(cli): add unit tests for handleCommand, start, stop, kill, spawn (#1191)
|
|
109
|
+
- chore: untrack pipeline files — local config only
|
|
110
|
+
- restore: recover daily-arch-improvement and weekly-dead-code-cleanup pipelines
|
|
111
|
+
- Harden CC stream resilience
|
|
112
|
+
|
|
3
113
|
## 0.1.1157 (2026-04-18)
|
|
4
114
|
|
|
5
115
|
### Features
|
package/dashboard.js
CHANGED
|
@@ -1323,7 +1323,10 @@ function checkRateLimit(key, maxPerMinute) {
|
|
|
1323
1323
|
|
|
1324
1324
|
function jsonReply(res, code, data, req) {
|
|
1325
1325
|
res.setHeader('Content-Type', 'application/json');
|
|
1326
|
-
|
|
1326
|
+
// Access-Control-Allow-Origin is set ONCE by the server dispatcher prelude:
|
|
1327
|
+
// `*` for GET/HEAD (read-only), never for mutating responses (Origin gate
|
|
1328
|
+
// already blocked cross-origin POSTs). Setting it here would reopen the
|
|
1329
|
+
// cross-origin write path.
|
|
1327
1330
|
res.statusCode = code;
|
|
1328
1331
|
const json = JSON.stringify(data);
|
|
1329
1332
|
const ae = req && req.headers && req.headers['accept-encoding'] || '';
|
|
@@ -1443,17 +1446,95 @@ function restartEngine() {
|
|
|
1443
1446
|
|
|
1444
1447
|
// -- Server --
|
|
1445
1448
|
|
|
1449
|
+
// Mutating HTTP methods that require Origin and Content-Type gating.
|
|
1450
|
+
// GET/HEAD/OPTIONS are treated as read-only/preflight and bypass these checks.
|
|
1451
|
+
const MUTATING_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE']);
|
|
1452
|
+
|
|
1446
1453
|
const server = http.createServer(async (req, res) => {
|
|
1447
|
-
//
|
|
1454
|
+
// ── Security headers (applied to every response) ──────────────────────────
|
|
1455
|
+
// Baseline CSP + clickjacking/mime/referrer protections. The dashboard HTML
|
|
1456
|
+
// entry-point later overrides CSP for its inline <script>/<style> blocks;
|
|
1457
|
+
// all API (JSON, text, SSE) responses inherit the strict CSP from here.
|
|
1458
|
+
const _secHeaders = shared.buildSecurityHeaders();
|
|
1459
|
+
for (const [k, v] of Object.entries(_secHeaders)) {
|
|
1460
|
+
try { res.setHeader(k, v); } catch { /* headers may already be sent in rare error paths */ }
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
// ── Origin gate (mutating + OPTIONS preflight) ────────────────────────────
|
|
1464
|
+
// Defense-in-depth against CSRF / DNS-rebinding: even though the dashboard
|
|
1465
|
+
// binds to 127.0.0.1, a malicious page can coerce a user's browser to POST
|
|
1466
|
+
// to localhost. We reject any mutating request whose Origin (or Referer, if
|
|
1467
|
+
// Origin is absent) is not in the local allowlist. When both headers are
|
|
1468
|
+
// absent (curl, CLI tooling, Node http.request without Origin) we allow the
|
|
1469
|
+
// request to preserve existing local automation.
|
|
1470
|
+
const _rawOrigin = req.headers['origin'];
|
|
1471
|
+
const _rawReferer = req.headers['referer'];
|
|
1472
|
+
const _isMutating = MUTATING_METHODS.has(req.method);
|
|
1473
|
+
|
|
1474
|
+
function _originGateReject(reason) {
|
|
1475
|
+
console.warn(`[origin-gate] reject ${req.method} ${req.url} origin=${_rawOrigin || _rawReferer || '(none)'} reason=${reason}`);
|
|
1476
|
+
res.statusCode = 403;
|
|
1477
|
+
res.setHeader('Content-Type', 'application/json');
|
|
1478
|
+
res.end(JSON.stringify({ error: 'Origin not allowed' }));
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
// CORS preflight — echo validated origin; reject disallowed origins outright.
|
|
1448
1482
|
if (req.method === 'OPTIONS') {
|
|
1449
|
-
|
|
1450
|
-
|
|
1483
|
+
if (_rawOrigin) {
|
|
1484
|
+
if (!shared.isAllowedOrigin(_rawOrigin)) { _originGateReject('preflight-origin'); return; }
|
|
1485
|
+
res.setHeader('Access-Control-Allow-Origin', _rawOrigin);
|
|
1486
|
+
res.setHeader('Vary', 'Origin');
|
|
1487
|
+
}
|
|
1488
|
+
// Note: when Origin is absent (non-browser preflight), no ACAO is echoed —
|
|
1489
|
+
// that's fine, only browsers care about ACAO and they always send Origin.
|
|
1490
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS');
|
|
1451
1491
|
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
1452
1492
|
res.statusCode = 204;
|
|
1453
1493
|
res.end();
|
|
1454
1494
|
return;
|
|
1455
1495
|
}
|
|
1456
1496
|
|
|
1497
|
+
// Mutating requests: enforce Origin allowlist (Origin first, Referer fallback).
|
|
1498
|
+
if (_isMutating) {
|
|
1499
|
+
if (_rawOrigin) {
|
|
1500
|
+
if (!shared.isAllowedOrigin(_rawOrigin)) { _originGateReject('origin'); return; }
|
|
1501
|
+
} else if (_rawReferer) {
|
|
1502
|
+
if (!shared.isAllowedOrigin(_rawReferer)) { _originGateReject('referer'); return; }
|
|
1503
|
+
}
|
|
1504
|
+
// Neither Origin nor Referer present → legacy tooling (curl, Node.js) is allowed.
|
|
1505
|
+
|
|
1506
|
+
// Content-Type enforcement: require application/json on mutating requests.
|
|
1507
|
+
// readBody() always expects JSON. Rejecting anything other than
|
|
1508
|
+
// application/json closes the entire CSRF "simple request" loophole
|
|
1509
|
+
// (text/plain, application/x-www-form-urlencoded, multipart/form-data are
|
|
1510
|
+
// all cross-origin-postable without a preflight). DELETE with an empty
|
|
1511
|
+
// body (Content-Length: 0) is exempt — it carries no payload to parse.
|
|
1512
|
+
const ct = String(req.headers['content-type'] || '').toLowerCase();
|
|
1513
|
+
const clen = parseInt(req.headers['content-length'] || '0', 10) || 0;
|
|
1514
|
+
const hasBody = clen > 0 || req.headers['transfer-encoding'];
|
|
1515
|
+
if (hasBody && !ct.startsWith('application/json')) {
|
|
1516
|
+
res.statusCode = 415;
|
|
1517
|
+
res.setHeader('Content-Type', 'application/json');
|
|
1518
|
+
res.end(JSON.stringify({ error: 'Content-Type must be application/json' }));
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
// Empty-body mutating request (e.g. DELETE /api/cc-sessions/:id) with
|
|
1522
|
+
// no Content-Type is allowed — no body, no CSRF-friendly payload.
|
|
1523
|
+
if (!hasBody && ct && !ct.startsWith('application/json')) {
|
|
1524
|
+
res.statusCode = 415;
|
|
1525
|
+
res.setHeader('Content-Type', 'application/json');
|
|
1526
|
+
res.end(JSON.stringify({ error: 'Content-Type must be application/json' }));
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// GET: permit cross-origin reads for external monitoring tools (curl, uptime
|
|
1532
|
+
// checks). Mutating responses deliberately do NOT set ACAO — cross-origin
|
|
1533
|
+
// browsers cannot use them anyway (Origin check blocks that path).
|
|
1534
|
+
if (req.method === 'GET' || req.method === 'HEAD') {
|
|
1535
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1457
1538
|
// ── Route Handler Functions ───────────────────────────────────────────────
|
|
1458
1539
|
|
|
1459
1540
|
async function handlePlansTriggerVerify(req, res) {
|
|
@@ -2289,6 +2370,20 @@ const server = http.createServer(async (req, res) => {
|
|
|
2289
2370
|
}
|
|
2290
2371
|
|
|
2291
2372
|
async function handleAgentLiveStream(req, res, match) {
|
|
2373
|
+
// SSE Origin gate — must run BEFORE res.writeHead(200, ...) so we can
|
|
2374
|
+
// still return a 403 with a normal status line. Once we upgrade to SSE
|
|
2375
|
+
// the client has no way to distinguish a rejection from a dropped stream.
|
|
2376
|
+
// GETs normally skip the Origin check, but SSE streams are long-lived and
|
|
2377
|
+
// warrant the same cross-origin guard as mutating endpoints.
|
|
2378
|
+
const _origin = req.headers['origin'];
|
|
2379
|
+
if (_origin && !shared.isAllowedOrigin(_origin)) {
|
|
2380
|
+
console.warn(`[sse-origin-gate] reject GET ${req.url} origin=${_origin}`);
|
|
2381
|
+
res.statusCode = 403;
|
|
2382
|
+
res.setHeader('Content-Type', 'application/json');
|
|
2383
|
+
res.end(JSON.stringify({ error: 'Origin not allowed' }));
|
|
2384
|
+
return;
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2292
2387
|
const agentId = match[1];
|
|
2293
2388
|
const agentDir = path.join(MINIONS_DIR, 'agents', agentId);
|
|
2294
2389
|
const liveLogPath = path.join(agentDir, 'live-output.log');
|
|
@@ -4021,6 +4116,18 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
4021
4116
|
}
|
|
4022
4117
|
|
|
4023
4118
|
async function handleCommandCenterStream(req, res) {
|
|
4119
|
+
// SSE Origin gate (belt-and-suspenders: the top-level dispatcher has
|
|
4120
|
+
// already rejected disallowed origins on POST, but validate again here
|
|
4121
|
+
// before res.writeHead(200, text/event-stream) so any future refactor
|
|
4122
|
+
// that moves the route can't accidentally bypass the check).
|
|
4123
|
+
const _origin = req.headers['origin'];
|
|
4124
|
+
if (_origin && !shared.isAllowedOrigin(_origin)) {
|
|
4125
|
+
console.warn(`[sse-origin-gate] reject POST /api/command-center/stream origin=${_origin}`);
|
|
4126
|
+
res.statusCode = 403;
|
|
4127
|
+
res.setHeader('Content-Type', 'application/json');
|
|
4128
|
+
res.end(JSON.stringify({ error: 'Origin not allowed' }));
|
|
4129
|
+
return;
|
|
4130
|
+
}
|
|
4024
4131
|
if (checkRateLimit('command-center', 10)) { res.statusCode = 429; res.end('Rate limited'); return; }
|
|
4025
4132
|
let tabId;
|
|
4026
4133
|
let _ccStreamAbort = null;
|
|
@@ -5366,6 +5473,20 @@ What would you like to discuss or change? When you're happy, say "approve" and I
|
|
|
5366
5473
|
res.setHeader('Content-Type', 'text/html; charset=utf-8');
|
|
5367
5474
|
res.setHeader('ETag', HTML_ETAG);
|
|
5368
5475
|
res.setHeader('Cache-Control', 'no-cache'); // revalidate each time, but use 304 if unchanged
|
|
5476
|
+
// The SPA ships as a single self-contained HTML: inline <script> block,
|
|
5477
|
+
// inline <style> block, and many inline onclick= handlers on page
|
|
5478
|
+
// fragments. Strict `script-src 'self'` would break every button. We
|
|
5479
|
+
// relax the default CSP ONLY for the dashboard HTML entry-point — the
|
|
5480
|
+
// strict CSP still applies to all /api/* responses (verified by tests).
|
|
5481
|
+
// data: in img-src permits the inline SVG favicon (<link rel="icon" href="data:...">).
|
|
5482
|
+
res.setHeader(
|
|
5483
|
+
'Content-Security-Policy',
|
|
5484
|
+
"default-src 'self'; " +
|
|
5485
|
+
"script-src 'self' 'unsafe-inline'; " +
|
|
5486
|
+
"style-src 'self' 'unsafe-inline'; " +
|
|
5487
|
+
"img-src 'self' data:; " +
|
|
5488
|
+
"connect-src 'self'"
|
|
5489
|
+
);
|
|
5369
5490
|
if (req.headers['if-none-match'] === HTML_ETAG) {
|
|
5370
5491
|
res.statusCode = 304;
|
|
5371
5492
|
res.end();
|
package/engine/shared.js
CHANGED
|
@@ -1131,6 +1131,56 @@ function sanitizeBranch(name) {
|
|
|
1131
1131
|
return String(name).replace(/[^a-zA-Z0-9._\-\/]/g, '-').slice(0, 200);
|
|
1132
1132
|
}
|
|
1133
1133
|
|
|
1134
|
+
// ── HTTP Origin Allowlist & Security Headers ─────────────────────────────────
|
|
1135
|
+
// Pure helpers used by dashboard.js to gate mutating requests against an
|
|
1136
|
+
// explicit allowlist of local origins and to attach uniform security response
|
|
1137
|
+
// headers. Extracted here so they're unit-testable without the HTTP server.
|
|
1138
|
+
|
|
1139
|
+
// Allowed origin (scheme + host) — port-agnostic. Dashboard always binds to
|
|
1140
|
+
// 127.0.0.1:7331 locally; browser tabs may arrive as localhost, 127.0.0.1, or
|
|
1141
|
+
// IPv6 [::1] depending on how the user opened the page.
|
|
1142
|
+
// WHATWG URL keeps IPv6 brackets in `hostname`, so we compare against the
|
|
1143
|
+
// bracketed form `[::1]`. We also accept the bare form `::1` defensively.
|
|
1144
|
+
const _ALLOWED_ORIGIN_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
|
|
1145
|
+
const _ALLOWED_ORIGIN_SCHEMES = new Set(['http:']);
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Returns true if the origin-like header value (either an `Origin` header value
|
|
1149
|
+
* such as `http://localhost:7331` or a full `Referer` URL) belongs to the local
|
|
1150
|
+
* dashboard allowlist. Port-agnostic. Returns false for null/undefined/empty,
|
|
1151
|
+
* the literal string `'null'` (sandboxed iframes, data: URIs), malformed URLs,
|
|
1152
|
+
* non-http schemes, and any host not in the allowlist.
|
|
1153
|
+
* @param {string|null|undefined} origin
|
|
1154
|
+
* @returns {boolean}
|
|
1155
|
+
*/
|
|
1156
|
+
function isAllowedOrigin(origin) {
|
|
1157
|
+
if (!origin || typeof origin !== 'string') return false;
|
|
1158
|
+
const trimmed = origin.trim();
|
|
1159
|
+
if (!trimmed || trimmed === 'null') return false;
|
|
1160
|
+
let parsed;
|
|
1161
|
+
try { parsed = new URL(trimmed); } catch { return false; }
|
|
1162
|
+
if (!parsed.hostname) return false;
|
|
1163
|
+
if (!_ALLOWED_ORIGIN_SCHEMES.has(parsed.protocol)) return false;
|
|
1164
|
+
return _ALLOWED_ORIGIN_HOSTS.has(parsed.hostname);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Returns the baseline set of security response headers to apply on every HTTP
|
|
1169
|
+
* response from the dashboard. Values match OWASP defaults for a same-origin
|
|
1170
|
+
* SPA served from 127.0.0.1. The HTML entry-point response intentionally
|
|
1171
|
+
* overrides CSP to allow its inline `<script>` / `<style>` blocks; API (JSON,
|
|
1172
|
+
* SSE) responses inherit the strict CSP returned here.
|
|
1173
|
+
* @returns {{[key:string]: string}}
|
|
1174
|
+
*/
|
|
1175
|
+
function buildSecurityHeaders() {
|
|
1176
|
+
return {
|
|
1177
|
+
'Content-Security-Policy': "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'",
|
|
1178
|
+
'X-Frame-Options': 'DENY',
|
|
1179
|
+
'X-Content-Type-Options': 'nosniff',
|
|
1180
|
+
'Referrer-Policy': 'same-origin',
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1134
1184
|
// ── Project Name / Path Validation (SEC-04 / SEC-05) ─────────────────────────
|
|
1135
1185
|
// Enforced at API boundaries (e.g. POST /api/projects/add). Callers that skip
|
|
1136
1186
|
// these validators leak caller-controlled strings into worktree paths, config
|
|
@@ -1809,6 +1859,8 @@ module.exports = {
|
|
|
1809
1859
|
getAdoOrgBase,
|
|
1810
1860
|
sanitizePath,
|
|
1811
1861
|
sanitizeBranch,
|
|
1862
|
+
isAllowedOrigin,
|
|
1863
|
+
buildSecurityHeaders,
|
|
1812
1864
|
hasDangerousKey,
|
|
1813
1865
|
validateProjectName,
|
|
1814
1866
|
validateProjectPath,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yemi33/minions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1159",
|
|
4
4
|
"description": "Multi-agent AI dev team that runs from ~/.minions/ — five autonomous agents share a single engine, dashboard, and knowledge base",
|
|
5
5
|
"bin": {
|
|
6
6
|
"minions": "bin/minions.js"
|