Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions SPECS/gh/CVE-2026-45803.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
From 12bdd95f36cef29851d73e97faeca539366b121a Mon Sep 17 00:00:00 2001
From: AllSpark <allspark@microsoft.com>
Date: Wed, 3 Jun 2026 07:02:07 +0000
Subject: [PATCH] Fix log terminal injection

Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
Upstream-reference: AI Backport of https://github.com/cli/cli/commit/a9d36fb9ef38ecc4c143da6bacd91b3e959cd76a.patch
---
.../run-view-log-escape-sequences.txtar | 70 +++++++++++++++++++
pkg/cmd/run/view/view.go | 2 +
pkg/cmd/run/view/view_test.go | 42 +++++++++++
3 files changed, 114 insertions(+)
create mode 100644 acceptance/testdata/workflow/run-view-log-escape-sequences.txtar

diff --git a/acceptance/testdata/workflow/run-view-log-escape-sequences.txtar b/acceptance/testdata/workflow/run-view-log-escape-sequences.txtar
new file mode 100644
index 0000000..47978cf
--- /dev/null
+++ b/acceptance/testdata/workflow/run-view-log-escape-sequences.txtar
@@ -0,0 +1,70 @@
+# This test ensures that a malicious workflow which emit terminal control sequences (ESC, OSC, CSI) in
+# its log output does not result in terminal injection when logs are displayed using `gh run view --log`
+
+# Use gh as a credential helper
+exec gh auth setup-git
+
+# Create a repository with a file so it has a default branch
+exec gh repo create $ORG/$SCRIPT_NAME-$RANDOM_STRING --add-readme --private
+
+# Defer repo cleanup
+defer gh repo delete --yes $ORG/$SCRIPT_NAME-$RANDOM_STRING
+
+# Clone the repo
+exec gh repo clone $ORG/$SCRIPT_NAME-$RANDOM_STRING
+
+# Commit the workflow file
+cd $SCRIPT_NAME-$RANDOM_STRING
+mkdir .github/workflows
+mv ../workflow.yml .github/workflows/workflow.yml
+exec git add .github/workflows/workflow.yml
+exec git commit -m 'Create workflow with escape sequences'
+exec git push -u origin main
+
+# Sleep because it takes a second for the workflow to register
+sleep 1
+
+# Run the workflow
+exec gh workflow run 'Escape Sequence PoC'
+
+# It takes some time for a workflow run to register
+sleep 10
+
+# Get the run ID we want to view
+exec gh run list --json databaseId --jq '.[0].databaseId'
+stdout2env RUN_ID
+
+# Wait for workflow to complete
+exec gh run watch $RUN_ID --exit-status
+
+# View the logs and check that raw ESC bytes (0x1b) are NOT present in output.
+# If this assertion fails, it means terminal escape sequences from the workflow
+# log are being passed through to the user's terminal unsanitised.
+exec gh run view $RUN_ID --log
+
+# The output should contain the safe/visible text but not raw ESC bytes.
+# \x1b is the ESC byte - it must not appear in the output.
+! stdout '\x1b'
+
+# The log output should still contain the non-escape parts of the log lines.
+stdout 'ESCAPE_MARKER_START'
+stdout 'ESCAPE_MARKER_END'
+
+-- workflow.yml --
+name: Escape Sequence PoC
+
+on:
+ workflow_dispatch:
+
+jobs:
+ emit-escape-sequences:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Emit terminal escape sequences
+ run: |
+ # OSC title set: \x1b]0;TITLE\x07
+ printf 'ESCAPE_MARKER_START \033]0;HIJACKED_TITLE\007 ESCAPE_MARKER_END\n'
+ # CSI color: \x1b[31m ... \x1b[0m
+ printf 'ESCAPE_MARKER_START \033[31mRED_TEXT\033[0m ESCAPE_MARKER_END\n'
+ # Screen title set (from original PoC): \x1bk ... \x1b\\
+ printf 'ESCAPE_MARKER_START \033k;malicious command;\033\\ ESCAPE_MARKER_END\n'
diff --git a/pkg/cmd/run/view/view.go b/pkg/cmd/run/view/view.go
index c794cff..9103ac8 100644
--- a/pkg/cmd/run/view/view.go
+++ b/pkg/cmd/run/view/view.go
@@ -26,7 +26,9 @@ import (
"github.com/cli/cli/v2/pkg/cmd/run/shared"
"github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/iostreams"
+ "github.com/cli/go-gh/v2/pkg/asciisanitizer"
"github.com/spf13/cobra"
+ "golang.org/x/text/transform"
)

