ktg-plugin-marketplace/plugins/linkedin-studio/docs/remediation/review.md
Kjell Tore Guttormsen 431a893f7c fix(linkedin-studio): S13 — close S12 WARN ($-scalar + false-green test) + $-safety lint guard
Closes the 2 grep/Read-verified findings from the S12 cold full-brief re-review
(docs/remediation/review.md, WARN 0/1/1/0, 0 dropped) and closes the $-injection
CLASS — not the line — across the whole state-updater.mjs mutation surface.

See docs/remediation/review.md (S13 ALLOW, 0/0/0/0) for the full closure record:
replaceField -> replacement function; the 3 additive-insert sites -> functions
(m === $1, behavior-preserving); a scalar assert.match pins last_post_topic; and a
behavioral, coverage-complete, self-testing Section 12 guard (check-replace-safety.mjs)
that is mutation-proven. Docs three-doc + residuals updated. test-runner.sh 71/0/0,
node --test 98/98.
2026-05-30 19:12:45 +02:00

9.6 KiB
Raw Blame History

type review_version task slug project_dir brief_path scope_sha_start scope_sha_end reviewed_files_count verdict mode effort profile findings
trekreview 1.0 Remediate linkedin-studio from the baseline audit — correctness, honesty, generalization, and the highest-leverage 2026 coverage gaps (full Phase 03 roadmap, phased) remediation docs/remediation/ docs/remediation/brief.md c5b4c58f4f 36f79dd702 50 ALLOW default high premium

Review — linkedin-studio audit-remediation (S13 re-review: $-class closure + scalar-test fix)

Executive Summary

Verdict: ALLOW — 0 BLOCKER, 0 MAJOR, 0 MINOR, 0 SUGGESTION.

This is the S13 re-review — the seventh full-brief sweep (c5b4c58..36f79dd + the uncommitted S13 working-tree delta), run COLD and high-effort. S13 was commissioned to close the two findings the S12 re-review left open (verdict WARN, 0/1/1/0): the MAJOR MISSING_TEST (the S12 $-bearing test asserted the Recent Posts section but never the last_post_topic scalar, so the corruption shipped green) and the MINOR MISSING_ERROR_HANDLING (the last_post_topic scalar was still $-unsafe because replaceField used a replacement string for untrusted content). Two independent reviewers (brief-conformance, code-correctness) ran without cross-feeding; the coordinator applied bounded dedup + the HubSpot Judge filters + verdict (high-effort → Cloudflare reasonableness filter skipped; the operator weighs borderline findings). Both reviewers returned empty finding sets.

Both S12 findings are CLOSED at the reviewed (uncommitted) state:

  • replaceField (hooks/scripts/state-updater.mjs:14-24) now uses a replacement function (() => \${field}: ${value}`), so the untrusted last_post_topicat the:64-equivalent call site is inserted verbatim — no $&/$1/`` $ `` expansion. The MINOR is closed.
  • The existing $-bearing test (hooks/scripts/__tests__/state-updater.test.mjs) now carries assert.match(result.content, /^last_post_topic: "\$100 budget — \$& and \$1 rule"$/m, …), distinct from the section-entry includes() it already had. This assertion fails on the old string-replaceField and passes on the function form (orchestrator-verified by reverting the fix: the test went 40 pass / 1 fail). The false-green MAJOR is closed.

The class — not just the line — is closed. The recurring S9→S12 lesson is "close the class, not the line"; the class here is "untrusted user content reaching ANY String.replace replacement string". Beyond the replaceField scalar, S13 also converted the three remaining additive-insert sites (recordFirstHourPlan :246-equiv; recordOutreachContact :305/:308-equiv) from a string replacement carrying an intentional $1 backref + interpolated date to a replacement function ((m) => \${m}\n…`). The code-correctness reviewer verified rigorously that this is **behavior-preserving**: each regex's capture group spans the *entire* match (the only chars outside the group are the zero-width ^/$anchors), so the full matchmis character-identical to the old$1. After S13, **every .replace()instate-updater.mjsuses a replacement function or a$`-free literal** — the class is closed by construction, not by per-line patch.

