candor-ts 0.8.12 → 0.8.14
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/lsp.mjs +1 -1
- package/package.json +1 -1
- package/query-core.mjs +9 -2
package/lsp.mjs
CHANGED
|
@@ -356,7 +356,7 @@ function runFix(a) {
|
|
|
356
356
|
lines.push(`or hoist higher (up to ${r.hoistHigher.slice(0, 4).join(", ")}): keeps the frontier pure too, threads through more signatures`);
|
|
357
357
|
}
|
|
358
358
|
} else {
|
|
359
|
-
lines.push("no clean hoist —
|
|
359
|
+
lines.push("no clean hoist — add a thin entry point ABOVE the layer and thread the value down (simplest), OR inject it as a function/closure (not a resolvable trait — candor charges the trait's impl back to the caller), OR relax the boundary");
|
|
360
360
|
}
|
|
361
361
|
lines.push(`policy alternative: ${r.policyAlternative}`);
|
|
362
362
|
transient.set(a.uri, [{
|
package/package.json
CHANGED
package/query-core.mjs
CHANGED
|
@@ -540,14 +540,21 @@ function computeRemedy(start, eff, layer, cg, rev, byName, policyParsed, scopeMa
|
|
|
540
540
|
// higher hoist options: allowed-layer transitive callers of the minimal frontier that also route the
|
|
541
541
|
// effect — hoisting higher keeps the frontier pure too, at the cost of threading through more signatures
|
|
542
542
|
// (FIX-SPEC: the trade-off, disclosed not hidden).
|
|
543
|
+
// The SANDWICHED-layer check (/code-review): a hoist is CLEAN only if no forbidden fn sits ABOVE the
|
|
544
|
+
// frontier. If a denied fn calls into a hoist target, hoisting the effect there leaves that caller
|
|
545
|
+
// violating. Detected in the same climb that gathers `hoistHigher` (the allowed ancestors).
|
|
543
546
|
const hoistHigher = new Set();
|
|
547
|
+
let sandwiched = false;
|
|
544
548
|
const hseen = new Set(hoistTo);
|
|
545
549
|
const hq = [...hoistTo];
|
|
546
550
|
while (hq.length) {
|
|
547
551
|
const cur = hq.shift();
|
|
548
552
|
for (const caller of rev.get(cur) ?? []) {
|
|
549
553
|
const ce = byName.get(caller);
|
|
550
|
-
if (ce
|
|
554
|
+
if (!ce || !(ce.inferred ?? []).includes(eff)) continue;
|
|
555
|
+
if (deniedLayer(caller, eff, policyParsed, scopeMatches) !== null) {
|
|
556
|
+
sandwiched = true;
|
|
557
|
+
} else if (!hseen.has(caller)) {
|
|
551
558
|
hseen.add(caller);
|
|
552
559
|
hoistHigher.add(caller);
|
|
553
560
|
hq.push(caller);
|
|
@@ -556,7 +563,7 @@ function computeRemedy(start, eff, layer, cg, rev, byName, policyParsed, scopeMa
|
|
|
556
563
|
}
|
|
557
564
|
return {
|
|
558
565
|
fn: start, effect: eff, layer,
|
|
559
|
-
cleanHoist: hoistTo.size > 0,
|
|
566
|
+
cleanHoist: hoistTo.size > 0 && !sandwiched,
|
|
560
567
|
site: [...sites].sort(),
|
|
561
568
|
deniedSpan: [...deniedSpan].sort(),
|
|
562
569
|
hoistTo: [...hoistTo].sort(),
|