Skip to content

Conversation

@jwikman
Copy link
Contributor

@jwikman jwikman commented Nov 20, 2025

❔What, Why & How

This pull request introduces an important enhancement to the workflow input handling in AL-Go: you can now set "hide": true on a workflow input in your settings to remove it from the manual workflow form. When an input is hidden, all references to that input in the workflow file are replaced with the configured value, ensuring workflows can use default values without exposing them to users. The implementation includes updates to the schema, helper functions, YAML manipulation, documentation, and test instructions.

Workflow Input Hiding Feature:

  • Added support for "hide": true in workflowDefaultInputs in the settings schema, allowing inputs to be removed from the workflow form and replaced with their configured values in the workflow file (Actions/.Modules/settings.schema.json).
  • Updated ApplyWorkflowDefaultInputs in CheckForUpdates.HelperFunctions.ps1 to:
    • Track inputs marked as hidden and remove them from the workflow YAML.
    • Replace all usages of hidden inputs (github.event.inputs.<name> and inputs.<name>) with their literal values, using regex and careful matching to avoid accidental replacements (Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1). [1] [2] [3]

YAML Manipulation Improvements:

  • Enhanced the Yaml class with a new ReplaceAll method supporting regex-based replacements, enabling robust substitution of input references throughout workflow files (Actions/CheckForUpdates/yamlclass.ps1).

Documentation Updates:

  • Updated release notes to document the new hiding feature, including usage examples and details about input reference replacement (RELEASENOTES.md). [1] [2]

Related to discussion: #1952

✅ Checklist

  • Add tests (E2E, unit tests)
  • Update RELEASENOTES.md
  • Update documentation (e.g. for new settings or scenarios)
  • Add telemetry

…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…-sensitive matching and update related test cases
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…nput' into w/johwik/1952-Hide-default-values-from-workflow_dispatch-input
…logic and remove unnecessary input removal code.
@jwikman
Copy link
Contributor Author

jwikman commented Nov 20, 2025

@mazhelez here's the PR for the "hide input" functionality I mentioned earlier this week.

What do you think of this functionality? 🤔
For us, it would remove a lot of "clutter" from the developer UX.

ps.
Don't know why GitHub thinks that I rewritten the whole test file, even though I added a lot, and increased indentation on my old tests... Use external comparison tool for that file - sorry!

@mazhelez
Copy link
Collaborator

mazhelez commented Dec 3, 2025

⚠️ Release Notes Update Reminder

Thank you for updating the release notes!

Please ensure that your changes are placed above the new version section (currently ## v8.1) in the RELEASENOTES.md file.

This helps maintain a clear changelog structure where new changes are grouped under the latest unreleased version.

@jwikman jwikman marked this pull request as ready for review December 3, 2025 17:09
@jwikman jwikman requested a review from a team as a code owner December 3, 2025 17:09
Copilot AI review requested due to automatic review settings December 3, 2025 17:09
Copy link
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 pull request introduces a workflow input hiding feature that extends the workflowDefaultInputs setting introduced in v8.1. Users can now set "hide": true on workflow inputs to remove them from the GitHub Actions manual workflow dispatch form while automatically replacing all references to those inputs with their configured values.

Key changes:

  • Enhanced workflowDefaultInputs setting to support an optional "hide" property
  • Added regex-based replacement logic to substitute hidden input references throughout workflow files
  • Implemented comprehensive test coverage for the hiding feature including edge cases

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Actions/.Modules/settings.schema.json Added "hide" property to workflow input schema with boolean type and description
Actions/CheckForUpdates/yamlclass.ps1 Added overloaded ReplaceAll method supporting regex-based replacements with $isRegex parameter
Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1 Enhanced ApplyWorkflowDefaultInputs to track hidden inputs, remove them from workflow YAML, and replace all references with literal values
Tests/CheckForUpdates.Action.Test.ps1 Added extensive test coverage for YAML regex replacement and hiding feature including edge cases for job outputs and partial matches
Scenarios/settings.md Updated documentation to describe the new "hide" property with examples
RELEASENOTES.md Added release notes documenting the hiding feature with usage examples
Comments suppressed due to low confidence (1)

Actions/CheckForUpdates/CheckForUpdates.HelperFunctions.ps1:583

  • The regex patterns directly use $inputName without escaping special regex characters. If an input name contains regex special characters (like ., *, +, ?, [, ], (, ), {, }, ^, $, |, \), the pattern will not work correctly or could cause runtime errors.

For example, if an input is named "my.input", the pattern (?<!\.)inputs\.my.input\b(?!\.) would match "inputs" followed by any three characters (due to the unescaped dots).

Suggestion: Use [regex]::Escape($inputName) when constructing the regex patterns to properly escape special characters:

$escapedInputName = [regex]::Escape($inputName)
$pattern = "`\$\{\{\s*github\.event\.inputs\.$escapedInputName\s*\}\}"
# ... and so on for other patterns
            $pattern = "`\$\{\{\s*github\.event\.inputs\.$inputName\s*\}\}"
            $yaml.ReplaceAll($pattern, $expressionValue, $true)
            $pattern = "`\$\{\{\s*inputs\.$inputName\s*\}\}"
            $yaml.ReplaceAll($pattern, $expressionValue, $true)

            # Replace references in if conditions: github.event.inputs.name and inputs.name (without ${{ }})
            # github.event.inputs are always strings (user input from UI)
            # inputs.* are typed values (boolean/number/string/choice)

            # For github.event.inputs.NAME: always use string literal format
            # Convert the value to a string literal (always quoted) for comparisons
            if ($rawValue -is [bool]) {
                $stringValue = $rawValue.ToString().ToLower()
            }
            else {
                $stringValue = $rawValue.ToString().Replace("'", "''")
            }
            $stringLiteral = "'$stringValue'"
            $yaml.ReplaceAll("github.event.inputs.$inputName", $stringLiteral)

            # For inputs.NAME: use expression literal format (typed value)
            # Replace inputs.NAME but be careful not to match patterns like:
            # - needs.inputs.outputs.NAME (where a job is named "inputs")
            # - steps.CreateInputs.outputs.NAME (where "inputs" is part of a word)
            # Use negative lookbehind (?<!\.) to ensure "inputs" is not preceded by a dot
            # Use word boundary \b after inputName to avoid partial matches
            # Don't match if followed by a dot (to avoid matching outputs references)
            $pattern = "(?<!\.)inputs\.$inputName\b(?!\.)"
            $yaml.ReplaceAll($pattern, $expressionValue, $true)

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

},
"value": {
"description": "The default value for the workflow input (can be string, boolean, or number)"
},
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Inconsistent indentation: Line 754 has improper closing of the "value" property (extra spaces before the closing brace). The line should be:

                    },