A structural guard replaces the per-line proof. New scripts/check-replace-safety.mjs (wired as test-runner.sh Section 12) proves the property behaviorally: it drives every exported mutator with an adversarial payload of every special replacement token ($&, $`, $', $$, $n) in every free-text and date field and asserts the payload survives verbatim. Two structural backstops run on every invocation — coverage-completeness (a newly-exported mutator without $-coverage fails the guard) and a non-vacuity self-test (a naive string-replace MUST corrupt the payload and a function MUST preserve it, else a PASS is meaningless), mirroring the Section 8/10/11 self-tests. The orchestrator mutation-proved it end-to-end: reverting replaceField to a string makes the guard exit 1 with two findings; restoring it returns exit 0.

No Phase-03 Success Criterion regressed. The brief-conformance reviewer traced each S13 clause to delivered code and confirmed the counts (19 agents / 27 commands / 25 references / 6 skills), the version (4.0.0), the single-source algorithm-signal, the model-consistency guard, and the render-chain-propagation guard all still hold; S13 touched no command/agent/reference file. The two Non-Goals the brief amendment re-opens (the command invocation surface for S14; saves manual-entry for S16) trace to explicit operator decisions in the brief amendment, and S13 itself did not touch either surface — no SCOPE_CREEP_BUILT.

Push decision: ALLOW. The two S12 findings are closed, the class is closed structurally, the lint is non-vacuous and mutation-proven, all suites are green (scripts/test-runner.sh → 71/0/0; node --test → 98/98), and no SC regressed. The ORIGINAL remediation brief now closes clean. Per feedback_trekreview_always_last + Handover 6, this review is the gate; with ALLOW, S13 may push.

Coverage

Scope SHA range: c5b4c58 (= origin/main, parent of remediation Steg 1) → 36f79dd (HEAD, the S12 commit) plus the uncommitted S13 working-tree delta (annotated [uncommitted] — a brief-level contract; the brief's Assumptions allow uncommitted review). The committed range (47 files) was already deep-reviewed and cleared at S12 except the 2 WARN findings; the active S13 delta is the 9 working-tree files below. No silent skips.

Treatment Count Notes
deep-review (hooks/** + the new guard) 4 state-updater.mjs, state-updater.test.mjs [uncommitted]; scripts/check-replace-safety.mjs [uncommitted, new]; scripts/test-runner.sh [uncommitted]
summary-only 46 the committed c5b4c58..36f79dd range (already cleared at S12) + the S13 doc edits CLAUDE.md/README.md/docs/integration-test-guide.md [uncommitted] + docs/remediation/{brief.md (amendment), finish-plan.md} [uncommitted]
skip 0 no lockfiles / svg / generated / dist

Cross-cutting execution criteria (run by orchestrator): scripts/test-runner.sh → 71 passed / 0 failed / 0 warnings, exit 0 (was 70; +1 — Section 12 $-safety guard). node --test hooks/scripts/__tests__/*.test.mjs → 98 tests, 98 pass, 0 fail (the S13 scalar assertion was added to an existing test, not a new test). node scripts/check-replace-safety.mjs → exit 0 (8 adversarial cases / 5 mutators, coverage-complete, self-test non-vacuous); mutation-proven (reverting the fix → exit 1).

S12 findings — both confirmed CLOSED: replaceField (state-updater.mjs:14-24) → replacement function; the $-bearing test now pins the last_post_topic scalar (state-updater.test.mjs); the three remaining additive-insert string-replacements (:246/:305/:308) → functions; the class is closed and guarded structurally (Section 12).

Findings

None. Both independent reviewers returned empty finding sets; the coordinator's bounded passes (dedup, HubSpot Judge, verdict) on an empty set yield ALLOW.

Remediation Summary

Gate: ALLOW. The S12 WARN is fully resolved: the replaceField scalar $-corruption (MINOR) and the false-green test (MAJOR) are both closed; the $-injection class is closed across the whole state-updater.mjs mutation surface; and a behavioral, coverage-complete, self-testing Section-12 lint guards it structurally against future regressions. All suites green; no Phase-03 SC regressed; the two re-opened Non-Goals trace to explicit operator decisions and S13 stayed in its lane.

Two non-blocking observations, recorded by both reviewers, neither rising to a catalogue finding:

  1. The S13 dead binding both reviewers named (check-replace-safety.mjs HERE + its now-unused node:url/node:path imports) was removed during this review pass; the guard remains green (exit 0) after removal.
  2. The behavioral guard catches a new unguarded exported mutator (coverage backstop) but not a new unsafe String.replace added inside an existing battery-covered mutator on a field the battery does not fuzz. This is a documented, deliberate limit of the behavioral proxy (vs a per-.replace() AST enumeration) and is moot today — every .replace() in state-updater.mjs is already a function. Recorded as a known boundary, not a defect; closing it further is out of S13 scope (no real $-unsafe site exists to catch).

The two adjacent machine-value .replace() sites the correctness reviewer probed — session-start.mjs:396 (${actualWeek}, a computed ISO week) and week-rollover.mjs (computed week / literal int) — carry no untrusted content and are therefore not members of the defect class, consistent with how the S12 review itself classified the date/integer replaceField call sites. No finding.

Per Handover 6, this review.md is consumable by /trekplan --brief docs/remediation/review.md; the trailing JSON block is the machine contract for that handover. With an ALLOW verdict and no BLOCKER/MAJOR findings, no follow-up remediation plan is required — the ORIGINAL brief is closed clean and the finish-plan continues at S14.

{
  "verdict": "ALLOW",
  "scope": { "sha_start": "c5b4c58f4f390aca83c8937880c5fd0bcc983e44", "sha_end": "36f79dd702b9315a0cd9100c3a8dd6dd81b3797f", "reviewed_files_count": 50, "uncommitted_delta": true },
  "counts": { "BLOCKER": 0, "MAJOR": 0, "MINOR": 0, "SUGGESTION": 0 },
  "findings": [],
  "dropped_findings": []
}