Skip to content

feat(plugins): add windows support - #1734

Open
developerkunal wants to merge 1 commit into
DXCDT-2609/plugin-telemetryfrom
DXCDT-2610/windows-support
Open

developerkunal wants to merge 1 commit into
DXCDT-2609/plugin-telemetryfrom
DXCDT-2610/windows-support

Conversation

@developerkunal

@developerkunal developerkunal commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

🔧 Changes

Plugins now run on Windows. Before this change a timeout or Ctrl+C on Windows stopped only the plugin itself, and anything it had started could keep running. Several file checks and renames also assumed Unix behaviour. This fills those gaps and adds no new commands or flags.

Process tree

  • On Windows the plugin starts suspended and is put into a job object before it runs any code, so everything it starts belongs to the same job. If that fails, the plugin is killed before it has run anything.
  • A timeout or Ctrl+C ends the whole tree through the job, including for interactive plugins. A plugin that is not interactive gets its own process group and is first asked to stop with Ctrl+Break, which is best effort because it needs a console.
  • If the CLI itself dies, closing the job ends the plugin. If the plugin exits by itself, what it left running keeps running, the same as on Unix.

Rename retry. On Windows a virus scanner or indexer can hold a file open for a moment, so plugins.Rename retries a failed rename for about 1.3 seconds in total. The store, the registry cache and the approvals file use it, and on other systems it is plain os.Rename. Removing a plugin that is running now tells the person to stop it and delete the directory by hand.

File checks. Installed files with more than one hard link are refused on Windows too, using the already open file handle, and directories that are junctions are refused. Windows ACLs are not checked. The Unix checks are unchanged.

Behaviour changes that reach other systems. These two apply everywhere, so that a plugin that installs on one system installs on all of them.

  • Both parts of a plugin ID (registry/name) are checked against the Windows device names (con, nul, com1 and so on), and an ID with such a segment is refused.
  • A registry index is rejected when the bin of a Windows artifact does not end in .exe, ignoring case. This keeps .bat and .cmd scripts out, because Windows runs those through the command interpreter.

CI. .github/workflows/main.yml has a new windows-tests job on windows-latest that runs the plugin tests, with and without the plugintest build tag. Both actions are pinned to a commit SHA.

📚 References

DXCDT-2610. Part of the plugin framework stack of 11 PRs, and it builds on the PR below it, DXCDT-2609.

🔬 Testing

The Windows specific behaviour runs in a new Windows CI job, and the rest is covered by unit tests.

📝 Checklist

  • All new/changed/fixed functionality is covered by tests (or N/A)
  • I have added documentation for all new/changed functionality (or N/A)

@developerkunal
developerkunal requested a review from a team as a code owner October 24, 2026 14:47
@developerkunal
developerkunal force-pushed the DXCDT-2610/windows-support branch from 8020ccf to 01f3e33 Compare October 24, 2026 15:30
@developerkunal
developerkunal force-pushed the DXCDT-2610/windows-support branch from 01f3e33 to 15f6691 Compare November 2, 2026 11:12
@developerkunal
developerkunal force-pushed the DXCDT-2610/windows-support branch from 15f6691 to 771e683 Compare November 2, 2026 14:31
- run plugins in a job object so a timeout or Ctrl-C stops the whole process tree
- retry file and directory renames that fail while another process has them open
- check the hard link count of installed files on Windows from the open handle
- refuse plugin IDs that use a Windows device name and Windows artifacts whose executable is not an .exe
- explain when a plugin cannot be removed because it is in use
- move POSIX only tests to their own files so the packages build and vet for Windows
- run the plugin tests on a Windows runner in the main workflow
@developerkunal
developerkunal force-pushed the DXCDT-2610/windows-support branch from 771e683 to e7b6e82 Compare November 2, 2026 19:36
_, _ = w.Write(answer.body[:len(answer.body)/2])
panic(http.ErrAbortHandler)
default:
_, _ = w.Write(answer.body)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High severity blocking issue identified in your code:
Untrusted input could be used to tamper with a web page rendering, which can lead to a Cross-site scripting (XSS) vulnerability. XSS vulnerabilities occur when untrusted input executes malicious JavaScript code, leading to issues such as account compromise and sensitive information leakage. To prevent this vulnerability, validate the user input, perform contextual output encoding or sanitize the input. For more information, see: [Go XSS prevention] (https://semgrep.dev/docs/cheat-sheets/go-xss/).

Why this might be safe to ignore:

The URL path is used only as a lookup key to retrieve a pre-configured route response body; the answer.body written to the response is server-controlled data, not user-supplied content derived from the request path. Additionally, this is a test helper file, making exploitation even less likely.

Dataflow graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>internal/plugins/registry/registrytest/server.go</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/registry/registrytest/server.go#L166 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 166] r.URL</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/registry/registrytest/server.go#L166 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 166] path</a>"]

            v3["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/registry/registrytest/server.go#L181 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 181] answer</a>"]
        end
            v2 --> v3
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/registry/registrytest/server.go#L196 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 196] w.Write(answer.body)</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink


Loading

To resolve this comment:

🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by no-direct-write-to-responsewriter-taint.

You can view more details about this finding in the Semgrep AppSec Platform.

}

