javascript-solid-server 0.0.71 → 0.0.73
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/.claude/settings.local.json +9 -1
- package/SECURITY-AUDIT-2026-01-05.md +382 -0
- package/docs/design/nostr-solid-browser-extension.md +1465 -0
- package/package.json +1 -1
- package/src/auth/solid-oidc.js +50 -1
- package/src/handlers/git.js +33 -6
- package/src/idp/interactions.js +5 -0
- package/src/utils/ssrf.js +9 -4
- package/src/utils/url.js +7 -2
- package/src/wac/parser.js +4 -3
- package/test/wac.test.js +36 -0
- package/SECURITY-AUDIT-2026-01-15.md +0 -514
|
@@ -220,7 +220,15 @@
|
|
|
220
220
|
"WebFetch(domain:webfinger.net)",
|
|
221
221
|
"Bash(npm update:*)",
|
|
222
222
|
"Bash(timeout 8 node:*)",
|
|
223
|
-
"Bash(gh pr view:*)"
|
|
223
|
+
"Bash(gh pr view:*)",
|
|
224
|
+
"Bash(gh pr diff:*)",
|
|
225
|
+
"Bash(gh pr review:*)",
|
|
226
|
+
"Bash(gh api:*)",
|
|
227
|
+
"Bash(gh pr comment:*)",
|
|
228
|
+
"Bash(git pull:*)",
|
|
229
|
+
"Bash(gh pr:*)",
|
|
230
|
+
"Bash(node --test:*)",
|
|
231
|
+
"Bash(TOKEN_SECRET=test node --test:*)"
|
|
224
232
|
]
|
|
225
233
|
}
|
|
226
234
|
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# JSS Security Audit Report
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-01-05
|
|
4
|
+
**Auditor:** Automated QE Fleet Analysis
|
|
5
|
+
**Version Audited:** 0.0.71
|
|
6
|
+
**Previous Audit:** 2026-01-03 (v0.0.48)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Executive Summary
|
|
11
|
+
|
|
12
|
+
A comprehensive multi-agent security audit of JavaScriptSolidServer (v0.0.71) was conducted using 5 specialized review agents examining authentication, path traversal, DoS resistance, WAC authorization, and dependency security.
|
|
13
|
+
|
|
14
|
+
**Overall Security Posture:** **GOOD** - Major vulnerabilities from previous audit (v0.0.48-0.0.51) have been fixed. Several medium and low severity issues remain.
|
|
15
|
+
|
|
16
|
+
| Severity | Count | Status |
|
|
17
|
+
|----------|-------|--------|
|
|
18
|
+
| Critical | 0 | - |
|
|
19
|
+
| High | 1 | NEW |
|
|
20
|
+
| Medium | 7 | Open |
|
|
21
|
+
| Low | 8 | Open |
|
|
22
|
+
|
|
23
|
+
**npm audit:** 0 vulnerabilities found
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Previous Audit Issues - Remediation Status
|
|
28
|
+
|
|
29
|
+
| Issue | Previous Severity | Status | Fixed In |
|
|
30
|
+
|-------|-------------------|--------|----------|
|
|
31
|
+
| ACL bypass (unauthenticated .acl access) | Critical | FIXED | v0.0.49 |
|
|
32
|
+
| JWT signature not verified | Critical | FIXED | v0.0.49 |
|
|
33
|
+
| SSRF in OIDC discovery | Critical | FIXED | v0.0.50 |
|
|
34
|
+
| SSRF in client document fetch | Critical | FIXED | v0.0.50 |
|
|
35
|
+
| Path traversal via `..` bypass | Critical | FIXED | v0.0.52 |
|
|
36
|
+
| Unauthenticated pod creation | High | FIXED | v0.0.51 |
|
|
37
|
+
| Default token secret | High | FIXED | v0.0.51 |
|
|
38
|
+
| No rate limiting | Medium | FIXED | v0.0.51 |
|
|
39
|
+
| WebSocket subscription spam | High | FIXED | v0.0.52 |
|
|
40
|
+
| JSON.parse DoS | High | PARTIALLY FIXED | v0.0.52 |
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## New/Open Vulnerabilities
|
|
45
|
+
|
|
46
|
+
### HIGH SEVERITY
|
|
47
|
+
|
|
48
|
+
#### 1. Path Traversal in Git Handler
|
|
49
|
+
|
|
50
|
+
**Location:** `src/handlers/git.js:82-97`
|
|
51
|
+
|
|
52
|
+
**Description:** The git handler does NOT use the secure `urlToPath()` function. Instead, it extracts the repository path and passes it directly to `path.resolve()` without sanitization.
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
const urlPath = decodeURIComponent(request.url.split('?')[0]);
|
|
56
|
+
const repoRelative = extractRepoPath(urlPath);
|
|
57
|
+
const repoAbs = resolve(dataRoot, repoRelative); // No traversal check!
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Attack Vector:**
|
|
61
|
+
```
|
|
62
|
+
GET /../../etc/passwd/info/refs?service=git-upload-pack
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Impact:** Potential directory traversal outside DATA_ROOT when git endpoints are enabled.
|
|
66
|
+
|
|
67
|
+
**Mitigating Factors:**
|
|
68
|
+
- `findGitDir()` validates git-specific files exist, limiting exploitation
|
|
69
|
+
- Git support is optional
|
|
70
|
+
|
|
71
|
+
**CVSS Score:** 7.5 (High)
|
|
72
|
+
|
|
73
|
+
**Recommendation:** Use `urlToPath()` or equivalent sanitization before `path.resolve()`.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### MEDIUM SEVERITY
|
|
78
|
+
|
|
79
|
+
#### 2. DPoP Replay Attack Vulnerability
|
|
80
|
+
|
|
81
|
+
**Location:** `src/auth/solid-oidc.js:178-181`
|
|
82
|
+
|
|
83
|
+
```javascript
|
|
84
|
+
// jti: Unique identifier (we should track these to prevent replay, but skip for now)
|
|
85
|
+
if (!payload.jti) {
|
|
86
|
+
return { thumbprint: null, error: 'DPoP proof missing jti' };
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Description:** DPoP proof `jti` (JWT ID) is required but NOT tracked. The same DPoP proof can be replayed within its 5-minute validity window (`DPOP_MAX_AGE`).
|
|
91
|
+
|
|
92
|
+
**Impact:** Token replay attacks within short window.
|
|
93
|
+
|
|
94
|
+
**CVSS Score:** 5.3 (Medium)
|
|
95
|
+
|
|
96
|
+
**Recommendation:** Implement time-bounded jti cache (5-min TTL).
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
#### 3. Agent Group Authorization Not Implemented
|
|
101
|
+
|
|
102
|
+
**Location:** `src/wac/checker.js:182`
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// TODO: Check agent groups (requires fetching and parsing group documents)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Description:** ACL rules using `acl:agentGroup` are parsed (`src/wac/parser.js:152-153`) but never checked during authorization. Group-based access control silently fails.
|
|
109
|
+
|
|
110
|
+
**Impact:** Resources protected by agent groups may be incorrectly denied or allowed.
|
|
111
|
+
|
|
112
|
+
**CVSS Score:** 6.1 (Medium)
|
|
113
|
+
|
|
114
|
+
**Recommendation:** Implement group document fetching and membership checking.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
#### 4. JSON.parse Without Size Limits (Partial)
|
|
119
|
+
|
|
120
|
+
**Locations:**
|
|
121
|
+
- `src/wac/parser.js:40` - ACL content parsing
|
|
122
|
+
- `src/auth/did-nostr.js:129,141` - DID document parsing
|
|
123
|
+
- `src/nostr/relay.js:242` - WebSocket message parsing
|
|
124
|
+
- `src/auth/token.js:105` - Token payload parsing
|
|
125
|
+
- `src/ap/routes/inbox.js:126` - ActivityPub inbox
|
|
126
|
+
|
|
127
|
+
**Description:** `safeJsonParse()` exists (`src/utils/url.js:242-246`) but is not used consistently.
|
|
128
|
+
|
|
129
|
+
**Impact:** DoS via large JSON payloads in unprotected parsers.
|
|
130
|
+
|
|
131
|
+
**CVSS Score:** 5.3 (Medium)
|
|
132
|
+
|
|
133
|
+
**Recommendation:** Use `safeJsonParse()` consistently across all JSON parsing.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
#### 5. Missing Global Security Headers
|
|
138
|
+
|
|
139
|
+
**Description:** Security headers are only applied for mashlib responses, not globally:
|
|
140
|
+
|
|
141
|
+
**Present (mashlib only):**
|
|
142
|
+
- `X-Frame-Options: DENY`
|
|
143
|
+
- `Content-Security-Policy: frame-ancestors 'none'`
|
|
144
|
+
|
|
145
|
+
**Missing globally:**
|
|
146
|
+
- `X-Content-Type-Options: nosniff`
|
|
147
|
+
- `Strict-Transport-Security` (for HTTPS deployments)
|
|
148
|
+
- `Referrer-Policy`
|
|
149
|
+
|
|
150
|
+
**CVSS Score:** 4.3 (Medium)
|
|
151
|
+
|
|
152
|
+
**Recommendation:** Add security headers via Fastify plugin for all responses.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
#### 6. DNS Resolution Failure Allows SSRF
|
|
157
|
+
|
|
158
|
+
**Location:** `src/utils/ssrf.js:125-130`
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
} catch (err) {
|
|
162
|
+
// DNS resolution failed - could be a legitimate issue or attacker trying to bypass
|
|
163
|
+
// For security, we'll allow it through but log a warning
|
|
164
|
+
console.warn(`DNS resolution failed for ${hostname}: ${err.message}`);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Description:** When DNS resolution fails, the request is allowed to proceed. An attacker could exploit DNS timing or failure to bypass SSRF protection.
|
|
169
|
+
|
|
170
|
+
**Impact:** Potential SSRF bypass via DNS manipulation.
|
|
171
|
+
|
|
172
|
+
**CVSS Score:** 4.9 (Medium)
|
|
173
|
+
|
|
174
|
+
**Recommendation:** Block requests on DNS resolution failure.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
#### 7. RDF Parsing Without Triple Limits
|
|
179
|
+
|
|
180
|
+
**Locations:** `src/rdf/turtle.js:34`, `src/patch/sparql-update.js:106`
|
|
181
|
+
|
|
182
|
+
**Description:** N3.js parser is used without limits on triple count or recursion depth. Combined with 10MB body limit, a crafted Turtle file could cause CPU exhaustion.
|
|
183
|
+
|
|
184
|
+
**CVSS Score:** 5.3 (Medium)
|
|
185
|
+
|
|
186
|
+
**Recommendation:** Add triple count limit (e.g., 100,000 max).
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
#### 8. Verbose Error Messages to Clients
|
|
191
|
+
|
|
192
|
+
**Locations:**
|
|
193
|
+
- `src/handlers/resource.js:501-505` - Turtle parsing errors
|
|
194
|
+
- `src/auth/solid-oidc.js:198` - DPoP proof errors
|
|
195
|
+
|
|
196
|
+
**Description:** Internal error messages from parsing libraries are exposed to clients.
|
|
197
|
+
|
|
198
|
+
**Impact:** Information disclosure about implementation details.
|
|
199
|
+
|
|
200
|
+
**CVSS Score:** 4.3 (Medium)
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
### LOW SEVERITY
|
|
205
|
+
|
|
206
|
+
#### 9. PodName Single-Pass Sanitization
|
|
207
|
+
|
|
208
|
+
**Location:** `src/utils/url.js:69`
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
let safePodName = podName.replace(/\.\./g, '');
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Uses single-pass `..` removal unlike the iterative approach for urlPath. Mitigated by subsequent boundary check.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
#### 10. No Global WebSocket Connection Limit
|
|
219
|
+
|
|
220
|
+
**Location:** `src/notifications/websocket.js`
|
|
221
|
+
|
|
222
|
+
Per-connection subscription limit (100) exists, but no global limit across all connections.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
#### 11. Minimum Username Length
|
|
227
|
+
|
|
228
|
+
**Location:** `src/idp/interactions.js:370-371`
|
|
229
|
+
|
|
230
|
+
Minimum username length is 3 characters, allowing very short usernames.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
#### 12. No Password Strength Validation
|
|
235
|
+
|
|
236
|
+
**Location:** `src/idp/interactions.js`
|
|
237
|
+
|
|
238
|
+
No minimum password length or complexity requirements enforced.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
#### 13. TOKEN_SECRET Entropy Not Validated
|
|
243
|
+
|
|
244
|
+
**Location:** `src/auth/token.js:16-33`
|
|
245
|
+
|
|
246
|
+
Production requires TOKEN_SECRET but doesn't validate minimum entropy/length.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
#### 14. bcrypt Rounds
|
|
251
|
+
|
|
252
|
+
**Location:** `src/idp/accounts.js:41`
|
|
253
|
+
|
|
254
|
+
Uses 10 rounds (acceptable but 12 recommended for future-proofing).
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
#### 15. CORS Reflected Origin Pattern
|
|
259
|
+
|
|
260
|
+
**Location:** `src/ldp/headers.js:93-102`
|
|
261
|
+
|
|
262
|
+
Reflects any origin with credentials. Intentional for Solid interoperability but should be documented.
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
#### 16. Console.error Information Logging
|
|
267
|
+
|
|
268
|
+
**Locations:** `src/handlers/resource.js:129,203,290`
|
|
269
|
+
|
|
270
|
+
Error messages logged to console may reveal internal details if logs are exposed.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Positive Security Findings
|
|
275
|
+
|
|
276
|
+
| Control | Location | Assessment |
|
|
277
|
+
|---------|----------|------------|
|
|
278
|
+
| Path traversal protection | `src/utils/url.js:24-46` | Multi-pass `..` removal + path.resolve boundary check |
|
|
279
|
+
| JWT signature verification | `src/auth/token.js:125-160` | Proper jose.jwtVerify() against JWKS |
|
|
280
|
+
| HMAC timing-safe comparison | `src/auth/token.js:94-101` | crypto.timingSafeEqual() used |
|
|
281
|
+
| Production token secret enforcement | `src/auth/token.js:21-26` | process.exit(1) if not set |
|
|
282
|
+
| ACL Control permission for .acl files | `src/auth/middleware.js:378-389` | Stricter than spec (more secure) |
|
|
283
|
+
| Default deny on missing ACL | `src/wac/checker.js:30-33` | Restrictive default |
|
|
284
|
+
| Rate limiting | `src/server.js`, `src/idp/index.js` | Comprehensive coverage |
|
|
285
|
+
| SSRF protection | `src/utils/ssrf.js` | Private IP blocking, DNS rebinding protection |
|
|
286
|
+
| WebSocket limits | `src/notifications/websocket.js` | 100 subs/connection, 2048 char URL limit |
|
|
287
|
+
| Dotfile blocking | `src/server.js:235-251` | Allowlist approach |
|
|
288
|
+
| Body size limits | `src/server.js:84` | 10MB global, 1MB for IdP |
|
|
289
|
+
| bcrypt password hashing | `src/idp/accounts.js` | Proper implementation |
|
|
290
|
+
| DPoP proof validation | `src/auth/solid-oidc.js` | Method, URI, timestamp, ath, cnf.jkt checks |
|
|
291
|
+
| Nostr NIP-98 validation | `src/auth/nostr.js` | Event kind, timestamp, URL, method, signature |
|
|
292
|
+
| Slug validation | `src/handlers/container.js:56-69` | Strict alphanumeric + length limit |
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## Dependency Security
|
|
297
|
+
|
|
298
|
+
**npm audit:** 0 vulnerabilities
|
|
299
|
+
|
|
300
|
+
| Package | Version | Latest | Notes |
|
|
301
|
+
|---------|---------|--------|-------|
|
|
302
|
+
| jose | 6.1.3 | 6.1.3 | Current - JWT/JWKS handling |
|
|
303
|
+
| bcrypt | 6.0.0 | 6.0.0 | Current - Password hashing |
|
|
304
|
+
| oidc-provider | 9.6.0 | 9.6.0 | Current - OIDC IdP |
|
|
305
|
+
| fastify | 4.29.1 | 5.6.2 | Major version behind (v5 available) |
|
|
306
|
+
| better-sqlite3 | 12.5.0 | 12.5.0 | Current |
|
|
307
|
+
| nostr-tools | 2.19.4 | 2.19.4 | Current |
|
|
308
|
+
|
|
309
|
+
**Outdated (non-security):**
|
|
310
|
+
- @fastify/middie: 8.3.3 -> 9.1.0
|
|
311
|
+
- @fastify/rate-limit: 9.1.0 -> 10.3.0
|
|
312
|
+
- @fastify/websocket: 8.3.1 -> 11.2.0
|
|
313
|
+
- fastify: 4.29.1 -> 5.6.2
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Recommendations
|
|
318
|
+
|
|
319
|
+
### Priority 1 (High)
|
|
320
|
+
|
|
321
|
+
1. **Fix git handler path traversal** - Use `urlToPath()` in `src/handlers/git.js`
|
|
322
|
+
|
|
323
|
+
### Priority 2 (Medium)
|
|
324
|
+
|
|
325
|
+
2. **Implement DPoP jti tracking** - Add time-bounded cache for replay prevention
|
|
326
|
+
3. **Implement agent group checking** - Complete TODO in `src/wac/checker.js`
|
|
327
|
+
4. **Use safeJsonParse consistently** - Replace raw `JSON.parse()` calls
|
|
328
|
+
5. **Add global security headers** - X-Content-Type-Options, HSTS
|
|
329
|
+
6. **Block on DNS failure** - Change SSRF to deny on DNS resolution failure
|
|
330
|
+
|
|
331
|
+
### Priority 3 (Low)
|
|
332
|
+
|
|
333
|
+
7. Add RDF triple count limits
|
|
334
|
+
8. Add password strength requirements
|
|
335
|
+
9. Validate TOKEN_SECRET entropy
|
|
336
|
+
10. Add global WebSocket connection limit
|
|
337
|
+
11. Consider Fastify v5 upgrade path
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## Changes Since Last Audit (v0.0.51 -> v0.0.71)
|
|
342
|
+
|
|
343
|
+
20 releases since last audit, including:
|
|
344
|
+
|
|
345
|
+
- **v0.0.52** - Security hardening (path traversal fix)
|
|
346
|
+
- **v0.0.53** - Stricter pod creation rate limit
|
|
347
|
+
- **v0.0.54** - SSRF false positive fix
|
|
348
|
+
- **v0.0.55** - Secure cookie flag for HTTPS
|
|
349
|
+
- **v0.0.56-0.0.58** - Invite-only registration, quotas, did:nostr
|
|
350
|
+
- **v0.0.59-0.0.61** - Nostr relay, ActivityPub federation
|
|
351
|
+
- **v0.0.62-0.0.66** - ActivityPub fixes, Android support
|
|
352
|
+
- **v0.0.67-0.0.71** - UI improvements, error pages, mashlib integration
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
## Legacy Audit Files
|
|
357
|
+
|
|
358
|
+
| File | Status | Action |
|
|
359
|
+
|------|--------|--------|
|
|
360
|
+
| SECURITY-AUDIT-2026-01-03.md | Outdated (v0.0.48) | Retained for history |
|
|
361
|
+
| SECURITY-AUDIT-2026-01-15.md | Invalid (future date) | **REMOVED** |
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## Conclusion
|
|
366
|
+
|
|
367
|
+
JavaScriptSolidServer v0.0.71 demonstrates strong security fundamentals with all critical vulnerabilities from previous audits remediated. The primary concern is the **HIGH severity path traversal in the git handler** which should be addressed promptly.
|
|
368
|
+
|
|
369
|
+
The codebase shows security-conscious design patterns including:
|
|
370
|
+
- Defense-in-depth path traversal protection
|
|
371
|
+
- Proper cryptographic verification
|
|
372
|
+
- Comprehensive rate limiting
|
|
373
|
+
- SSRF protection with DNS rebinding mitigation
|
|
374
|
+
- Secure defaults (deny on missing ACL)
|
|
375
|
+
|
|
376
|
+
**Overall Assessment:** Suitable for production use after addressing the git handler path traversal issue.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
_Report generated: 2026-01-05_
|
|
381
|
+
_Audit methodology: Multi-agent QE fleet (5 specialized reviewers)_
|
|
382
|
+
_Next audit recommended: 2026-04-05_
|