instead of:

                                },

This maintains consistent indentation with the surrounding JSON properties.

Suggested change
},
},

Copilot uses AI. Check for mistakes.
Comment on lines +1206 to +2346
"name: 'Test Workflow'",
"on:",
" workflow_dispatch:",
" inputs:",
" input1:",
" type: string",
" default: ''",
" input2:",
" type: boolean",
" default: false",
"jobs:",
" test:",
" runs-on: ubuntu-latest"
)
$yaml = [Yaml]::new($yamlContent)

$yaml = [Yaml]::new($yamlContent)
# Create settings with duplicate entries where first has hide=false, last has hide=true
$repoSettings = @{
"workflowDefaultInputs" = @(
@{ "name" = "myInput"; "value" = "first-value"; "hide" = $false },
@{ "name" = "myInput"; "value" = "final-value"; "hide" = $true }
)
}

# Create settings with duplicate entries for input1 - simulating merged conditional settings
# This can happen when multiple conditionalSettings blocks both match and both define the same input
$repoSettings = @{
"workflowDefaultInputs" = @(
@{ "name" = "input1"; "value" = "first-value" },
@{ "name" = "input2"; "value" = $false },
@{ "name" = "input1"; "value" = "second-value" }, # Duplicate input1
@{ "name" = "input1"; "value" = "final-value" } # Another duplicate input1
# Apply the defaults
ApplyWorkflowDefaultInputs -yaml $yaml -repoSettings $repoSettings -workflowName "Test Workflow - Duplicate Hide"

# Verify last entry wins - input should be hidden
$inputs = $yaml.Get('on:/workflow_dispatch:/inputs:/')
$inputs.Find('myInput:', [ref] $null, [ref] $null) | Should -Be $false

# Verify the final hidden value was used
$content = $yaml.content -join "`n"
$content | Should -Match "echo 'final-value'"
$content | Should -Not -Match "inputs\.myInput"
}

It 'applies last value when duplicate entries have hide reversed' {
. (Join-Path $scriptRoot "yamlclass.ps1")

# Create a test workflow YAML
$yamlContent = @(
"name: 'Test Workflow'",
"on:",
" workflow_dispatch:",
" inputs:",
" myInput:",
" type: string",
" default: ''",
"jobs:",
" test:",
" runs-on: ubuntu-latest",
" steps:",
" - name: Test",
" run: echo `${{ inputs.myInput }}"
)
}

# Apply the defaults
ApplyWorkflowDefaultInputs -yaml $yaml -repoSettings $repoSettings -workflowName "Test Workflow"
$yaml = [Yaml]::new($yamlContent)

# Verify "last wins" - the final value for input1 should be applied
$yaml.Get('on:/workflow_dispatch:/inputs:/input1:/default:').content -join '' | Should -Be "default: 'final-value'"
$yaml.Get('on:/workflow_dispatch:/inputs:/input2:/default:').content -join '' | Should -Be 'default: false'
}
# Create settings with duplicate entries where first has hide=true, last has hide=false
$repoSettings = @{
"workflowDefaultInputs" = @(
@{ "name" = "myInput"; "value" = "first-value"; "hide" = $true },
@{ "name" = "myInput"; "value" = "final-value"; "hide" = $false }
)
}