type RunLogCache struct {
diff --git a/pkg/cmd/run/view/view_test.go b/pkg/cmd/run/view/view_test.go
index 05eb257..e56e826 100644
--- a/pkg/cmd/run/view/view_test.go
+++ b/pkg/cmd/run/view/view_test.go
@@ -1611,6 +1611,48 @@ var coolJobRunLogOutput = fmt.Sprintf("%s%s", fobTheBarzLogOutput, barfTheFobLog
var sadJobRunLogOutput = fmt.Sprintf("%s%s", barfTheQuuxLogOutput, quuxTheBarfLogOutput)
var expectedRunLogOutput = fmt.Sprintf("%s%s", coolJobRunLogOutput, sadJobRunLogOutput)

+
+func TestCopyLogWithLinePrefix_TerminalEscapeSequences(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ }{
+ {
+ name: "OSC title set sequence",
+ input: "normal prefix\x1b]0;HIJACKED TITLE\x07trailing text\n",
+ },
+ {
+ name: "CSI color sequence",
+ input: "\x1b[31mRED TEXT\x1b[0m normal text\n",
+ },
+ {
+ name: "screen title set sequence used in original report",
+ input: "\x1bk;echo this is an arbitrary command;\x1b\\\n",
+ },
+ {
+ name: "CSI window title query",
+ input: "before\x1b[21tafter\n",
+ },
+ {
+ name: "multiple escape sequences",
+ input: "\x1b]0;title\x07\x1b[31mred\x1b[0m\x1b[21t\n",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var buf bytes.Buffer
+ err := copyLogWithLinePrefix(&buf, strings.NewReader(tt.input), "jobname\tstep\t")
+ require.NoError(t, err)
+
+ output := buf.String()
+ assert.NotContains(t, output, "\x1b",
+ "output should not contain raw ESC (0x1b) bytes, got: %q", output)
+ })
+ }
+}
+
+
func TestRunLog(t *testing.T) {
t.Run("when the cache dir doesn't exist, exists return false", func(t *testing.T) {
cacheDir := t.TempDir() + "/non-existent-dir"
--
2.45.4

6 changes: 5 additions & 1 deletion SPECS/gh/gh.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Summary: GitHub official command line tool
Name: gh
Version: 2.62.0
Release: 17%{?dist}
Release: 18%{?dist}
License: MIT
Vendor: Microsoft Corporation
Distribution: Azure Linux
Expand Down Expand Up @@ -45,6 +45,7 @@ Patch29: CVE-2026-39827.patch
Patch30: CVE-2026-39828.patch
Patch31: CVE-2026-39835.patch
Patch32: CVE-2026-42502.patch
Patch33: CVE-2026-45803.patch

BuildRequires: golang < 1.24
BuildRequires: git
Expand Down Expand Up @@ -89,6 +90,9 @@ make test
%{_datadir}/zsh/site-functions/_gh

%changelog
* Wed Jun 03 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 2.62.0-18
- Patch for CVE-2026-45803

* Mon Jun 01 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 2.62.0-17
- Patch for CVE-2026-42502, CVE-2026-39835, CVE-2026-39828, CVE-2026-39827, CVE-2026-25681, CVE-2026-25680

Expand Down
Loading