Skip to content

fix: legacy-security mode installs awf non-rootless, uses plain sudo for log parsing#48098

Open
pelikhan with Copilot wants to merge 6 commits into
mainfrom
copilot/legacy-security-enable-emits-sudo-e-awf
Open

fix: legacy-security mode installs awf non-rootless, uses plain sudo for log parsing#48098
pelikhan with Copilot wants to merge 6 commits into
mainfrom
copilot/legacy-security-enable-emits-sudo-e-awf

Conversation

Copilot AI commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

legacy-security: enable opts the agent job into sudo -E awf, but the install step unconditionally passed --rootless when NetworkIsolation=true (the default), placing the binary in ~/.local/bin — outside sudo's secure_path. Result: agent startup fails with sudo: awf: command not found. The print_firewall_logs.sh step had the same logic flaw.

Changes

  • pkg/workflow/copilot_engine_installation.gogenerateAWFInstallationStep: skip --rootless when agentConfig.LegacySecurity is true, so the binary lands in /usr/local/bin
  • pkg/workflow/engine_firewall_support.gogenerateFirewallLogParsingStep: skip --rootless when LegacySecurity is true; AWF already has full sudo access in this mode
  • .github/workflows/smoke-service-ports.lock.yml — regenerated (this workflow was already using legacy-security: enable and was silently broken)
  • Tests added for both functions covering the LegacySecurity=true + NetworkIsolation=true combination, plus an end-to-end compilation test

The condition in both places went from:

