Skip to content

fix: allow piped bash commands through Copilot SDK permission checker#37369

Merged
pelikhan merged 6 commits into
mainfrom
copilot/aw-fix-geo-optimizer-audit
Jun 6, 2026
Merged

fix: allow piped bash commands through Copilot SDK permission checker#37369
pelikhan merged 6 commits into
mainfrom
copilot/aw-fix-geo-optimizer-audit

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Jun 6, 2026

When the Copilot SDK sends a permission request for a piped/chained command (e.g. ls /tmp && cat file.json || echo "not found"), it sends commands: [] — an empty identifiers array. The original checker only tested commandIdentifiers.includes(rule), so every piped command was unconditionally denied, hitting the 5/5 denial threshold and killing the GEO Optimizer agent session.

Core fix — copilot_sdk_driver.cjs

Added a fallback path in isAllowed for the "shell" case: when commandIdentifiers is empty, parse fullCommandText via the new pipeline parser and require all pipeline stages to be individually permitted (every(), not some() — a disallowed stage anywhere in the chain still denies the whole command).

// Before: commandIdentifiers is [] for piped commands → always false → deny
commandIdentifiers.some(id => shellRules.includes(id))

// After: parse fullCommandText, check every stage
const names = extractCommandNamesFromPipeline(fullCommand);
if (names.length > 1) return names.every(isIdentifierAllowedByShellRules);

New parser — bash_command_parser.cjs

Dedicated state-machine parser that splits on &&, ||, |, ; while correctly handling single/double quotes and $(...) subshells. Exports:

  • splitOnPipelineOperators(text) — returns pipeline segments
  • extractCommandName(segment) — skips env-var assignments, redirections, shell keywords (then, fi, etc.), and negation (!)
  • extractCommandNamesFromPipeline(text) — deduplicated end-to-end

Runtime scope

This PR now targets the JavaScript runtime only. The previously added Python driver/runtime artifacts were removed because this repository does not maintain a Python runtime for this path.

Tests

File Tests
bash_command_parser.test.cjs 90 unit tests — expanded extensive vector suite for all three exports, GEO optimizer failing commands, and additional AW-style parser edge cases
fuzz_bash_command_parser_harness.test.cjs 177 property/fuzz tests — exhaustive operator×command matrix, quote-protection and no-throw invariants
copilot_sdk_driver.test.cjs +9 integration tests — piped command permission scenarios

Copilot AI linked an issue Jun 6, 2026 that may be closed by this pull request
Copilot AI and others added 2 commits June 6, 2026 18:05
- Add bash_command_parser.cjs: dedicated parser splitting on &&, ||, |, ; with quote and $() awareness
- Update copilot_sdk_driver.cjs: fallback for empty commandIdentifiers uses pipeline parser so piped commands (e.g. ls && cat || echo) are allowed instead of denied
- Add bash_command_parser.test.cjs: 55 unit tests
- Add fuzz_bash_command_parser_harness.cjs: property-based fuzz harness
- Add fuzz_bash_command_parser_harness.test.cjs: 177 property/fuzz tests
- Update copilot_sdk_driver.test.cjs: 9 new integration tests for piped permission handling
- Add bash_tool_compiler.py: Python implementation of the same bash pipeline parser and permission checker
- Add test_bash_tool_compiler.py: 87 Python unittest+pytest tests

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- copilot_sdk_driver.cjs: expand JSDoc on isIdentifierAllowedByShellRules to detail all three rule formats (wildcard :*, single-word, full-command)
- bash_command_parser.cjs: expand SHELL_KEYWORDS comment explaining why keywords must be excluded from permission checking
- fuzz_bash_command_parser_harness.test.cjs: extract nested flatMap/filter/map chain into generateCommandPairs helper
- bash_tool_compiler.py: add note explaining why linear scan in is_identifier_allowed_by_shell_rules is acceptable

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix GEO Optimizer Daily Audit workflow failure fix: allow piped bash commands through Copilot SDK permission checker Jun 6, 2026
Copilot AI requested a review from pelikhan June 6, 2026 18:09
@pelikhan pelikhan marked this pull request as ready for review June 6, 2026 18:29
Copilot AI review requested due to automatic review settings June 6, 2026 18:29
@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Jun 6, 2026

@copilot add extensive unit test suite for parser. Mine existing AW for patterns.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes Copilot SDK permission-checking for chained/piped shell commands when the SDK provides an empty commands: [] identifiers list, by falling back to parsing fullCommandText and requiring every pipeline stage’s command name to be permitted.

