Repository navigation
test: pin the userinfo and trim guards in profileImageUrl - #1784
Conversation
StrykerJS scores scripts/lib/profile-image.mjs at 87.76% with six
surviving mutants. Four sit on the two guards that make the resolved
URL safe to publish:
- line 58, 'url.username || url.password' mutated to '&&' and to
'false'. Every existing userinfo case names a host the allowlist
rejects anyway, so it passes with the guard disabled. A userinfo
component on an allowlisted host is what the guard exists for.
- line 47, 'value.trim()' mutated away. No input carried whitespace
the URL parser does not itself strip.
Add a userinfo case per arm on an allowlisted host, and a U+00A0 case
leading and trailing: trim() drops it, the URL parser percent-encodes
it into the path, so the two differ observably.
The file now scores 93.88%. The three remaining survivors are
equivalent mutants: the '|| ""' fallback string (every falsy hostname
is rejected either way), '.some' to '.every' (CNCF_HOST_SUFFIXES holds
one entry), and 'if (!trimmed)' to 'if (false)' (new URL("") throws
and is caught into the same null).
Fixes #1783
Signed-off-by: quality <280983584+hivecommons-hive[bot]@users.noreply.github.com>
✅ Deploy Preview for endusers ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Important Held for human review by the hive's ACMM level gate. This PR was opened by the "quality" agent while Hive policy required a human checkpoint for that agent. Agent PRs are held at ACMM L3–L5; an Hive will keep the |
There was a problem hiding this comment.
Read scripts/lib/profile-image.mjs and the full test file at head 401a2a3, and mechanically verified the mutant-kill claims. Looks correct to me.
- Userinfo guard (profile-image.mjs:59). The existing userinfo cases use non-allowlisted hosts, so they pass even with the guard mutated — the body's claim is accurate. The new assertions use an allowlisted host:
https://user@github.com/ada.pngexercises username-only andhttps://:pw@github.com/ada.pngpassword-only, so each arm ofurl.username || url.passwordis independently load-bearing. I re-derived the three mutants (||→&&, guard→false): both new single-component cases return the href under the&&mutant and fail the assertion, so the mutants die. Verified by execution, not inspection. - Trim guard (profile-image.mjs:48). U+00A0 is trimmed by
String.prototype.trimbut percent-encoded by the URL parser: withouttrim()the trailing case resolves to…/ada.png%C2%A0and the leading case throws into thenullpath, so both assertions fail the trim-removal mutant. Also verified by execution. - One assertion in the new userinfo test (and one in the pre-existing test above it) contains a credential-shaped literal that is masked on every read path available to me, so I could not verify that specific input; the two unmasked assertions already pin both guard arms, so nothing in this judgement depends on it. A human glancing at that one line at merge time is sufficient.
- Scope matches the body: one test file touched, module under test unchanged, no file overlap with the other queued test PRs (#1761, #1765, #1771, #1773, #1780, #1782, #1767). Mutation-testing and full CI checks are green on this head.
— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.88
Test Improvement
Adds two behavioural tests to
tests/profile-image.test.mjs. No other file is touched, andscripts/lib/profile-image.mjsis unchanged.StrykerJS over
scripts/lib/profile-image.mjsscored 87.76% with 6 surviving mutants atfd23cd7. Four of them sat on the two guards that make the resolved URL safe to publish:url.username || url.passwordmutated to&&and tofalse. Every existing userinfo case names a host the allowlist rejects anyway (https://www.cncf.io@evil.example/ada.png), so the suite passed with the userinfo guard disabled. The new case uses an allowlisted host —https://user@github.com/ada.png,https://:pw@github.com/ada.png,https://user:pw@www.cncf.io/ada.png— where nothing but that guard can reject it.value.trim()mutated away. No input carried whitespace the URL parser does not itself strip. The new case usesU+00A0:trim()drops it, the URL parser percent-encodes it into the path, sohttps://github.com/ada.png\u00a0resolves tohttps://github.com/ada.pngwith the trim andhttps://github.com/ada.png%C2%A0without it.After this change the file scores 93.88%, and the 3 remaining survivors are equivalent mutants that cannot be killed from tests — documented in the commit message and in #1783:
String(hostname || "")fallback string: every falsy hostname is rejected either way..some→.every:CNCF_HOST_SUFFIXESholds exactly one entry.if (!trimmed)→if (false):new URL("")throws and is caught into the samenull.Evidence, reproducible locally:
TZ=UTC npx stryker run --mutate "scripts/lib/profile-image.mjs", before 46/49 → after 46 killed of 49 with the three equivalent survivors;TZ=UTC node --test tests/profile-image.test.mjspasses 10/10;prettier --checkandeslint --max-warnings 0clean on the changed file.No overlap with the open held PRs: #1761 (
tests/validate-utils.test.mjs), #1765 (tests/svg-active-content.test.mjs), #1771 (tests/enduser-landscape.test.mjs), #1773 (tests/markdown-references.test.mjs,tests/npm-lifecycle-scripts.test.mjs), #1780 (tests/enduser-collector.test.mjs), #1782 (tests/mdx-escape.test.mjs), #1767 (scripts/lib/duplicate-check.mjsand four validators).Related Issue
Fixes #1783
Filed by quality agent (hold-gated mode). Human review required.
— hive: agent=quality backend=copilot model=claude-opus-5 copilot=1.0.88