# Apply the defaults
ApplyWorkflowDefaultInputs -yaml $yaml -repoSettings $repoSettings -workflowName "Test Workflow - Duplicate Hide Reversed"

# Verify last entry wins - input should NOT be hidden
$inputs = $yaml.Get('on:/workflow_dispatch:/inputs:/')
$inputs.Find('myInput:', [ref] $null, [ref] $null) | Should -Be $true

# Verify the final value was applied to the default
$yaml.Get('on:/workflow_dispatch:/inputs:/myInput:/default:').content -join '' | Should -Be "default: 'final-value'"

# Verify the reference was NOT replaced (input is visible)
$content = $yaml.content -join "`n"
$content | Should -Match "echo `\$\{\{ inputs\.myInput \}\}"
$content | Should -Not -Match "echo 'final-value'"
}

} # End of Context 'ApplyWorkflowDefaultInputs - Hide Feature'
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Missing test coverage: There are no tests for hiding inputs whose names contain regex special characters (e.g., my.input, my-input, my+input).

Since the regex patterns in lines 555-583 of CheckForUpdates.HelperFunctions.ps1 directly use $inputName without escaping, inputs with special characters could cause incorrect pattern matching or runtime errors.

Suggestion: Add test cases that verify hiding works correctly for input names containing characters like ., -, +, *, ?, [, ], (, ), etc.

Copilot uses AI. Check for mistakes.
Comment on lines +359 to +362
$hideInput = $false
if ($default['hide']) {
$hideInput = [bool]$default.hide
}
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Inconsistent indentation: Lines 359-362 have extra indentation (8 spaces instead of 4). This should be aligned with the surrounding code at the same scope level.

Suggested change
$hideInput = $false
if ($default['hide']) {
$hideInput = [bool]$default.hide
}
$hideInput = $false
if ($default['hide']) {
$hideInput = [bool]$default.hide
}

Copilot uses AI. Check for mistakes.
Comment on lines +555 to +558
$pattern = "`\$\{\{\s*github\.event\.inputs\.$inputName\s*\}\}"
$yaml.ReplaceAll($pattern, $expressionValue, $true)
$pattern = "`\$\{\{\s*inputs\.$inputName\s*\}\}"
$yaml.ReplaceAll($pattern, $expressionValue, $true)
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Regex patterns are constructed using unescaped input names ($inputName) when replacing hidden input references, e.g., "${{\sgithub.event.inputs.$inputName\s}}"and"(?<!.)inputs.$inputName\b(?!.)". If an input name contains regex metacharacters (like . * + ? ( ) [ ] |), this can cause overbroad or malformed matches and unintended replacements across the workflow, enabling an attacker who controls workflow input names to tamper with workflow logic. Fix by escaping $inputNamefor regex (e.g., using[Regex]::Escape($inputName)`) before interpolating it into patterns:

$escaped = [Regex]::Escape($inputName)
$pattern1 = "`\$\{\{\s*github\.event\.inputs\.$escaped\s*\}\}"
$pattern2 = "(?<!\.)inputs\.$escaped\b(?!\.)"

Also ensure any other regex constructions using input-derived values apply the same escaping.

Copilot uses AI. Check for mistakes.
# Use negative lookbehind (?<!\.) to ensure "inputs" is not preceded by a dot
# Use word boundary \b after inputName to avoid partial matches
# Don't match if followed by a dot (to avoid matching outputs references)
$pattern = "(?<!\.)inputs\.$inputName\b(?!\.)"
Copy link

Copilot AI Dec 3, 2025

Choose a reason for hiding this comment

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

Regex pattern "(?<!\.)inputs\.$inputName\b(?!\.)" embeds $inputName without regex escaping. If the input name includes regex metacharacters, it can match unintended substrings (e.g., parts of job/step output references or other content) and replace them with attacker-controlled values, corrupting workflow logic. Fix by escaping the name: "(?<!\.)inputs\.[Regex]::Escape($inputName)\b(?!\.)" or build the pattern using $escaped = [Regex]::Escape($inputName) and then interpolate $escaped.

Suggested change
$pattern = "(?<!\.)inputs\.$inputName\b(?!\.)"
$escapedInputName = [Regex]::Escape($inputName)
$pattern = "(?<!\.)inputs\.$escapedInputName\b(?!\.)"

Copilot uses AI. Check for mistakes.
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.

2 participants