Changes:

  • Added a bash pipeline parser (splitOnPipelineOperators, extractCommandName, extractCommandNamesFromPipeline) and used it as a fallback in the Node Copilot SDK permission handler.
  • Added comprehensive JS unit + fuzz/property tests covering pipeline splitting and command-name extraction.
  • Added a Python port of the parser and permission helpers with a matching test suite.
Show a summary per file
File Description
actions/setup/js/copilot_sdk_driver.cjs Adds pipeline-aware fallback permission logic for shell requests with missing SDK identifiers.
actions/setup/js/bash_command_parser.cjs Introduces state-machine parsing for pipeline operators and command-name extraction.
actions/setup/js/copilot_sdk_driver.test.cjs Adds integration tests for piped/chained command permission scenarios.
actions/setup/js/bash_command_parser.test.cjs Adds unit tests for the new parser utilities.
actions/setup/js/fuzz_bash_command_parser_harness.cjs Provides a structured harness for fuzz/property invariants (no-throw, quoted operators, safe defaults).
actions/setup/js/fuzz_bash_command_parser_harness.test.cjs Adds vitest-based fuzz/property tests over operators/quoting matrices and invariants.
.github/drivers/bash_tool_compiler.py Adds Python implementation of the parser + permission helpers for Python-based drivers.
.github/drivers/test_bash_tool_compiler.py Adds a comprehensive Python unittest suite for the new Python parser/helpers.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 8/8 changed files
  • Comments generated: 6

Comment on lines +87 to +106
// ── $(...) subshell: skip balanced parentheses as a unit ──
if (ch === "$" && i + 1 < len && commandText[i + 1] === "(") {
current += ch;
i++;
let depth = 0;
while (i < len) {
const sc = commandText[i];
if (sc === "(") depth++;
else if (sc === ")") {
depth--;
current += sc;
i++;
if (depth === 0) break;
continue;
}
current += sc;
i++;
}
continue;
}
Comment on lines +188 to +189
// Skip leading env-var assignments: IDENTIFIER=anything (repeat)
const envAssignRe = /^[A-Za-z_][A-Za-z0-9_]*=\S*\s*/;
Comment on lines +82 to +88
it("handles nested $() subshells", () => {
const segments = splitOnPipelineOperators("echo $(echo $(ls && pwd)) && date");
expect(segments).toHaveLength(2);
expect(segments[0]).toContain("echo $(echo $(ls && pwd))");
expect(segments[1]).toContain("date");
});

Comment thread .github/drivers/bash_tool_compiler.py Outdated
Comment on lines +113 to +131
# ── $(...) subshell: balanced parentheses ─────────────────────────
if ch == "$" and i + 1 < n and command_text[i + 1] == "(":
current.append(ch)
i += 1
depth = 0
while i < n:
sc = command_text[i]
if sc == "(":
depth += 1
elif sc == ")":
depth -= 1
current.append(sc)
i += 1
if depth == 0:
break
continue
current.append(sc)
i += 1
continue
Comment thread .github/drivers/bash_tool_compiler.py Outdated
Comment on lines +38 to +39
# Regex to detect leading env-var assignment: WORD=anything
_ENV_ASSIGN_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=\S*")
Comment thread .github/drivers/bash_tool_compiler.py Outdated
Comment on lines +359 to +362
names = extract_command_names_from_pipeline(command_text)
if not names:
return False
return all(is_identifier_allowed_by_shell_rules(name, shell_rules) for name in names)
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 6, 2026

@copilot add extensive unit test suite for parser. Mine existing AW for patterns.

Added an extensive parser unit test vector suite in actions/setup/js/bash_command_parser.test.cjs (table-driven coverage for split, command extraction, and pipeline extraction cases) in commit cb2b14b.

@pelikhan
Copy link
Copy Markdown
Collaborator

pelikhan commented Jun 6, 2026

@copilot merge main and recompile

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jun 6, 2026

@copilot merge main and recompile

Done in 8a7249b: merged origin/main into this branch, resolved the merge conflict in actions/setup/js/copilot_sdk_driver.cjs, and recompiled actions (make actions-build).

Comment thread .github/drivers/bash_tool_compiler.py Outdated
@@ -0,0 +1,362 @@
#!/usr/bin/env python3
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot delete this file. We are not maintaimimg a python runtime

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 424abea — removed .github/drivers/bash_tool_compiler.py and the related .github/drivers/test_bash_tool_compiler.py file.

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI requested a review from pelikhan June 6, 2026 19:25
@pelikhan pelikhan merged commit 071e5ca into main Jun 6, 2026
@pelikhan pelikhan deleted the copilot/aw-fix-geo-optimizer-audit branch June 6, 2026 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[aw] GEO Optimizer Daily Audit failed

3 participants