if agentConfig != nil && agentConfig.NetworkIsolation && !agentConfig.Disabled {

to:

if agentConfig != nil && agentConfig.NetworkIsolation && !agentConfig.Disabled && !agentConfig.LegacySecurity {

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 23.1 AIC · ⌖ 6.65 AIC · ⊞ 7.1K ·
Comment /souschef to run again


Generated by 👨‍🍳 PR Sous Chef · gpt54 · 8.98 AIC · ⌖ 8.12 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Copilot AI and others added 2 commits July 26, 2026 03:29
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…do for log parsing

When `legacy-security: enable` is set, the AWF execution step correctly uses
`sudo -E awf` (which requires the binary to be in sudo's secure_path, i.e.
/usr/local/bin). However, two other steps incorrectly added `--rootless`:

1. The `install_awf_binary.sh` step passed `--rootless` (installing to
   ~/.local/bin), making `sudo awf` fail with "command not found".
2. The `print_firewall_logs.sh` step passed `--rootless` (using non-interactive
   sudo with a non-sudo fallback), even though AWF had full sudo access.

Fix: In both `generateAWFInstallationStep` and `generateFirewallLogParsingStep`,
skip `--rootless` when `LegacySecurity` is true, even if `NetworkIsolation` is
also true (which is the default).

This also fixes the compiled `.github/workflows/smoke-service-ports.lock.yml`
which already used `legacy-security: enable`.

Closes #47804

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix agent startup failure due to legacy security mode fix: legacy-security mode installs awf non-rootless, uses plain sudo for log parsing Jul 26, 2026
Copilot AI requested a review from pelikhan July 26, 2026 03:44
@pelikhan
pelikhan marked this pull request as ready for review July 26, 2026 03:46
@pelikhan
pelikhan requested review from Copilot and lpcox and removed request for Copilot July 26, 2026 03:46
@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

@github-actions

github-actions Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The fix is correct and well-scoped. Both generateAWFInstallationStep and generateFirewallLogParsingStep now correctly skip --rootless when LegacySecurity is true, matching the documented behavior that sudo -E awf requires the binary in sudos secure_path. The reuse of getAgentConfig in engine_firewall_support.go is consistent with the existing pattern. Tests cover the unit cases and an end-to-end compilation check.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · sonnet46 · 17.4 AIC · ⌖ 5.14 AIC · ⊞ 5K

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs — one minor observation on the new code; no blocking issues.

📋 Key Themes & Highlights

Findings

  • Dead nil guard (engine_firewall_support.go:139): the agentCfg == nil branch is unreachable — see inline comment.

Positive Highlights

  • ✅ Root cause correctly identified and fixed in both affected callsites
  • ✅ Regression tests added for the LegacySecurity=true + NetworkIsolation=true combination in three different test files
  • ✅ End-to-end compilation test (TestLegacySecurityInstallNonRootless) verifies the lock file output end-to-end
  • smoke-service-ports.lock.yml regenerated — confirming the previously silently broken workflow is now correct
  • ✅ Comment in copilot_engine_installation.go clearly documents the sudo secure_path constraint

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · sonnet46 · 26.1 AIC · ⌖ 4.67 AIC · ⊞ 6.7K
Comment /matt to run again

scriptArg := ""
if isAWFNetworkIsolationEnabled(workflowData) {
agentCfg := getAgentConfig(workflowData)
if isAWFNetworkIsolationEnabled(workflowData) && (agentCfg == nil || !agentCfg.LegacySecurity) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[/diagnosing-bugs] The agentCfg == nil guard is dead code — isAWFNetworkIsolationEnabled already returns false when agentCfg is nil (see firewall.go lines 154-157), so the outer condition is already false before the nil check is evaluated.

💡 Suggested simplification
// agentCfg is guaranteed non-nil when isAWFNetworkIsolationEnabled returns true
agentCfg := getAgentConfig(workflowData)
if isAWFNetworkIsolationEnabled(workflowData) && !agentCfg.LegacySecurity {
    scriptArg = " --rootless"
}

Removing the unreachable branch makes the invariant explicit and prevents future readers from thinking nil is a valid outcome here.

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (>100 new lines in pkg/workflow/) but does not have a linked Architecture Decision Record (ADR).

Draft ADR committed: docs/adr/48098-legacy-security-awf-install-non-rootless.md — review and complete it before merging.

This PR cannot merge until an ADR is linked in the PR body.

What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff
  2. Complete the missing sections — add context the AI could not infer, refine the decision rationale, and list real alternatives you considered
  3. Commit the finalized ADR to docs/adr/ on your branch
  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-48098: Legacy-Security Mode Installs AWF to Non-Rootless Path

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

Why ADRs Matter

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 0042-use-postgresql.md for PR #42).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · sonnet46 · 50.8 AIC · ⌖ 10 AIC · ⊞ 8.5K ·
Comment /review to run again

Copilot AI review requested due to automatic review settings July 26, 2026 04:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because there is no eligible user to bill. To allow Copilot reviews on bot-authored pull requests, enable direct organization billing in your organization's Copilot settings.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 70/100 — Acceptable

Analyzed 6 test(s): 6 design, 0 implementation, 0 violation(s).

📊 Metrics (6 tests)
Metric Value
Analyzed 6 (Go: 6, JS: 0)
✅ Design 6 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 0 (0%)
Duplicate clusters 0
Inflation N/A (integration tests)
🚨 Violations 0
Test File Classification Coverage
TestGenerateFirewallLogParsingStepFixesFirewallPermissions engine_firewall_support_test.go:273 design_test, behavioral_contract Firewall logs preservation
TestGenerateFirewallLogParsingStepNetworkIsolationOmitsSudo engine_firewall_support_test.go:293 design_test, behavioral_contract Network isolation + rootless
TestGenerateFirewallLogParsingStepWithNetworkIsolationFalse engine_firewall_support_test.go:312 design_test, behavioral_contract Explicit false boundary
TestGenerateFirewallLogParsingStepLegacySecurityOmitsRootless engine_firewall_support_test.go:334 design_test, behavioral_contract Core fix: legacy-security mode
TestCustomAWFConfiguration (legacy-security subtest) sandbox_custom_agent_test.go:103 design_test, behavioral_contract Core fix: AWF install path
TestLegacySecurityInstallNonRootless sandbox_network_isolation_rootless_test.go:146 design_test, behavioral_contract Core fix: non-rootless install
⚠️ Quality Concern: No Error/Edge Case Testing

All 6 tests verify happy-path behavior only:

  • ✅ Firewall step generation with valid configs
  • ✅ AWF installation step generation with valid configs
  • ✅ Compilation succeeds with legacy-security mode

Missing:

  • ❌ Compilation error handling
  • ❌ Invalid configuration validation
  • ❌ Missing required fields
  • ❌ Error message assertions

Impact: While these tests provide excellent coverage of the intended behavior (the PR's core feature — legacy-security mode handling), they don't guard against misuse (invalid configs, missing setup steps). Consider adding error-case tests in a follow-up to strengthen robustness.

Verdict

passed. 0% implementation tests (threshold: 30%). All tests are design-level behavioral contracts covering the core feature (legacy-security mode installation and log-parsing). No coding violations detected.

Note: Test inflation ratio (19.5:1) is legitimately high for integration tests that write temporary files and compile workflows, but not counted against the score.

🧪 Test quality analysis by Test Quality Sentinel · haiku45 · 27.1 AIC · ⌖ 10.3 AIC · ⊞ 8.1K ·
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 70/100. 0% implementation tests (threshold: 30%). All tests are design-level behavioral contracts covering the core feature (legacy-security mode installation and log-parsing).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: legacy-security non-rootless install fix

The core fix is correct — LegacySecurity mode must install AWF to /usr/local/bin (non-rootless) so sudo -E awf can find it, and the firewall log parsing step must match. Two issues flagged; neither is a blocker but one warrants attention.

Findings

Medium — Dead nil guard in engine_firewall_support.go:139

The agentCfg == nil branch in (agentCfg == nil || !agentCfg.LegacySecurity) is unreachable. isAWFNetworkIsolationEnabled already returns false when agentConfig is nil, so the outer && short-circuits before the inner || is evaluated. This is not a correctness bug today, but it misleads readers and could silently regress if isAWFNetworkIsolationEnabled is refactored. See inline comment.

Low — TestLegacySecurityInstallNonRootless relies on implicit network-isolation defaults

The test frontmatter does not declare network-isolation: true explicitly, meaning the test only exercises the LegacySecurity exception if network isolation happens to be the default. See inline comment.

Out-of-diff observation (no comment posted)

Other callers of isAWFNetworkIsolationEnabled — notably difc-proxy host selection in awf_helpers.go:738 and compiler_difc_proxy.go:266/585 — do not have a LegacySecurity exception. For legacy-security+network-isolation, AWF runs on the host network (non-rootless), so the difc-proxy should route via host.docker.internal rather than awmg-cli-proxy. This PR does not touch those files; worth a follow-up issue.

🔎 Code quality review by PR Code Quality Reviewer · sonnet46 · 63.7 AIC · ⌖ 4.87 AIC · ⊞ 5.7K
Comment /review to run again

scriptArg := ""
if isAWFNetworkIsolationEnabled(workflowData) {
agentCfg := getAgentConfig(workflowData)
if isAWFNetworkIsolationEnabled(workflowData) && (agentCfg == nil || !agentCfg.LegacySecurity) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Dead nil guard creates misleading logic: the agentCfg == nil branch in the new condition is unreachable, since isAWFNetworkIsolationEnabled already returns false when agentConfig == nil. This is not a crash, but it obscures intent and could mask a future regression if isAWFNetworkIsolationEnabled is refactored.

💡 Explanation and suggested fix

isAWFNetworkIsolationEnabled (firewall.go) checks agentConfig == nil first and returns false immediately, so the outer && already guarantees agentCfg is non-nil when the inner condition is reached. Simplify to:

agentCfg := getAgentConfig(workflowData)
if isAWFNetworkIsolationEnabled(workflowData) && !agentCfg.LegacySecurity {
    scriptArg = " --rootless"
}

If you prefer to keep the nil guard for defensive style, add a comment explaining the invariant so future readers don’t get confused.

strict: false
network:
allowed:
- github.com

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

TestLegacySecurityInstallNonRootless relies on implicit network-isolation defaults: the test frontmatter omits network-isolation: true and id: awf with an explicit NetworkIsolation field, so the --rootless suppression is exercised only if network isolation happens to be enabled by default. If the default changes, the test silently stops covering the LegacySecurity exception.

💡 Suggested improvement

Make the network-isolation dependency explicit in the test frontmatter:

sandbox:
  agent:
    id: awf
    network-isolation: true   # explicit — required for --rootless suppression test
    legacy-security: enable

This documents the invariant under test and prevents silent test staleness if the default changes.

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage — §30192186368

Field Value
Category bug
Risk 🟡 medium
Score 69 / 100 (impact 38 + urgency 15 + quality 16)
Action ⚡ fast_track

Why: Fixes #47804legacy-security: enable silently broken: AWF installed to ~/.local/bin (outside sudo secure_path), causing agent startup failure. Bot-approved, tests added. CI status unknown.

Next: Verify CI passes, then fast-track merge.

Generated by 🔧 PR Triage Agent · sonnet46 · 48.8 AIC · ⌖ 8.75 AIC · ⊞ 5.7K ·

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the remaining review feedback, and confirm whether the ADR requirement has been satisfied before handing back to maintainers.

Unresolved review feedback / follow-up:

Please reply with the specific remaining blocker, if any, after checking the current branch state.

Generated by 👨🍳 PR Sous Chef · gpt54 ·
Comment /souschef to run again

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 21.7 AIC · ⌖ 8.89 AIC · ⊞ 7.1K ·
Comment /souschef to run again

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot July 26, 2026 09:48
@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug
Risk 🟡 Medium
Priority Score 58/100
Score Breakdown Impact: 30, Urgency: 18, Quality: 10
Action fast_track — CI passing (18✅ 5⏳), legacy-security startup fix

Legacy-security mode agent startup fix (sudo path issue). CI mostly passing. ADR included.

Generated by 🔧 PR Triage Agent · sonnet46 · 26.8 AIC · ⌖ 4.99 AIC · ⊞ 5.7K ·

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, review the current branch state, and confirm whether any reviewer feedback or ADR/body follow-up is still blocking maintainer merge review.

Recent context:

  • Prior sous-chef follow-up asked for remaining review feedback and ADR confirmation.
  • Latest visible activity is triage-only, so please give maintainers a fresh blocker summary or explicitly confirm the PR is ready.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 23.1 AIC · ⌖ 6.65 AIC · ⊞ 7.1K ·
Comment /souschef to run again

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, review the current branch state, and confirm whether any reviewer feedback or ADR/body follow-up is still blocking maintainer merge review.

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 8.98 AIC · ⌖ 8.12 AIC · ⊞ 7.1K ·
Comment /souschef to run again

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage — §30214847023

Field Value
Category bug
Risk 🟡 Medium
Priority 62/100
Breakdown Impact 30 · Urgency 20 · Quality 12
Action fast_track

Rationale: Legacy-security mode: AWF installed non-rootless, plain sudo for log parsing. 164 additions in 7 files including Go engine/firewall code, tests, ADR, and lock.yml. CI passing (2 runs). AI reviewers approved. Ready for expedited human review.

Generated by 🔧 PR Triage Agent · sonnet46 · 34.1 AIC · ⌖ 7.94 AIC · ⊞ 5.7K ·

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the skill, review the current branch state, address any still-open reviewer feedback and failed checks, and reply with whether this PR is ready for maintainer review.

Run: https://github.com/github/gh-aw/actions/runs/30215321924

Generated by 👨‍🍳 PR Sous Chef · gpt54 · 16.9 AIC · ⌖ 7.13 AIC · ⊞ 5.6K ·
Comment /souschef to run again

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

legacy-security: enable emits sudo -E awf but installs awf with --rootless, so the agent fails at startup with sudo: awf: command not found

4 participants