microsoft / microsoft/hve-core

#2623 feat(workflows): enforce CQ-6 for caller-controlled inputs

Open
#2,625 0 comments 0 reactions 0 assignees View on GitHub
agentic-workflows
Dominant language
Python
Stars
1.5k
Forks
301
Avg merge
3d 3h
Merged PRs (30d)
92

Description

> [!CAUTION]
> Protected files were modified in this change.
> This pull request is in `request_review` mode and requires explicit human scrutiny before merge.
>
> Protected files: `README.md`, `.github/workflows/README.md`, `.github/workflows/dependency-pinning-scan.yml`, `.github/workflows/fuzz-tests.yml`, `.github/workflows/gitleaks-scan.yml`, `.github/workflows/node-tests.yml`, `.github/workflows/pytest-tests.yml`, `.github/workflows/python-lint.yml`, `.github/workflows/skill-validation.yml`

Closes \#2623

## Summary

Implements enforceable CQ-6 rule preventing caller-controlled `string`-type workflow inputs from being interpolated directly into `run:` shell blocks, and migrates all confirmed command-bearing contexts to the established `INPUT_*` environment-variable pattern.

## Changes

### Detection — `scripts/security/Test-DangerousWorkflow.ps1`

- Promoted `Get-WorkflowCallInputTypes` and `Test-IsDirectInputInterpolation` from incorrectly-nested (inside `Test-IsUntrustedInjectionExpression`) to top-level script functions so they are callable by `Invoke-DangerousWorkflowCheck`
- `Get-WorkflowCallInputTypes` parses `on.workflow_call.inputs` and returns a name→type map
- `Test-IsDirectInputInterpolation` returns `$true` for `string` (or unclassified) inputs only; `boolean` and `number` are exempt (GitHub enforces their type constraints)
- Added `dangerous-workflow/direct-input-interpolation` SARIF rule alongside the existing `template-injection` rule
- Detection fires per-expression inside `run:` and `script:` blocks, reports actionable file + step context

### Tests — `scripts/tests/security/Test-DangerousWorkflow.Tests.ps1`

Five new Pester fixtures:
- `flags direct string input interpolation in run blocks (CQ-6)` — confirms violation reported
- `does not flag direct boolean input interpolation as a safe exception` — no violation
- `does not flag direct number input interpolation as a safe exception` — no violation
- `does not flag string inputs already routed through env: mappings` — no violation
- `flags string input used in bash run block` — confirms bash shell violation reported

### Workflow migrations

Seven reusable workflows migrated from direct `${{ inputs. }}` in `run:` blocks to `env:` isolation:

| Workflow | Input | Pattern |
|---|---|---|
| `fuzz-tests.yml` | `working-directory` (string) | Job-level `INPUT_WORKING_DIRECTORY` |
| `python-lint.yml` | `working-directory` (string) | Job-level `INPUT_WORKING_DIRECTORY` |
| `pytest-tests.yml` | `working-directory` (string) | Job-level `INPUT_WORKING_DIRECTORY` |
| `skill-validation.yml` | `base-branch` (string) | Step-level `INPUT_BASE_BRANCH` |
| `node-tests.yml` | `working-directory` (string) | Job-level `INPUT_WORKING_DIRECTORY` |
| `dependency-pinning-scan.yml` | `dependency-types` (string) | Step-level `INPUT_DEPENDENCY_TYPES` |
| `gitleaks-scan.yml` | `log-opts` (string) | Step-level `INPUT_LOG_OPTS` |

Boolean inputs (`soft-fail`, `changed-files-only`, etc.) and number inputs (`fuzz-runs`, `threshold`, `max-age-days`) remain as direct interpolation — these are documented safe exceptions under CQ-6 and covered by test fixtures.

### Documentation — `.github/workflows/README.md`

Added **CQ-6: Caller-Controlled Input Isolation** section documenting:
- Enforcement via `dangerous-workflow/direct-input-interpolation` rule
- `INPUT_*` isolation pattern with PowerShell and bash examples
- Safe-exception table (boolean/number types)
- Residual posture statement

> [!WARNING]
>
> Firewall blocked 2 domains
>
> The following domains were blocked by the firewall during workflow execution:
>
> - `dc.services.visualstudio.com`
> - `www.powershellgallery.com`
>> To allow these domains, add them to the `network.allowed` list in your workflow frontmatter:
>
> ```yaml
> network:
> allowed:
> - defaults
> - "dc.services.visualstudio.com"
> - "www.powershellgallery.com"
> ```
>
> See [Network Configuration](https://github.github.com/gh-aw/reference/network/) for more information.
>
>

> Generated by [Issue Implementation Agent](https://github.com/microsoft/hve-core/actions/runs/31153657929) for #2623 · 595.3 AIC · ⌖ 19.6 AIC · ⊞ 7.4K · [◷](https://github.com/search?q=repo%3Amicrosoft%2Fhve-core+%22gh-aw-workflow-id%3A+issue-implement%22&type=pullrequests)

---

> [!NOTE]
> This was originally intended as a pull request, but the git push operation failed.
>
> **Original error:** pushSignedCommits: refusing unsigned push for branch 'feat/cq6-caller-input-isolation-6c1cfcb7094a5cde': Signed-commit payload violates file-protection policy (request_review): .github/workflows/README.md, .github/workflows/dependency-pinning-scan.yml, .github/workflows/fuzz-tests.yml, .github/workflows/gitleaks-scan.yml, .github/workflows/node-tests.yml, .github/workflows/pytest-tests.yml, .github/workflows/python-lint.yml, .github/workflows/skill-validation.yml
>
> **Workflow Run:** [View run details and download bundle artifact](https://github.com/microsoft/hve-core/actions/runs/31153657929)
>
> The bundle file is available in the `agent` artifact in the workflow run linked above.

To create a pull request with the changes:

```sh
# Download the artifact from the workflow run
gh run download 31153657929 -n agent -D /tmp/agent-31153657929

# Fetch the bundle into a temporary ref, then update the local branch
git fetch /tmp/agent-31153657929/aw-feat-cq6-caller-input-isolation.bundle refs/heads/feat/cq6-caller-input-isolation:refs/bundles/create-pr-feat-cq6-caller-input-isolation-6c1cfcb7094a5cde-96abc18d
git update-ref refs/heads/feat/cq6-caller-input-isolation-6c1cfcb7094a5cde refs/bundles/create-pr-feat-cq6-caller-input-isolation-6c1cfcb7094a5cde-96abc18d
git checkout feat/cq6-caller-input-isolation-6c1cfcb7094a5cde
# Ensure the working tree matches the updated branch
git reset --hard
# Remove the temporary bundle ref
git update-ref -d refs/bundles/create-pr-feat-cq6-caller-input-isolation-6c1cfcb7094a5cde-96abc18d

# Push the branch to origin
git push origin feat/cq6-caller-input-isolation-6c1cfcb7094a5cde

# Create the pull request
gh pr create --title '#2623 feat(workflows): enforce CQ-6 for caller-controlled inputs' --base main --head feat/cq6-caller-input-isolation-6c1cfcb7094a5cde --repo microsoft/hve-core
```

Contributor guide

Open the contributing guide

Research direction

Review scripts/security/Test-DangerousWorkflow.ps1 and scripts/tests/security/Test-DangerousWorkflow.Tests.ps1 first, then inspect the seven listed workflow files and .github/workflows/README.md. Run the Pester fixtures and verify the documented CQ-6 detection, safe exceptions, workflow migrations, and documentation are complete.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, powershell
Domain
ci-cd, security
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
15/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.