Merge reviewed contributor PR #3213 into backlog batch

Source-PR: https://github.com/affaan-m/ECC/pull/3213
Source-Head: 1394cf6f0a

Local integration checkpoint; aggregate review and hosted acceptance pending.
This commit is contained in:
affaan-m
2026-09-27 23:36:51 -04:00
2 changed files with 75 additions and 11 deletions
+18 -11
View File
@@ -423,18 +423,22 @@ function currentClause(prefix) {
return prefix.slice(clauseStart + 1).trim();
}
function invokesContainerResult(prefix) {
function invokesContainerResult(prefix, options = {}) {
const clause = currentClause(prefix);
const pipelineStart = clause.lastIndexOf('|');
const pipelineCommand = clause.slice(pipelineStart + 1).trim();
const isForeachLoopHeader = Boolean(options.groupingExpression) &&
pipelineStart === -1 &&
/^foreach$/i.test(pipelineCommand);
return /(?:^|\s)(?:&|\.)\s*$/.test(clause) ||
/\.\s*(?:foreach|where)\s*$/i.test(clause) ||
/-(?:action|begin|command|end|expression|filter|initializationscript|parallel|process|scriptblock)(?:\s*:\s*)?$/i.test(clause) ||
/^(?:(?:[\w.-]+\\)?(?:foreach-object|where-object|foreach|where|invoke-command|start-job|measure-command)|%|\?)(?:\s|$)/i.test(pipelineCommand);
(!isForeachLoopHeader &&
/^(?:(?:[\w.-]+\\)?(?:foreach-object|where-object|foreach|where|invoke-command|start-job|measure-command)|%|\?)(?:\s|$)/i.test(pipelineCommand));
}
function invokesDynamicResult(prefix) {
return invokesContainerResult(prefix) ||
function invokesDynamicResult(prefix, options = {}) {
return invokesContainerResult(prefix, options) ||
/(?:^|\s)(?:iex|invoke-expression)\s*$/i.test(currentClause(prefix));
}
@@ -850,6 +854,9 @@ function extractExecutableContainers(input, options = {}) {
const withinDoubleQuote = quote === '"';
const prefix = context;
const invokesContainer = invokesContainerResult(prefix, {
groupingExpression: isGroupingExpression,
});
const invokedAfter = isInvokedAfterContainer(input, group.end);
const createsScriptBlock = /\[\s*(?:system\.management\.automation\.)?scriptblock\s*\]\s*::\s*create\s*$/i.test(
currentClause(prefix)
@@ -863,7 +870,7 @@ function extractExecutableContainers(input, options = {}) {
options: {
executeBareScriptBlocks: Boolean(options.executeBareScriptBlocks) ||
invokedAfter || executesNestedScriptBlocks ||
(!isScriptBlock && invokesContainerResult(prefix)),
(!isScriptBlock && invokesContainer),
},
});
} else {
@@ -883,7 +890,7 @@ function extractExecutableContainers(input, options = {}) {
}
let resolvedCommand = null;
if (!isScriptBlock) {
if (isSubexpression || invokesContainerResult(prefix)) {
if (isSubexpression || invokesContainer) {
resolvedCommand = staticOutputResult(group.body);
if (resolvedCommand === null && isSubexpression) {
const scalarReference = variableReference(group.body);
@@ -904,16 +911,16 @@ function extractExecutableContainers(input, options = {}) {
const executableBlockExpression = /\{|\[\s*(?:system\.management\.automation\.)?scriptblock\s*\]\s*::\s*create/i.test(
maskQuotedStrings(group.body)
);
if (!resolvedCommand && !isScriptBlock && invokesDynamicResult(prefix) && !executableBlockExpression) {
if (!resolvedCommand && !isScriptBlock && invokesDynamicResult(prefix, {
groupingExpression: isGroupingExpression,
}) && !executableBlockExpression) {
resolvedCommand = DYNAMIC_EXECUTION_MARKER;
}
if (resolvedCommand) {
for (let offset = 0; offset < resolvedCommand.length; offset += 1) {
masked[index + offset] = resolvedCommand[offset];
}
masked[index] = resolvedCommand;
if (!withinDoubleQuote) appendContext(resolvedCommand);
} else if (isScriptBlock) {
if (invokesContainerResult(prefix)) {
if (invokesContainer) {
context = prefix;
} else {
resetContext();
@@ -472,6 +472,56 @@ test('classifies invoked functions and filters across executable containers', ()
for (const command of commands) expectRules(command, [RULES.REMOVE_FORCE]);
});
test('distinguishes foreach statements from the pipeline alias', () => {
for (const command of [
"foreach ($r in 'aaaaaaaaaaaaa') { $r }",
"foreach ($r in 'aaaaaaaaaaaaaa') { $r }",
"foreach ($s in 'BTCUSDT','ETHUSDT','SOLUSDT') { $s }",
]) {
expectSafe(command);
}
expectRules(
"foreach ($r in 'aaaaaaaaaaaaaa') { Remove-Item -Force C:/tmp/demo }",
[RULES.REMOVE_FORCE]
);
expectRules('1 | foreach { Remove-Item -Force C:/tmp/demo }', [
RULES.REMOVE_FORCE,
]);
expectRules('1 | foreach ({ Remove-Item -Force C:/tmp/demo })', [
RULES.REMOVE_FORCE,
]);
});
test('foreach headers and nested bodies retain destructive scanning', () => {
for (const command of [
'foreach ($x in $(Remove-Item -Force C:/tmp/demo)) { $x }',
'foreach ($x in (Remove-Item -Force C:/tmp/demo)) { $x }',
'FoReAcH \n ($x in @("a")) { Remove-Item -Force C:/tmp/demo }',
'Get-Date; foreach ($x in "a") { foreach ($y in "b") { Remove-Item -Force C:/tmp/demo } }',
]) expectRules(command, [RULES.REMOVE_FORCE]);
for (const command of [
'FoReAcH \n ($x in @("a", "b")) { $x }',
'Get-Date; foreach ($x in "a") { $x }',
'foreach ($x in @({ Remove-Item -Force C:/tmp/demo })) { $x }',
'Write-Output "foreach ($x in Remove-Item -Force C:/tmp/demo)"',
]) expectSafe(command);
});
test('short dynamic groups preserve following command boundaries', () => {
for (const command of [
'iex ($a)\nRemove-Item -Force C:/tmp/demo',
'iex ($a) | Remove-Item -Force C:/tmp/demo',
'& ($a); iex ($b); Remove-Item -Force C:/tmp/demo',
]) expectRules(command, [RULES.DYNAMIC_EXECUTION, RULES.REMOVE_FORCE]);
});
test('resolved scalar output longer than its group preserves following commands', () => {
expectRules("$name='Remove-Item'; & $($name) -Force C:/tmp/demo; Clear-Content C:/tmp/demo", [
RULES.REMOVE_FORCE, RULES.CLEAR_CONTENT,
]);
});
test('classifies invoked static script-block variables but leaves assignments inert', () => {
expectSafe('$cleanup = { Remove-Item -Force C:/tmp/demo }');
expectRules('$cleanup = { Remove-Item -Force C:/tmp/demo }; & $cleanup', [
@@ -620,6 +670,13 @@ test('classifies static execution primitives', () => {
expectRules('& (Get-Command Remove-Item) -Force C:/tmp/demo', [
RULES.DYNAMIC_EXECUTION,
]);
for (const command of [
'iex ($a); Remove-Item -Force C:/tmp/demo',
'Invoke-Expression ($a); Remove-Item -Force C:/tmp/demo',
'& ($a); Remove-Item -Force C:/tmp/demo',
]) {
expectRules(command, [RULES.DYNAMIC_EXECUTION, RULES.REMOVE_FORCE]);
}
expectRules("iex ('Remove-'+'Item -Force C:/tmp/demo')", [
RULES.DYNAMIC_EXECUTION,
]);