w.WriteHeader(response.status)
_, _ = w.Write(body)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟠 High severity blocking issue identified in your code:
Untrusted input could be used to tamper with a web page rendering, which can lead to a Cross-site scripting (XSS) vulnerability. XSS vulnerabilities occur when untrusted input executes malicious JavaScript code, leading to issues such as account compromise and sensitive information leakage. To prevent this vulnerability, validate the user input, perform contextual output encoding or sanitize the input. For more information, see: [Go XSS prevention] (https://semgrep.dev/docs/cheat-sheets/go-xss/).

Why this might be safe to ignore:

The data written to the response (cleaned) is the upstream API's JSON response body processed through stripSecrets, not a direct reflection of user-supplied input. The proxy reads the request body, forwards it to an upstream service, validates the response is JSON, strips secrets, and returns that - the taint tracking conflates the request body source with the upstream response data flowing to the sink.

Dataflow graph
flowchart LR
    classDef invis fill:white, stroke: none
    classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none

    subgraph File0["<b>internal/plugins/proxy/proxy.go</b>"]
        direction LR
        %% Source

        subgraph Source
            direction LR

            v0["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L489 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 489] r.Body</a>"]
        end
        %% Intermediate

        subgraph Traces0[Traces]
            direction TB

            v2["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L489 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 489] body</a>"]

            v3["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L447 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 447] readRequestBody</a>"]

            v4["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L447 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 447] body</a>"]

            v5["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L454 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 454] {</a>"]

            v6["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L454 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 454] request</a>"]

            v7["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L462 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 462] response</a>"]

            v8["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L473 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 473] cleaned</a>"]

            v9["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L480 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 480] writeResponse</a>"]

            v10["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L663 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 663] body</a>"]
        end
            v2 --> v3
            v3 --> v4
            v4 --> v5
            v5 --> v6
            v6 --> v7
            v7 --> v8
            v8 --> v9
            v9 --> v10
        %% Sink

        subgraph Sink
            direction LR

            v1["<a href=https://github.com/auth0/auth0-cli/blob/e7b6e82a790e9a67b4cd2b9632c2636c714fee25/internal/plugins/proxy/proxy.go#L684 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 684] w.Write(body)</a>"]
        end
    end
    %% Class Assignment
    Source:::invis
    Sink:::invis

    Traces0:::invis
    File0:::invis

    %% Connections

    Source --> Traces0
    Traces0 --> Sink


Loading

To resolve this comment:

🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.

💬 Ignore this finding

Reply with Semgrep commands to ignore this finding.

  • /fp <comment> for false positive
  • /ar <comment> for acceptable risk
  • /other <comment> for all other reasons

Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by no-direct-write-to-responsewriter-taint.

You can view more details about this finding in the Semgrep AppSec Platform.

This branch has not been deployed

No deployments
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.

1 participant