-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathcontainer.js
More file actions
76 lines (69 loc) · 2.89 KB
/
Copy pathcontainer.js
File metadata and controls
76 lines (69 loc) · 2.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* Generate container representation as JSON-LD
*/
const LDP = 'http://www.w3.org/ns/ldp#';
// Dotfiles allowed to appear in ldp:contains. Anything else starting with '.'
// is server-internal state and must not leak into container listings — even
// when direct GETs are 403'd by the routing-layer dotfile guard in server.js
// (which rejects non-allowlisted dotpaths before WAC even runs), listing the
// *name* still leaks existence and gives attackers free path-fingerprinting
// (#350).
//
// `.well-known` is allowed because JSS exposes legitimate public resources
// there (e.g. the webledger registry at /.well-known/webledgers/...). At the
// origin root — including each pod's own origin in subdomain mode — server.js
// bypasses auth for `/.well-known/*` per RFC 8615. For path-based pods at
// `/pod/.well-known/`, the bypass does *not* apply (it matches root-relative
// paths only) — that case is a regular subdirectory governed by ordinary WAC,
// and listing the name is fine. We allow `.well-known` uniformly here so the
// subdomain-pod and root-pod cases work without conditional logic on the
// container path.
//
// Internal state that JSS currently persists under `.well-known/` (token
// store, pay state) shouldn't be in a public namespace at all; tracked at
// #358.
//
// `.acl` and `.meta` are canonical Solid per-resource sidecars.
const ALLOWED_DOTFILES = new Set(['.acl', '.meta', '.well-known']);
function isHiddenEntry(name) {
return name.startsWith('.') && !ALLOWED_DOTFILES.has(name);
}
/**
* Generate JSON-LD representation of a container
* @param {string} containerUrl - Full URL of the container
* @param {Array<{name: string, isDirectory: boolean}>} entries - Container contents
* @returns {object} - JSON-LD representation
*/
export function generateContainerJsonLd(containerUrl, entries) {
// Ensure container URL ends with /
const baseUrl = containerUrl.endsWith('/') ? containerUrl : containerUrl + '/';
const contains = entries.filter(entry => !isHiddenEntry(entry.name)).map(entry => {
const childUrl = baseUrl + entry.name + (entry.isDirectory ? '/' : '');
const item = {
'@id': childUrl,
'@type': entry.isDirectory ? [`${LDP}Container`, `${LDP}BasicContainer`, `${LDP}Resource`] : [`${LDP}Resource`]
};
if (entry.size != null) item['stat:size'] = entry.size;
if (entry.modified) item['dcterms:modified'] = entry.modified;
return item;
});
return {
'@context': {
'ldp': LDP,
'stat': 'http://www.w3.org/ns/posix/stat#',
'dcterms': 'http://purl.org/dc/terms/',
'contains': { '@id': 'ldp:contains', '@type': '@id' }
},
'@id': baseUrl,
'@type': ['ldp:Container', 'ldp:BasicContainer', 'ldp:Resource'],
'contains': contains
};
}
/**
* Convert JSON-LD to string
* @param {object} jsonLd
* @returns {string}
*/
export function serializeJsonLd(jsonLd) {
return JSON.stringify(jsonLd, null, 2);
}