mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-28 20:45:11 +02:00
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:
@@ -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,
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user