fix(javascript): preserve unknown module-alias provenance

Replace the implicit local-provenance fallback with explicit locally constructed values. Keep opaque results, parameters and caught values unknown; follow assignment and sequence results while retaining exact module aliases. Clarify that alias provenance does not represent transitive data taint.

Validation: 26 focused provenance/module-link tests, 135 semantic and graph-builder tests, six application module-relationship boundary tests, and the fast typecheck/lint gate.
This commit is contained in:
morluto
2026-10-13 16:08:41 +08:00
parent b955b825fc
commit c7a890d129
3 changed files with 130 additions and 2 deletions
@@ -817,6 +817,13 @@ const provenanceForBindingValue = (
binding: JavaScriptSemanticBindingState,
context: EvaluationContext,
): JavaScriptBindingProvenance => {
if (binding.kind === "parameter" || binding.kind === "catch")
return semanticUnresolvedProvenance(
"unknown",
binding.kind === "parameter"
? "Parameter provenance depends on caller arguments."
: "Catch binding provenance depends on the thrown value.",
);
if (binding.directOrigins.length > 0)
return semanticOriginsProvenance(binding.directOrigins);
if (binding.initializers.length === 0) return semanticLocalProvenance();
@@ -890,6 +897,14 @@ const provenanceForExpression = (
const required = semanticRequireOrigin(node, context.state);
if (required !== undefined)
return semanticOriginsProvenance([required.origin]);
if (t.isSequenceExpression(node)) {
const finalExpression = node.expressions.at(-1);
return finalExpression === undefined
? semanticUnresolvedProvenance("unknown", "Empty sequence expression.")
: provenanceForExpression(finalExpression, nestedContext(context));
}
if (t.isAssignmentExpression(node) && node.operator === "=")
return provenanceForExpression(node.right, nestedContext(context));
if (t.isIdentifier(node)) {
const binding = resolveSemanticBindingState(context.state, node, node.name);
return binding === undefined
@@ -961,9 +976,25 @@ const provenanceForExpression = (
"Conditional provenance is unresolved.",
);
}
return semanticLocalProvenance();
if (isLocallyCreatedValue(node)) return semanticLocalProvenance();
return semanticUnresolvedProvenance(
"unknown",
`Unsupported ${node.type} provenance.`,
);
};
const isLocallyCreatedValue = (node: t.Node): boolean =>
readExactJavaScriptLiteral(node).found ||
t.isObjectExpression(node) ||
t.isArrayExpression(node) ||
t.isFunctionExpression(node) ||
t.isArrowFunctionExpression(node) ||
t.isClassExpression(node) ||
t.isTemplateLiteral(node) ||
t.isUnaryExpression(node) ||
t.isBinaryExpression(node) ||
t.isUpdateExpression(node);
const mergeValues = (
values: readonly JavaScriptSemanticValue[],
): JavaScriptSemanticValue => {