From 4b1f90e5d6e7b7293b9d36b26f7ec8f879e1a064 Mon Sep 17 00:00:00 2001 From: Priyanka Rajurkar Date: Tue, 3 Nov 2026 23:44:57 +0530 Subject: [PATCH 1/3] Require the actor's SSH key by default (WARP-1192) limit-access-to-actor now defaults to true. If the user who started the workflow has no public SSH key, the step fails instead of opening the session to anyone. Set limit-access-to-actor: false to allow open access. Co-Authored-By: Claude Opus 5.5 --- README.md | 6 ++++-- action.yml | 4 ++-- detached/action.yml | 4 ++-- lib/index.js | 20 +++++--------------- src/index.js | 18 ++++-------------- src/index.test.js | 45 +++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 60 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index 539cf8a..7c1d36f 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,9 @@ jobs: ## Use registered public SSH key(s) -If [you have registered one or more public SSH keys with your GitHub profile](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account), ssh will be started such that only those keys are authorized to connect, otherwise anybody can connect to the ssh session. If you want to require a public SSH key to be installed with the ssh session, no matter whether the user who started the workflow has registered any in their GitHub profile, you will need to configure the setting `limit-access-to-actor` to `true`, like so: +By default, only the public SSH keys [registered with the GitHub profile](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account) of the user who started the workflow can connect. If that user has no public SSH key, the session does not start and the step fails. + +To allow anyone with the session address to connect, set `limit-access-to-actor` to `false`. Do not do this on a public repository, because anyone who can read the job log can then open a shell on the runner. ```yaml name: CI @@ -205,7 +207,7 @@ jobs: - name: Setup interactive ssh session uses: Warpbuilds/action-debugger@v1.3 with: - limit-access-to-actor: true + limit-access-to-actor: false ``` If the registered public SSH key is not your default private SSH key, you will need to specify the path manually, like so: `ssh -i `. diff --git a/action.yml b/action.yml index a47a599..bbcff11 100644 --- a/action.yml +++ b/action.yml @@ -19,9 +19,9 @@ inputs: required: false default: "true" limit-access-to-actor: - description: "Whether to authorize only the public SSH keys of the user triggering the workflow (defaults to true if the GitHub profile of the user has a public SSH key)" + description: "Whether to authorize only the public SSH keys of the user triggering the workflow. The session fails to start if the user has no public SSH key. Set to false to allow anyone with the session address to connect." required: false - default: "auto" + default: "true" detached: description: "In detached mode, the workflow job will continue while the ssh session is active" required: false diff --git a/detached/action.yml b/detached/action.yml index 2df030e..90f2d77 100644 --- a/detached/action.yml +++ b/detached/action.yml @@ -19,9 +19,9 @@ inputs: required: false default: "true" limit-access-to-actor: - description: "Whether to authorize only the public SSH keys of the user triggering the workflow (defaults to true if the GitHub profile of the user has a public SSH key)" + description: "Whether to authorize only the public SSH keys of the user triggering the workflow. The session fails to start if the user has no public SSH key. Set to false to allow anyone with the session address to connect." required: false - default: "auto" + default: "true" detached: description: "In detached mode, the workflow job will continue while the ssh session is active" required: false diff --git a/lib/index.js b/lib/index.js index 8a6658f..6964fa8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -35242,7 +35242,7 @@ function core_error(message, properties = {}) { * @param properties optional properties to add to the annotation. */ function warning(message, properties = {}) { - command_issueCommand('warning', utils_toCommandProperties(properties), message instanceof Error ? message.toString() : message); + issueCommand('warning', toCommandProperties(properties), message instanceof Error ? message.toString() : message); } /** * Adds a notice issue @@ -41232,9 +41232,8 @@ async function run() { let newSessionExtra = ""; let tmateSSHDashI = ""; - let publicSSHKeysWarning = ""; const limitAccessToActor = getInput("limit-access-to-actor"); - if (limitAccessToActor === "true" || limitAccessToActor === "auto") { + if (limitAccessToActor !== "false") { const { actor, apiUrl } = github_context; const auth = getInput("github-token"); const octokit = new dist_src_Octokit({ @@ -41246,12 +41245,9 @@ async function run() { username: actor, }); if (keys.data.length === 0) { - if (limitAccessToActor === "auto") - publicSSHKeysWarning = `No public SSH keys found for ${actor}; continuing without them even if it is less secure (please consider adding an SSH key, see https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account)`; - else - throw new Error( - `No public SSH keys registered with ${actor}'s GitHub profile` - ); + throw new Error( + `No public SSH keys registered with ${actor}'s GitHub profile. Add one at https://github.com/settings/keys, or set limit-access-to-actor: false to allow anyone with the session address to connect.` + ); } else { const sshPath = external_path_default().join(external_os_default().homedir(), ".ssh"); await external_fs_default().promises.mkdir(sshPath, { recursive: true }); @@ -41354,9 +41350,6 @@ async function run() { core_debug("Entering detached mode"); let message = ""; - if (publicSSHKeysWarning) { - message += `::warning::${publicSSHKeysWarning}\n`; - } if (tmateWeb) { message += `::notice::Web shell: ${tmateWeb}\n`; } @@ -41383,9 +41376,6 @@ async function run() { core_debug("Entering main loop"); while (true) { - if (publicSSHKeysWarning) { - warning(publicSSHKeysWarning); - } if (tmateWeb) { info(`Web shell: ${tmateWeb}`); } diff --git a/src/index.js b/src/index.js index 320b2d6..71d9502 100644 --- a/src/index.js +++ b/src/index.js @@ -155,9 +155,8 @@ export async function run() { let newSessionExtra = ""; let tmateSSHDashI = ""; - let publicSSHKeysWarning = ""; const limitAccessToActor = core.getInput("limit-access-to-actor"); - if (limitAccessToActor === "true" || limitAccessToActor === "auto") { + if (limitAccessToActor !== "false") { const { actor, apiUrl } = github.context; const auth = core.getInput("github-token"); const octokit = new Octokit({ @@ -169,12 +168,9 @@ export async function run() { username: actor, }); if (keys.data.length === 0) { - if (limitAccessToActor === "auto") - publicSSHKeysWarning = `No public SSH keys found for ${actor}; continuing without them even if it is less secure (please consider adding an SSH key, see https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account)`; - else - throw new Error( - `No public SSH keys registered with ${actor}'s GitHub profile` - ); + throw new Error( + `No public SSH keys registered with ${actor}'s GitHub profile. Add one at https://github.com/settings/keys, or set limit-access-to-actor: false to allow anyone with the session address to connect.` + ); } else { const sshPath = path.join(os.homedir(), ".ssh"); await fs.promises.mkdir(sshPath, { recursive: true }); @@ -277,9 +273,6 @@ export async function run() { core.debug("Entering detached mode"); let message = ""; - if (publicSSHKeysWarning) { - message += `::warning::${publicSSHKeysWarning}\n`; - } if (tmateWeb) { message += `::notice::Web shell: ${tmateWeb}\n`; } @@ -306,9 +299,6 @@ export async function run() { core.debug("Entering main loop"); while (true) { - if (publicSSHKeysWarning) { - core.warning(publicSSHKeysWarning); - } if (tmateWeb) { core.info(`Web shell: ${tmateWeb}`); } diff --git a/src/index.test.js b/src/index.test.js index 51cf640..0bc1d29 100644 --- a/src/index.test.js +++ b/src/index.test.js @@ -39,13 +39,16 @@ jest.mock( }), { virtual: true } ); +const mockListPublicKeysForUser = jest.fn(async () => ({ + data: [{ key: "ssh-ed25519 AAAA test-user" }], +})); jest.mock( "@octokit/rest", () => ({ __esModule: true, Octokit: class { users = { - listPublicKeysForUser: async () => ({ data: [] }), + listPublicKeysForUser: mockListPublicKeysForUser, }; }, }), @@ -124,7 +127,7 @@ describe("Tmate GitHub integration", () => { Object.defineProperty(process, "platform", { value: "win32", }); - core.getInput.mockReturnValueOnce("false"); + core.getInput.mockReturnValueOnce("false").mockReturnValueOnce("false"); const customConnectionString = "foobar"; execShellCommand.mockReturnValue(Promise.resolve(customConnectionString)); await run(); @@ -244,6 +247,44 @@ describe("Tmate GitHub integration", () => { expect(core.setFailed).not.toHaveBeenCalled(); }); + + it("should authorize only the actor's public SSH keys by default", async () => { + core.getInput.mockReturnValue(""); + + await run(); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(execShellCommand).toHaveBeenCalledWith( + expect.stringContaining("-a \"") + ); + }); + + it("should fail when the actor has no public SSH keys", async () => { + core.getInput.mockReturnValue(""); + mockListPublicKeysForUser.mockResolvedValueOnce({ data: [] }); + + await run(); + + expect(core.setFailed).toHaveBeenCalledWith( + expect.objectContaining({ + message: expect.stringContaining("No public SSH keys registered with test-user"), + }) + ); + expect(execShellCommand).not.toHaveBeenCalledWith( + expect.stringContaining("new-session") + ); + }); + + it("should allow anyone to connect when limit-access-to-actor is false", async () => { + core.getInput.mockImplementation((name) => + name === "limit-access-to-actor" ? "false" : "" + ); + + await run(); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(mockListPublicKeysForUser).not.toHaveBeenCalled(); + }); it("should validate correct tmate options", async () => { // Check for the happy path first. core.getInput.mockImplementation(function (opt) { From 396ecac3039f50c6a6b4f79fe9453024f3d343b2 Mon Sep 17 00:00:00 2001 From: Priyanka Rajurkar Date: Wed, 4 Nov 2026 02:04:47 +0530 Subject: [PATCH 2/3] README: note the web shell is off by default, use connection string (WARP-1192) Co-Authored-By: Claude Opus 5.5 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7c1d36f..443b443 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ jobs: uses: Warpbuilds/action-debugger@v1.3 ``` -To get the connection string, just open the `Checks` tab in your Pull Request or Run and scroll to the bottom. There you can connect either directly per SSH or via a web based terminal. The connection string is also logged inside workflow logs. +To get the connection string, just open the `Checks` tab in your Pull Request or Run and scroll to the bottom. There you can connect directly per SSH, or via a web based terminal when `limit-access-to-actor` is `false`. The connection string is also logged inside workflow logs. ![screenshots](https://github.com/WarpBuilds/action-debugger/assets/9110203/2e1ce772-285f-4a4e-a41a-d12054d8960e) ![check](https://github.com/WarpBuilds/action-debugger/assets/9110203/d8cb31ef-5044-4f39-a391-a198ec09ce39) @@ -192,9 +192,9 @@ jobs: ## Use registered public SSH key(s) -By default, only the public SSH keys [registered with the GitHub profile](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account) of the user who started the workflow can connect. If that user has no public SSH key, the session does not start and the step fails. +By default, only the user who started the workflow can connect, using the public SSH keys [registered with their GitHub profile](https://docs.github.com/en/github/authenticating-to-github/adding-a-new-ssh-key-to-your-github-account). If that user has no public SSH key, the step fails and no session starts. The web shell is also turned off. -To allow anyone with the session address to connect, set `limit-access-to-actor` to `false`. Do not do this on a public repository, because anyone who can read the job log can then open a shell on the runner. +To allow anyone with the connection string to connect, set `limit-access-to-actor` to `false`. Do not do this on a public repository, because anyone who can read the job log can then open a shell on the runner. ```yaml name: CI From 1142c72f29fbb7fb983e9bda509766025f8159a3 Mon Sep 17 00:00:00 2001 From: Priyanka Rajurkar Date: Thu, 5 Nov 2026 00:13:06 +0530 Subject: [PATCH 3/3] README: use @v2 in examples (WARP-1192) Co-Authored-By: Claude Opus 5.5 --- README.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 443b443..520050f 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup ssh session - uses: Warpbuilds/action-debugger@v1.3 + uses: Warpbuilds/action-debugger@v2 ``` To get the connection string, just open the `Checks` tab in your Pull Request or Run and scroll to the bottom. There you can connect directly per SSH, or via a web based terminal when `limit-access-to-actor` is `false`. The connection string is also logged inside workflow logs. @@ -66,7 +66,7 @@ jobs: steps: # Enable ssh debugging of manually-triggered workflows if the input option was provided - name: Setup interactive ssh session - uses: Warpbuilds/action-debugger@v1.3 + uses: Warpbuilds/action-debugger@v2 if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }} ``` @@ -89,7 +89,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup interactive ssh session - uses: Warpbuilds/action-debugger@v1.3 + uses: Warpbuilds/action-debugger@v2 with: detached: true ``` @@ -119,7 +119,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup ssh session id: debugger - uses: Warpbuilds/action-debugger@v1.3 + uses: Warpbuilds/action-debugger@v2 with: detached: true @@ -145,7 +145,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup ssh session - uses: Warpbuilds/action-debugger@v1.3 + uses: Warpbuilds/action-debugger@v2 with: sudo: false ``` @@ -163,7 +163,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Setup interactive ssh session - uses: Warpbuilds/action-debugger@v1.3 + uses: Warpbuilds/action-debugger@v2 timeout-minutes: 15 ``` @@ -184,7 +184,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup interactive ssh session if: ${{ failure() }} - uses: Warpbuilds/action-debugger@v1.3 + uses: Warpbuilds/action-debugger@v2 ```