Skip to content

Commit c87eca6

Browse files
Address copilot pass 6 on #2
- doctor.js: harden canonical-WebID derivation. Wrap URL parsing in try/catch so a malformed profile @id can't crash diagnostics; only adopt the profile @id when its fragmentless form matches the fetched docUrl, otherwise fall back to the user-supplied URL. This prevents a snippet whose VM id is rooted at one document while the UI tells the user to PATCH a different one. - lib/lws-cid.js: route the self-control check through normalizeControllers too, so all four JSON-LD shapes (string, object-with-@id, object-with-id, array) yield consistent results. Previously, controller={id: ...} or array forms produced an undefined comparison and a misleading warn. Empty/unresolvable controller now reports "does not resolve to a usable IRI" rather than silently degrading.
1 parent 3f00628 commit c87eca6

2 files changed

Lines changed: 30 additions & 11 deletions

File tree

doctor.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,25 @@ async function runAll(webIdUrl) {
151151

152152
// Profile was fetched and parsed — safe to root a snippet against this URL.
153153
// Take the canonical WebID from the profile's own @id (absolutized
154-
// against the document URL); fall back to the user-supplied URL if
155-
// the profile didn't declare one. This preserves any fragment (e.g.
156-
// "#me") so verificationMethod controllers will agree with the
157-
// profile's outer controller predicate.
154+
// against the document URL), but only when its fragmentless form
155+
// matches the URL we actually fetched — otherwise the snippet's VM
156+
// id would be rooted at one document while the UI tells the user to
157+
// patch a different one. Untrusted input, so the URL parse is
158+
// wrapped: malformed @id falls back to the user-supplied URL.
158159
const profileId = profile['@id'] || profile.id;
159-
const canonicalWebId = profileId
160-
? new URL(profileId, docUrl).toString()
161-
: webIdUrl;
160+
let canonicalWebId = webIdUrl;
161+
if (profileId) {
162+
try {
163+
const resolved = new URL(profileId, docUrl);
164+
const resolvedNoHash = new URL(resolved);
165+
resolvedNoHash.hash = '';
166+
if (resolvedNoHash.toString() === docUrl.toString()) {
167+
canonicalWebId = resolved.toString();
168+
}
169+
} catch {
170+
// malformed @id; fall through to user-supplied URL
171+
}
172+
}
162173
result.profileFetched = true;
163174
result.docUrl = docUrl.toString();
164175
result.webId = canonicalWebId;

lib/lws-cid.js

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,24 @@ export function runLwsCidChecks(profile, { webIdUrl }) {
9292
detail: 'CID v1 requires a controller for verification methods to be usable.',
9393
});
9494
} else {
95-
const ctrl = typeof controller === 'string' ? controller : controller['@id'];
96-
const ctrlAbs = absolutize(ctrl, requestedNoHash);
95+
// Normalize through the same helper as the VM check so all four
96+
// JSON-LD shapes (string, object-with-@id, object-with-id, array)
97+
// are handled consistently.
98+
const ctrlList = normalizeControllers(controller, requestedNoHash);
9799
const idAbsolute = absolutize(id, requestedNoHash);
98-
if (ctrlAbs === idAbsolute) {
100+
if (ctrlList.length === 0) {
101+
out.push({
102+
status: 'fail',
103+
label: 'Profile declares a controller',
104+
detail: 'controller is present but does not resolve to a usable IRI.',
105+
});
106+
} else if (ctrlList.includes(idAbsolute)) {
99107
out.push({ status: 'pass', label: 'controller === @id (self-controlled)' });
100108
} else {
101109
out.push({
102110
status: 'warn',
103111
label: 'controller differs from @id',
104-
detail: `controller=${ctrlAbs}, @id=${idAbsolute}. Delegated control is allowed by CID v1 but most pods are self-controlled.`,
112+
detail: `controller=${ctrlList.join(', ')}, @id=${idAbsolute}. Delegated control is allowed by CID v1 but most pods are self-controlled.`,
105113
});
106114
}
107115
}

0 commit comments

Comments
 (0)