From bb26012f276f091f024a2043d0a382ab83a2175f Mon Sep 17 00:00:00 2001 From: Eyal Gal Date: Fri, 8 May 2026 01:15:04 -0700 Subject: [PATCH 1/5] Remove Codespaces from GHE.com 'Features in public preview' (now GA) (#61129) Co-authored-by: Isaac Brown <101839405+isaacmbrown@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- ...rview-for-github-enterprise-cloud-with-data-residency.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md b/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md index 2a46e68bb86e..9dac1372a0a8 100644 --- a/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md +++ b/content/admin/data-residency/feature-overview-for-github-enterprise-cloud-with-data-residency.md @@ -104,12 +104,8 @@ You can use {% data variables.product.prodname_github_connect %} to connect to { To enable {% data variables.product.prodname_github_connect %}, you must configure your {% data variables.product.prodname_ghe_server %} instance to connect to your {% data variables.enterprise.data_residency_site %} subdomain. See [AUTOTITLE](/enterprise-server@latest/admin/configuring-settings/configuring-github-connect/enabling-github-connect-for-ghecom). -## Features in {% data variables.release-phases.public_preview %} - -Some features on {% data variables.enterprise.data_residency_site %} are currently in {% data variables.release-phases.public_preview %}. - ### {% data variables.product.prodname_github_codespaces %} -{% data variables.product.prodname_github_codespaces %} on {% data variables.enterprise.data_residency_site %} are in {% data variables.release-phases.public_preview %} and are available in all {% data variables.enterprise.data_residency %} regions. +{% data variables.product.prodname_github_codespaces %} on {% data variables.enterprise.data_residency_site %} is available in all {% data variables.enterprise.data_residency %} regions. To use {% data variables.product.prodname_github_codespaces %} from {% data variables.product.prodname_vscode_shortname %} desktop with an enterprise on {% data variables.enterprise.data_residency_site %}, you must configure the `Github-enterprise: Uri` and `Github > Codespaces: Auth Provider` settings. For more information, see [AUTOTITLE](/codespaces/developing-in-a-codespace/using-github-codespaces-in-visual-studio-code#connecting-to-an-enterprise-on-ghecom). From 716d940c460b2b81a105feaa3a36c704ba794362 Mon Sep 17 00:00:00 2001 From: docs-bot <77750099+docs-bot@users.noreply.github.com> Date: Fri, 8 May 2026 01:31:45 -0700 Subject: [PATCH 2/5] docs: update copilot-cli content from source docs (#61016) Co-authored-by: github-actions[bot] Co-authored-by: hubwriter --- .../cli-command-reference.md | 99 ++++++++++++++++--- .../cli-config-dir-reference.md | 1 + src/content-pipelines/state/copilot-cli.sha | 2 +- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md index f39fd1ccca96..59eeb3b06f51 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-command-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-command-reference.md @@ -104,6 +104,7 @@ copilot completion fish > ~/.config/fish/completions/copilot.fish | Ctrl+V | Paste from clipboard as an attachment. | | Ctrl+X then `/` | After you have started typing a prompt, this allows you to run a slash command—for example, if you want to change the model without having to retype your prompt. | | Ctrl+X then `e` | Edit the prompt in an external editor (`$EDITOR`). | +| Ctrl+X then `b` | Promote the running task or shell command to the background. | | Ctrl+X then `o` | Open the most recent link from the timeline. | | Ctrl+Z | Suspend the process to the background (Unix). | | Shift+Enter or Option+Enter (Mac) / Alt+Enter (Windows/Linux) | Insert a newline in the input. | @@ -113,11 +114,36 @@ copilot completion fish > ~/.config/fish/completions/copilot.fish | Shortcut | Purpose | |------------------------------|---------------------------------------| +| Ctrl+F | Open timeline search. | | Ctrl+O | While there is nothing in the prompt input, this expands recent items in {% data variables.product.prodname_copilot_short %}'s response timeline to show more details. | | Ctrl+E | While there is nothing in the prompt input, this expands all items in {% data variables.product.prodname_copilot_short %}'s response timeline. | | Ctrl+T | Expand/collapse display of reasoning in responses. | | Page Up/Page Down | Scroll the timeline up or down by one page. | +## Session picker shortcuts + +When the session picker is open (opened via `/resume` or `--continue`): + +| Shortcut | Purpose | +|----------|---------| +| / | Move selection up or down. | +| Enter | Open the selected session. | +| `s` | Cycle the sort order: relevance → created → name → last used. | +| Tab | Switch between the local and remote tabs. | +| `d` | Delete the selected session. | +| Esc | Close the picker. | + +Sessions sort by the following modes: + +| Mode | Description | +|------|-------------| +| `relevance` | Scores sessions by match to the current working directory (default). | +| `last used` | Most recently modified sessions first. | +| `created` | Most recently created sessions first. | +| `name` | Alphabetical by session name; unnamed sessions sort to the end. | + +Sessions already open in another window float to the top in all non-relevance sort modes. When no working-directory context is available, the `relevance` mode is skipped. + ## Navigation shortcuts in the interactive interface | Shortcut | Purpose | @@ -147,6 +173,7 @@ copilot completion fish > ~/.config/fish/completions/copilot.fish | `/changelog [summarize] [VERSION\|last N\|since VERSION]`, `/release-notes [summarize] [VERSION\|last N\|since VERSION]` | Display the CLI changelog. Optionally specify a version, a count of recent releases, or a starting version. Add the keyword `summarize` for an AI-generated summary. | | `/chronicle ` | Session history tools and insights. {% data reusables.copilot.experimental %} | | `/clear [PROMPT]`, `/new [PROMPT]`, `/reset [PROMPT]` | Start a new conversation. | +| `/clikit [COMPONENT]` | Preview CLI business components (for example, quota info). | | `/compact` | Summarize the conversation history to reduce context window usage. See [AUTOTITLE](/copilot/concepts/agents/copilot-cli/context-management#compaction). | | `/context` | Show the context window token usage and visualization. See [AUTOTITLE](/copilot/concepts/agents/copilot-cli/context-management#checking-your-context-usage). | | `/copy` | Copy the last response to the clipboard. | @@ -163,7 +190,7 @@ copilot completion fish > ~/.config/fish/completions/copilot.fish | `/ide` | Connect to an IDE workspace. See [AUTOTITLE](/copilot/how-tos/copilot-cli/use-copilot-cli/connecting-vs-code#managing-the-connection-with-the-ide-slash-command). | | `/init` | Initialize {% data variables.product.prodname_copilot_short %} custom instructions and agentic features for this repository. See [Project initialization for {% data variables.product.prodname_copilot_short %}](#project-initialization-for-copilot). | | `/instructions` | View and toggle custom instruction files. | -| `/keep-alive [on\|busy\|NUMBERm\|NUMBERh]` | Prevent the machine from going to sleep: while a CLI session is active, while the agent is busy, or for a defined length of time. | +| `/keep-alive [on\|off\|busy\|DURATION]`, `/caffeinate [on\|off\|busy\|DURATION]` | Prevent the machine from going to sleep: while a CLI session is active, while the agent is busy, or for a defined length of time. Accepts durations like `30`, `30m`, `2h`, `1d` (bare numbers default to minutes). | | `/list-dirs` | Display all of the directories for which file access has been allowed. | | `/login` | Log in to {% data variables.product.prodname_copilot_short %}. | | `/logout` | Log out of {% data variables.product.prodname_copilot_short %}. | @@ -180,11 +207,12 @@ copilot completion fish > ~/.config/fish/completions/copilot.fish | `/restart` | Restart the CLI, preserving the current session. | | `/resume [SESSION-ID]`, `/continue [SESSION-ID]` | Switch to a different session by choosing from a list (optionally specify a session ID). | | `/review [PROMPT]` | Run the code review agent to analyze changes. See [AUTOTITLE](/copilot/how-tos/copilot-cli/use-copilot-cli/agentic-code-review). | +| `/search [QUERY]`, `/find [QUERY]` | Search the conversation timeline. {% data reusables.copilot.experimental %} | | `/session [info\|checkpoints [n]\|files\|plan\|rename [NAME]\|cleanup\|prune\|delete [ID]\|delete-all]`, `/sessions [info\|checkpoints [n]\|files\|plan\|rename [NAME]\|cleanup\|prune\|delete [ID]\|delete-all]` | Show session information and manage sessions. Subcommands: `info`, `checkpoints`, `files`, `plan`, `rename`, `cleanup`, `prune`, `delete`, `delete-all`. | | `/share [file\|html\|gist] [session\|research] [PATH]`, `/export [file\|html\|gist] [session\|research] [PATH]` | Share the session to a Markdown file, interactive HTML file, or {% data variables.product.github %} gist. | | `/skills [list\|info\|add\|remove\|reload] [ARGS...]` | Manage skills for enhanced capabilities. See [AUTOTITLE](/copilot/how-tos/copilot-cli/customize-copilot/create-skills). | | `/statusline`, `/footer` | Configure which items appear in the status line. | -| `/tasks` | View and manage background tasks (subagents and shell sessions). | +| `/tasks` | View and manage tasks (subagents and shell commands). | | `/terminal-setup` | Configure the terminal for multiline input support (Shift+Enter and Ctrl+Enter). | | `/theme [default\|dim\|high-contrast\|colorblind]` | View or set the color mode. | | `/undo`, `/rewind` | Rewind the last turn and revert file changes. | @@ -302,7 +330,6 @@ The `--available-tools` and `--excluded-tools` options support these values: | `ask_user` | Ask the user a question | | `glob` | Find files matching patterns | | `grep` (or `rg`) | Search for text in files | -| `show_file` | Present code snippets inline in the timeline. {% data reusables.copilot.experimental %} | | `skill` | Invoke custom skills | | `web_fetch` | Fetch and parse web content | @@ -354,6 +381,9 @@ copilot --allow-tool='MyMCP' | `COPILOT_SUBAGENT_MAX_DEPTH` | Maximum subagent nesting depth. Default: `6`. Range: `1`–`256`. | | `GH_HOST` | {% data variables.product.github %} hostname for both {% data variables.product.prodname_cli %} and {% data variables.copilot.copilot_cli_short %} (default: `github.com`). Set to your {% data variables.product.prodname_ghe_cloud %} with data residency hostname. Override with `COPILOT_GH_HOST` for {% data variables.copilot.copilot_cli_short %} only. | | `GH_TOKEN` | Authentication token. Takes precedence over `GITHUB_TOKEN`. | +| `GITHUB_COPILOT_PROMPT_MODE_EXTENSIONS` | Set to `true` to load project extensions and allow extension management tools in prompt mode (`-p`). Disabled by default to prevent running repository-controlled extension code without interactive trust. | +| `GITHUB_COPILOT_PROMPT_MODE_REPO_HOOKS` | Set to `true` to load repository hooks in prompt mode (`-p`). Disabled by default to prevent running repository-provided hook commands without interactive trust confirmation. | +| `GITHUB_COPILOT_PROMPT_MODE_WORKSPACE_MCP` | Set to `true` to load workspace MCP sources in prompt mode (`-p`). Disabled by default to prevent starting repository-controlled MCP servers without interactive trust. | | `GITHUB_TOKEN` | Authentication token. | | `PLAIN_DIFF` | Set to `true` to disable rich diff rendering. | | `USE_BUILTIN_RIPGREP` | Set to `false` to use the system ripgrep instead of the bundled version. | @@ -452,14 +482,40 @@ Use `copilot mcp` to manage MCP server configurations from the command line with | `tools` | Yes | Tools to enable. | | `headers` | No | HTTP headers. Supports variable expansion. | | `oauthClientId` | No | Static OAuth client ID (skips dynamic registration). | -| `oauthPublicClient` | No | Whether the OAuth client is public. Default: `true`. | -| `oidc` | No | Enable OIDC token injection. When `true`, injects a `GITHUB_COPILOT_OIDC_MCP_TOKEN` environment variable (local servers) or a `Bearer` `Authorization` header (remote servers). | +| `oauthPublicClient` | No | Whether the OAuth client is public. Default: `true`. Set to `false` for confidential clients with a stored secret. | +| `oauthGrantType` | No | OAuth grant type: `"authorization_code"` (default, browser-based flow) or `"client_credentials"` (fully headless, no browser or callback). | +| `oidc` | No | Enable OIDC token injection. When `true`, the CLI injects OIDC tokens for any `GITHUB_COPILOT_OIDC_MCP_TOKEN` or `GITHUB_COPILOT_OIDC_MCP_TOKEN_` variable referenced in the server's `env` block (local servers), or sends the token as a `Bearer` `Authorization` header (remote servers). For local servers, prefer suffixed variants (for example, `${GITHUB_COPILOT_OIDC_MCP_TOKEN_MY_SVC}`) to assign a unique variable name per server. | | `timeout` | No | Tool call timeout in milliseconds. | ### OAuth re-authentication Remote MCP servers that use OAuth may show a `needs-auth` status when a token expires or when a different account is required. Use `/mcp auth ` to trigger a fresh OAuth flow. This opens a browser authentication prompt, allowing you to sign in or switch accounts. After completing the flow, the server reconnects automatically. +### Headless OAuth (`client_credentials` grant) + +For CI or cron use cases where no browser is available, set `oauthGrantType: "client_credentials"`. This requires: + +* `oauthClientId`—the static client ID issued by the MCP provider. +* `oauthPublicClient: false`—the client is confidential. +* A `client_secret` stored in the system keychain (configured once via the `/mcp` UI or by writing to the OAuth credential store). + +When configured, the CLI skips the browser, callback server, PKCE, and dynamic client registration entirely. On every 401, it posts `grant_type=client_credentials` directly to the server's discovered token endpoint. + +```json +{ + "mcpServers": { + "headless-api": { + "type": "http", + "url": "https://api.example.com/mcp", + "tools": ["*"], + "oauthClientId": "YOUR-CLIENT-ID", + "oauthPublicClient": false, + "oauthGrantType": "client_credentials" + } + } +} +``` + ### Filter mapping Control how MCP tool output is processed using the `filterMapping` field in a server's configuration. @@ -487,6 +543,12 @@ Use `--disable-builtin-mcps` to disable all built-in servers, or `--disable-mcp- Server names can contain any printable characters, including spaces, Unicode characters, and punctuation. Control characters (U+0000–U+001F, U+007F) and the closing brace (`}`) are not allowed. Server names are used as prefixes for tool names—for example, a server named `my-server` produces tool names like `my-server-fetch`, and a server named `My Server` produces `My Server-fetch`. +### MCP tool name sanitization + +MCP server names and tool names are sanitized before being sent to the model. Characters that are invalid in tool names (anything other than `a-z`, `A-Z`, `0-9`, `-`, `_`) are replaced with `-`. Unicode characters are Punycode-encoded. The `@` symbol is also replaced with `-` to avoid conflicts with Punycode encoding. + +The combined name (`serverName-toolName`) is capped at 64 characters. When truncation would create a name collision, a numeric suffix is appended (for example, `my-server-tool2`, `my-server-tool3`) to ensure uniqueness. + ### MCP server trust levels MCP servers are loaded from multiple sources, each with a different trust level. @@ -585,7 +647,6 @@ Custom agents are specialized AI agents defined in Markdown files. The filename | Agent | Default model | Description | |-------|--------------|-------------| | `code-review` | claude-sonnet-4.5 | High signal-to-noise code review. Analyzes diffs for bugs, security issues, and logic errors. | -| `configure-copilot` | varies | Built-in sub-agent for managing {% data variables.copilot.copilot_cli_short %} configuration through natural language—adding MCP servers, installing agents, and managing skills. {% data reusables.copilot.experimental %} | | `explore` | claude-haiku-4.5 | Fast codebase exploration. Searches files, reads code, and answers questions. Returns focused answers under 300 words. Safe to run in parallel. | | `general-purpose` | claude-sonnet-4.5 | Full-capability agent for complex multi-step tasks. Runs in a separate context window. | | `research` | claude-sonnet-4.6 | Deep research agent. Generates a report based on information in your codebase, in relevant repositories, and on the web. | @@ -636,7 +697,17 @@ When the CLI prompts for permission to execute an operation, you can respond wit | `#` | Deny all similar requests for the rest of the session. | | `?` | Show detailed information about the request. | -Session approvals reset when you run `/clear` or start a new session. +When the full dialog is shown, you can also choose from these options: + +| Option | Scope | Persistence | +|--------|-------|-------------| +| Once | Single use | None | +| This location | Until manually cleared | Saved to disk per location | +| Always | Permanent | Config file | + +The **This location** option appears when the CLI can determine a location key (git root or current directory). It persists the approval to disk so the same permission is automatically granted the next time you work in that directory without prompting again. + +Use `/permissions reset` to clear in-memory approvals for the current session. ## OpenTelemetry monitoring @@ -678,11 +749,12 @@ Wraps the entire agent invocation: all LLM calls and tool executions for one use |-----------|-------------|-----------| | `gen_ai.operation.name` | `invoke_agent` | Both | | `gen_ai.provider.name` | Provider (for example, `github`, `anthropic`) | Both | -| `gen_ai.agent.id` | Session identifier | Both | +| `gen_ai.agent.id` | Stable agent-definition identifier when known; top-level default uses `github.copilot.default` | Both | | `gen_ai.agent.name` | Agent name (when available) | Both | | `gen_ai.agent.description` | Agent description (when available) | Both | -| `gen_ai.agent.version` | Runtime version | Both | +| `gen_ai.agent.version` | Agent definition version when known; otherwise runtime version | Both | | `gen_ai.conversation.id` | Session identifier | Both | +| `enduser.pseudo.id` | Pseudonymous Copilot user identifier from `analytics_tracking_id` when available | Both | | `gen_ai.request.model` | Requested model | Both | | `gen_ai.response.finish_reasons` | `["stop"]` or `["error"]` | Both | | `gen_ai.usage.input_tokens` | Total input tokens (all turns) | Both | @@ -709,21 +781,22 @@ One span per LLM request. Span kind: `CLIENT`. | `gen_ai.operation.name` | `chat` | | `gen_ai.provider.name` | Provider name | | `gen_ai.request.model` | Requested model | +| `gen_ai.request.stream` | Whether streaming mode was used (streaming only) | | `gen_ai.conversation.id` | Session identifier | +| `gen_ai.response.finish_reasons` | Stop reasons | | `gen_ai.response.id` | Response ID | | `gen_ai.response.model` | Resolved model | -| `gen_ai.response.finish_reasons` | Stop reasons | +| `gen_ai.response.time_to_first_chunk` | Time to first streaming chunk, in seconds (streaming only) | +| `gen_ai.usage.cache_creation.input_tokens` | Cached tokens created | +| `gen_ai.usage.cache_read.input_tokens` | Cached tokens read | | `gen_ai.usage.input_tokens` | Input tokens this turn | | `gen_ai.usage.output_tokens` | Output tokens this turn | -| `gen_ai.usage.cache_read.input_tokens` | Cached tokens read | -| `gen_ai.usage.cache_creation.input_tokens` | Cached tokens created | | `github.copilot.cost` | Turn cost | | `github.copilot.aiu` | AI units consumed this turn | | `github.copilot.server_duration` | Server-side duration | | `github.copilot.initiator` | Request initiator | | `github.copilot.turn_id` | Turn identifier | | `github.copilot.interaction_id` | Interaction identifier | -| `github.copilot.time_to_first_chunk` | Time to first streaming chunk, in seconds (streaming only) | | `server.address` | Server hostname | | `server.port` | Server port | | `error.type` | Error class name (on error) | diff --git a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md index 197a3c04bca8..c6344db831de 100644 --- a/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md +++ b/content/copilot/reference/copilot-cli-reference/cli-config-dir-reference.md @@ -225,6 +225,7 @@ These settings apply across all your sessions and repositories. You can edit thi | `ide.autoConnect` | `boolean` | `true` | Automatically connect to an IDE workspace on startup. When `false`, you can still connect manually using the `/ide` command. | | `ide.openDiffOnEdit` | `boolean` | `true` | Open file edit diffs in the connected IDE for approval. When `false`, file edit approvals are shown only in the terminal. | | `includeCoAuthoredBy` | `boolean` | `true` | Add a `Co-authored-by` trailer to git commits made by the agent. | +| `keepAlive` | `"on"` \| `"off"` \| `"busy"` | `"off"` | Keep-alive mode applied at CLI startup. `"on"` always prevents the system from sleeping, `"busy"` prevents sleeping only while the agent is running, and `"off"` disables keep-alive. Also configurable with the `/keep-alive` slash command. | | `logLevel` | `"none"` \| `"error"` \| `"warning"` \| `"info"` \| `"debug"` \| `"all"` \| `"default"` | `"default"` | Logging verbosity. | | `mergeStrategy` | `"rebase"` \| `"merge"` | — | Conflict resolution strategy for `/pr fix conflicts`. When set to `"rebase"`, conflicts are resolved by rebasing onto the base branch. When set to `"merge"`, the base branch is merged into the feature branch. If not configured, a picker dialog is shown. | | `model` | `string` | varies | AI model to use. Set to `"auto"` to let {% data variables.product.prodname_copilot_short %} pick the best available model automatically. Managed by the `/model` slash command. | diff --git a/src/content-pipelines/state/copilot-cli.sha b/src/content-pipelines/state/copilot-cli.sha index 6e0c0a59647f..35c06a62d26c 100644 --- a/src/content-pipelines/state/copilot-cli.sha +++ b/src/content-pipelines/state/copilot-cli.sha @@ -1 +1 @@ -1a74df9f2a81e052650047d18b97fcf82300ba79 +391259f70310fc987767077c68d29b5e38b28263 From b7e563b280904b41d198c831b8fe1f6f675bc131 Mon Sep 17 00:00:00 2001 From: mc <42146119+mchammer01@users.noreply.github.com> Date: Fri, 8 May 2026 10:03:18 +0100 Subject: [PATCH 3/5] Overhaul the "Support patterns" article (#60991) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Joe Clark <31087804+jc-clark@users.noreply.github.com> Co-authored-by: Sarah Schneider --- .github/workflows/headless-tests.yml | 1 + .../supported-secret-scanning-patterns.md | 88 ++--- data/ui.yml | 26 ++ .../secret-scanning-transformer.ts | 37 +- .../components/AutomatedPage.tsx | 87 +++-- .../components/AutomatedPageContext.tsx | 2 + .../fixtures/content/code-security/index.md | 1 + .../content/code-security/reference/index.md | 11 + .../reference/secret-security/index.md | 11 + .../supported-secret-scanning-patterns.md | 15 + src/fixtures/fixtures/data/ui.yml | 26 ++ .../tests/playwright-secret-scanning.spec.ts | 121 +++++++ .../supported-secret-scanning-patterns.tsx | 4 + .../components/SecretScanningTable.tsx | 337 ++++++++++++++++++ src/secret-scanning/pages/api/patterns.ts | 24 ++ .../supported-secret-scanning-patterns.tsx | 54 +++ 16 files changed, 750 insertions(+), 95 deletions(-) create mode 100644 src/fixtures/fixtures/content/code-security/reference/index.md create mode 100644 src/fixtures/fixtures/content/code-security/reference/secret-security/index.md create mode 100644 src/fixtures/fixtures/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md create mode 100644 src/fixtures/tests/playwright-secret-scanning.spec.ts create mode 100644 src/pages/[versionId]/code-security/reference/secret-security/supported-secret-scanning-patterns.tsx create mode 100644 src/secret-scanning/components/SecretScanningTable.tsx create mode 100644 src/secret-scanning/pages/api/patterns.ts create mode 100644 src/secret-scanning/pages/supported-secret-scanning-patterns.tsx diff --git a/.github/workflows/headless-tests.yml b/.github/workflows/headless-tests.yml index 24414ffa3d69..aa0455cfb49c 100644 --- a/.github/workflows/headless-tests.yml +++ b/.github/workflows/headless-tests.yml @@ -33,6 +33,7 @@ jobs: node: - playwright-rendering - playwright-a11y + - playwright-secret-scanning fail-fast: false timeout-minutes: 60 steps: diff --git a/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md b/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md index bc597a2905d5..76e204c9649a 100644 --- a/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md +++ b/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md @@ -10,7 +10,6 @@ redirect_from: - /code-security/secret-scanning/secret-scanning-partners - /code-security/secret-scanning/secret-scanning-patterns - /code-security/secret-scanning/introduction/supported-secret-scanning-patterns -layout: inline shortTitle: Supported patterns autogenerated: secret-scanning contentType: reference @@ -24,32 +23,33 @@ category: For in-depth information about each alert type, see [AUTOTITLE](/code-security/secret-scanning/managing-alerts-from-secret-scanning/about-alerts). -For details about all the supported patterns, see the [Supported secrets](#supported-secrets) section below. - If you use the REST API for {% data variables.product.prodname_secret_scanning %}, you can use the `Secret type` to report on secrets from specific issuers. For more information, see [AUTOTITLE](/enterprise-cloud@latest/rest/secret-scanning). -If you believe that {% data variables.product.prodname_secret_scanning %} should have detected a secret committed to your repository, and it has not, you first need to check that {% data variables.product.prodname_dotcom %} supports your secret. For more information, refer to the following sections. For more advanced troubleshooting information, see [AUTOTITLE](/code-security/secret-scanning/troubleshooting-secret-scanning-and-push-protection/troubleshooting-secret-scanning). +### Pattern categories + +| Category | Description | Detection approach | Example | +|----------|-------------|-------------------|---------| +| **Generic** | Secrets not tied to a specific provider, such as private keys and database connection strings | Regex-based | `rsa_private_key` | +| **AI-detected** | Generic passwords detected by {% data variables.secret-scanning.copilot-secret-scanning %} using AI models | AI-based | `password` | +| **Provider** | Secrets tied to a specific service provider (such as AWS, Azure, Stripe) | Regex-based | `aws_access_key_id` | -## Supported secrets +### Capabilities by category -The tables list the secrets supported by {% data variables.product.prodname_secret_scanning %} for each secret type. Information in the tables may include this data: +| Capability | Generic patterns | AI-detected | Provider patterns | +|------------|:-:|:-:|:-:| +| User alerts | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | {% octicon "check" aria-label="Supported" %} | +| Partner notifications | {% octicon "x" aria-label="Not supported" %} | {% octicon "x" aria-label="Not supported" %} | {% octicon "check" aria-label="Supported" %} (if partner) | +| Push protection (default) | {% octicon "x" aria-label="Not supported" %} | {% octicon "x" aria-label="Not supported" %} | {% octicon "check" aria-label="Supported" %} (most) | +| Push protection (configurable) | {% octicon "check" aria-label="Supported" %} | {% octicon "x" aria-label="Not supported" %} | Some | +| Validity checks | {% octicon "x" aria-label="Not supported" %} | {% octicon "x" aria-label="Not supported" %} | Some | +| Extended metadata | {% octicon "x" aria-label="Not supported" %} | {% octicon "x" aria-label="Not supported" %} | Some | +| Base64 format support | {% octicon "x" aria-label="Not supported" %} | {% octicon "x" aria-label="Not supported" %} | Some | -* **Provider:** Name of the token provider.{% ifversion fpt or ghec %} -* **Partner:** Token for which leaks are reported to the relevant token partner. Applies to public repositories and all gists, including secret gists. Secret gists are not private and can be accessed by anyone with the URL. See [About gists](/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists#about-gists). -* **User:** Token for which leaks are reported to users on {% data variables.product.prodname_dotcom %}. - * Applies to public repositories, and to private repositories where {% data variables.product.prodname_GH_secret_protection %} and {% data variables.product.prodname_secret_scanning %} are enabled. - * Includes {% ifversion secret-scanning-alert-experimental-list %}default{% else %}high confidence{% endif %} tokens, which relate to supported patterns and specified custom patterns, as well as non-provider tokens such as private keys, which usually have a higher ratio of false positives. - * For {% data variables.product.prodname_secret_scanning %} to scan for non-provider patterns, the detection of non-provider patterns must be enabled for the repository or the organization. For more information, see [AUTOTITLE](/code-security/secret-scanning/enabling-secret-scanning-features/enabling-secret-scanning-for-your-repository). - {% data reusables.secret-scanning.non-provider-patterns-beta %}{% endif %}{% ifversion ghes %} -* **{% data variables.product.prodname_secret_scanning_caps %} alert:** Token for which leaks are reported to users on {% data variables.product.prodname_dotcom %}. - * Applies to private repositories where {% data variables.product.prodname_GH_secret_protection %} and {% data variables.product.prodname_secret_scanning %} are enabled. - * Includes {% ifversion secret-scanning-alert-experimental-list %}default{% else %}high confidence{% endif %} tokens, which relate to supported patterns and specified custom patterns, as well as non-provider tokens such as private keys, which often result in false positives.{% endif %} -* **Push protection:** Token for which leaks are reported to users on {% data variables.product.prodname_dotcom %}. Applies to repositories with {% data variables.product.prodname_secret_scanning %} and push protection enabled. -* **Validity check:** Token for which a validity check is implemented. {% ifversion secret-scanning-validity-check-partner-patterns %}For partner tokens, {% data variables.product.prodname_dotcom %} sends the token to the relevant partner. Note that not all partners are based in the United States. For more information, see [{% data variables.product.prodname_AS %}](/free-pro-team@latest/site-policy/github-terms/github-terms-for-additional-products-and-features#advanced-security) in the Site Policy documentation.{% else %} Currently only applies to {% data variables.product.prodname_dotcom %} tokens.{% endif %} -* **Metadata check:** Token for which extended metadata is available, providing additional context about the detected secret. -* **Base64:** Token for which Base64-encoded versions are supported. +>[! NOTE] Validity and extended metadata checks are only available to users with {% data variables.product.prodname_team %} or {% data variables.product.prodname_enterprise %} who enable the feature as part of {% data variables.product.prodname_GH_secret_protection %}. -### Non-provider patterns +## Supported generic patterns + + {% data reusables.secret-scanning.non-provider-patterns-beta %} @@ -96,11 +96,11 @@ Precision levels are estimated based on the pattern type's typical false positiv {% endif %} >[!NOTE] -> Validity checks are **not supported** for non-provider patterns. +> Validity checks are **not supported** for generic/ non-provider patterns. {% ifversion secret-scanning-ai-generic-secret-detection %} -### {% data variables.secret-scanning.copilot-secret-scanning %} +## Supported AI-detected patterns {% data variables.product.prodname_secret_scanning_caps %} uses {% data variables.product.prodname_copilot_short %} to detect generic passwords. See [AUTOTITLE](/code-security/secret-scanning/copilot-secret-scanning/responsible-ai-generic-secrets). @@ -111,44 +111,14 @@ Precision levels are estimated based on the pattern type's typical false positiv >[!NOTE] Push protection and validity checks are not supported for passwords. {% endif %} -### {% ifversion secret-scanning-alert-experimental-list %}Default{% else %}High confidence{% endif %} patterns - - -{% ifversion fpt or ghec %} - -> [!NOTE] -> Validity and extended metadata checks are only available to users with {% data variables.product.prodname_team %} or {% data variables.product.prodname_enterprise %} who enable the feature as part of {% data variables.product.prodname_GH_secret_protection %}. - -| Provider | Token | Partner | User | Push protection | Validity check | Metadata check | Base64 | -|----|:----|:----:|:----:|:----:|:----:|:----:|:----:| -{%- for entry in secretScanningData %} -| {{ entry.provider }} | {{ entry.secretType }} | {% if entry.isPublic %}{% else %}{% endif %} | {% if entry.isPrivateWithGhas %}{% else %}{% endif %} | {% if entry.hasPushProtection %}{% else %}{% endif %} | {% if entry.hasValidityCheck %}{% else %}{% endif %} | {% if entry.hasExtendedMetadata %}{% else %}{% endif %} | {% if entry.base64Supported %}{% else %}{% endif %} | -{%- endfor %} - -{% endif %} - - -{% ifversion ghes %} - -| Provider | Token | {% data variables.product.prodname_secret_scanning_caps %} alert | Push protection | Validity check | Base64 | -|----|:----|:----:|:----:|:----:|:----:| -{%- for entry in secretScanningData %} -| {{ entry.provider }} | {{ entry.secretType }} | {% if entry.isPrivateWithGhas %}{% else %}{% endif %} | {% if entry.hasPushProtection %}{% else %}{% endif %} | {% if entry.hasValidityCheck %}{% else %}{% endif %} | {% if entry.base64Supported %}{% else %}{% endif %} | -{%- endfor %} - -{% endif %} +## Supported provider patterns -#### Token versions +Use the table below to search, filter, and browse all supported patterns. You can filter by provider name, push protection support, validity checks, and more. -Service providers update the patterns used to generate tokens periodically and may support more than one version of a token. Push protection only supports the most recent token versions that {% data variables.product.prodname_secret_scanning %} can identify with confidence. This avoids push protection blocking commits unnecessarily when a result may be a false positive, which is more likely to happen with legacy tokens. - -## Further reading +> [!NOTE] Service providers update the patterns used to generate tokens periodically and may support more than one version of a token. Push protection only supports the most recent token versions that {% data variables.product.prodname_secret_scanning %} can identify with confidence. This avoids push protection blocking commits unnecessarily when a result may be a false positive, which is more likely to happen with legacy tokens. -* [AUTOTITLE](/code-security/secret-scanning/managing-alerts-from-secret-scanning/about-alerts) -{%- ifversion fpt or ghec %} -* [AUTOTITLE](/code-security/secret-scanning/secret-scanning-partnership-program/secret-scanning-partner-program) -{%- endif %} -* [AUTOTITLE](/code-security/getting-started/securing-your-repository) -* [AUTOTITLE](/authentication/keeping-your-account-and-data-secure) + diff --git a/data/ui.yml b/data/ui.yml index 1eae9b4d28f0..f6dc19214e50 100644 --- a/data/ui.yml +++ b/data/ui.yml @@ -198,6 +198,32 @@ rest_reference: notes: Notes parameters: Parameters for "{{ RESTOperationTitle }}" response: Response +secret_scanning: + search_placeholder: Search by provider or secret name... + search_aria_label: Search patterns + filter_aria_label: Filter secret scanning patterns + filter_push_protection: 'Push protection' + filter_validity_check: 'Validity check' + filter_partner_alert: 'Partner alert' + filter_metadata: Metadata check + filter_base64: Base64 + filter_all: All + filter_yes: 'Yes' + filter_no: 'No' + showing_patterns: 'Showing {filtered} of {total} patterns' + column_provider: Provider + column_secret: Secret + column_partner: Partner + column_user_alert: User alert + column_push_protection: Push protection + column_validity_check: Validity check + column_metadata: Metadata check + column_base64: Base64 + bool_yes: 'Yes' + bool_no: 'No' + token_versions: Token versions + table_title: Supported patterns + pagination_label: Secret scanning patterns pagination request_example: Request example request_examples: Request examples example_response: Example response diff --git a/src/article-api/transformers/secret-scanning-transformer.ts b/src/article-api/transformers/secret-scanning-transformer.ts index 10789967ccf4..46bacb93686f 100644 --- a/src/article-api/transformers/secret-scanning-transformer.ts +++ b/src/article-api/transformers/secret-scanning-transformer.ts @@ -66,6 +66,34 @@ export class SecretScanningTransformer implements PageTransformer { context.markdownRequested = true let content = await page.render(context) + // Inject the full patterns table for agent/crawler access + // (The React DataTable is not rendered in markdown mode) + if (context.secretScanningData && context.secretScanningData.length > 0) { + const bool = (v: unknown) => (v ? '✓' : '✗') + const escape = (s: string) => s.replace(/\\/g, '\\\\').replace(/\|/g, '\\|') + // Strip HTML from secretType before inserting into markdown table rows. + // The isduplicate logic above appends
HTML which would break + // single-line markdown table rows once
is later converted to \n. + const cleanSecretType = (s: string) => + s + .replace( + /
Token versions<\/a>/, + ', [Token versions](#token-versions)', + ) + .replace(//gi, ', ') + .replace(/]*>([^<]*)<\/a>/gi, '[$2]($1)') + .replace(/<[^>]+>/g, '') + const header = + '| Provider | Secret | Secret type | Partner | User alert | Push protection | Validity check | Metadata | Base64 |' + const separator = '| --- | --- | --- | :---: | :---: | :---: | :---: | :---: | :---: |' + const rows = context.secretScanningData.map( + (entry: Record) => + `| ${escape(String(entry.provider))} | ${escape(String(entry.supportedSecret))} | ${escape(cleanSecretType(String(entry.secretType)))} | ${bool(entry.isPublic)} | ${bool(entry.isPrivateWithGhas)} | ${bool(entry.hasPushProtection)} | ${bool(entry.hasValidityCheck)} | ${bool(entry.hasExtendedMetadata)} | ${bool(entry.base64Supported)} |`, + ) + const table = ['\n\n## Supported patterns\n', header, separator, ...rows].join('\n') + content += table + } + // Strip HTML comments from the rendered content content = content.replace(//gs, '') @@ -75,11 +103,16 @@ export class SecretScanningTransformer implements PageTransformer { // Convert
tags to newlines and
text to markdown links content = content.replace(//gi, '\n') content = content.replace(/]*>([^<]*)<\/a>/gi, '[$2]($1)') - // Strip any remaining HTML tags (loop to handle nested/malformed tags) + // Strip any remaining HTML tags. Loop until stable to handle nested or + // malformed tags (e.g. "ipt>"). Limit iterations to prevent + // infinite loops on pathological input. let previous = '' - while (content !== previous) { + let iterations = 0 + const MAX_STRIP_ITERATIONS = 10 + while (content !== previous && iterations < MAX_STRIP_ITERATIONS) { previous = content content = content.replace(/<[^>]+>/g, '') + iterations++ } // Normalize whitespace after stripping comments diff --git a/src/automated-pipelines/components/AutomatedPage.tsx b/src/automated-pipelines/components/AutomatedPage.tsx index be7bcab209ea..9d0b74d8d7ad 100644 --- a/src/automated-pipelines/components/AutomatedPage.tsx +++ b/src/automated-pipelines/components/AutomatedPage.tsx @@ -5,56 +5,75 @@ import { MarkdownContent } from '@/frame/components/ui/MarkdownContent' import { Lead } from '@/frame/components/ui/Lead' import { PermissionsStatement } from '@/frame/components/ui/PermissionsStatement' import { ArticleGridLayout } from '@/frame/components/article/ArticleGridLayout' +import { ArticleInlineLayout } from '@/frame/components/article/ArticleInlineLayout' import { MiniTocs } from '@/frame/components/ui/MiniTocs' import { useAutomatedPageContext } from '@/automated-pipelines/components/AutomatedPageContext' import { ClientSideHighlight } from '@/frame/components/ClientSideHighlight' import { Breadcrumbs } from '@/frame/components/page-header/Breadcrumbs' type Props = { - children: React.ReactNode + children?: React.ReactNode + rawChildren?: React.ReactNode fullWidth?: boolean } -export const AutomatedPage = ({ children, fullWidth }: Props) => { - const { title, intro, renderedPage, miniTocItems, product, permissions } = +export const AutomatedPage = ({ children, rawChildren, fullWidth }: Props) => { + const { title, intro, renderedPage, miniTocItems, product, permissions, currentLayout } = useAutomatedPageContext() + const articleContents = ( +
+ {renderedPage && {renderedPage}} + {children && {children}} + {rawChildren &&
{rawChildren}
} +
+ ) + + const introProp = ( + <> + {intro && ( + + {intro} + + )} + + + ) + + const toc = miniTocItems.length > 1 ? : undefined + return ( -
- -
- -
- {title} - - } - intro={ - <> - {intro && ( - - {intro} - - )} - - - - } - toc={miniTocItems.length > 1 && } + {currentLayout === 'inline' ? ( + {title}} + intro={introProp} + toc={toc} + breadcrumbs={} > -
- {renderedPage && ( - {renderedPage} - )} - {children && {children}} -
-
-
+ {articleContents} + + ) : ( +
+ +
+ +
+ {title} + + } + intro={introProp} + toc={toc} + > + {articleContents} +
+
+ )}
) } diff --git a/src/automated-pipelines/components/AutomatedPageContext.tsx b/src/automated-pipelines/components/AutomatedPageContext.tsx index 7f567ae55cd0..8776a580444a 100644 --- a/src/automated-pipelines/components/AutomatedPageContext.tsx +++ b/src/automated-pipelines/components/AutomatedPageContext.tsx @@ -11,6 +11,7 @@ export type AutomatedPageContextT = { miniTocItems: Array product?: string permissions?: string + currentLayout?: string } export const AutomatedPageContext = createContext(null) @@ -57,5 +58,6 @@ export const getAutomatedPageContextFromRequest = ( miniTocItems, product: page.product ?? '', permissions: page.permissions ?? page.rawPermissions ?? '', + currentLayout: context.currentLayoutName ?? 'default', } } diff --git a/src/fixtures/fixtures/content/code-security/index.md b/src/fixtures/fixtures/content/code-security/index.md index 125dfdb3ccd6..e329b17d28b1 100644 --- a/src/fixtures/fixtures/content/code-security/index.md +++ b/src/fixtures/fixtures/content/code-security/index.md @@ -15,4 +15,5 @@ children: - /getting-started - /secret-scanning - /codeql-cli + - /reference --- diff --git a/src/fixtures/fixtures/content/code-security/reference/index.md b/src/fixtures/fixtures/content/code-security/reference/index.md new file mode 100644 index 000000000000..b4f4a8ebe680 --- /dev/null +++ b/src/fixtures/fixtures/content/code-security/reference/index.md @@ -0,0 +1,11 @@ +--- +title: Reference +shortTitle: Reference +intro: Reference documentation for code security. +versions: + fpt: '*' + ghes: '*' + ghec: '*' +children: + - /secret-security +--- diff --git a/src/fixtures/fixtures/content/code-security/reference/secret-security/index.md b/src/fixtures/fixtures/content/code-security/reference/secret-security/index.md new file mode 100644 index 000000000000..f38467b7207f --- /dev/null +++ b/src/fixtures/fixtures/content/code-security/reference/secret-security/index.md @@ -0,0 +1,11 @@ +--- +title: Secret security reference +shortTitle: Secret security +intro: Reference documentation for secret security. +versions: + fpt: '*' + ghes: '*' + ghec: '*' +children: + - /supported-secret-scanning-patterns +--- diff --git a/src/fixtures/fixtures/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md b/src/fixtures/fixtures/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md new file mode 100644 index 000000000000..6e8276470c5e --- /dev/null +++ b/src/fixtures/fixtures/content/code-security/reference/secret-security/supported-secret-scanning-patterns.md @@ -0,0 +1,15 @@ +--- +title: Supported secret scanning patterns +intro: Lists of supported secrets. +versions: + fpt: '*' + ghes: '*' + ghec: '*' +contentType: reference +autogenerated: secret-scanning +layout: inline +--- + +## Supported secrets + +This table lists the secrets supported by secret scanning. diff --git a/src/fixtures/fixtures/data/ui.yml b/src/fixtures/fixtures/data/ui.yml index 1eae9b4d28f0..f6dc19214e50 100644 --- a/src/fixtures/fixtures/data/ui.yml +++ b/src/fixtures/fixtures/data/ui.yml @@ -198,6 +198,32 @@ rest_reference: notes: Notes parameters: Parameters for "{{ RESTOperationTitle }}" response: Response +secret_scanning: + search_placeholder: Search by provider or secret name... + search_aria_label: Search patterns + filter_aria_label: Filter secret scanning patterns + filter_push_protection: 'Push protection' + filter_validity_check: 'Validity check' + filter_partner_alert: 'Partner alert' + filter_metadata: Metadata check + filter_base64: Base64 + filter_all: All + filter_yes: 'Yes' + filter_no: 'No' + showing_patterns: 'Showing {filtered} of {total} patterns' + column_provider: Provider + column_secret: Secret + column_partner: Partner + column_user_alert: User alert + column_push_protection: Push protection + column_validity_check: Validity check + column_metadata: Metadata check + column_base64: Base64 + bool_yes: 'Yes' + bool_no: 'No' + token_versions: Token versions + table_title: Supported patterns + pagination_label: Secret scanning patterns pagination request_example: Request example request_examples: Request examples example_response: Example response diff --git a/src/fixtures/tests/playwright-secret-scanning.spec.ts b/src/fixtures/tests/playwright-secret-scanning.spec.ts new file mode 100644 index 000000000000..a7a190190f32 --- /dev/null +++ b/src/fixtures/tests/playwright-secret-scanning.spec.ts @@ -0,0 +1,121 @@ +import { test, expect } from '@playwright/test' + +const PAGE_PATH = '/code-security/reference/secret-security/supported-secret-scanning-patterns' + +test.describe('Secret scanning DataTable accessibility', () => { + test('table has an accessible name via aria-labelledby', async ({ page }) => { + await page.goto(PAGE_PATH) + + const table = page.getByRole('table') + await expect(table).toBeVisible() + + // The table should be labelled by the Table.Title heading + const labelledBy = await table.getAttribute('aria-labelledby') + expect(labelledBy).toBeTruthy() + + // The referenced element should exist and contain text + const titleEl = page.locator(`#${labelledBy}`) + await expect(titleEl).toBeVisible() + await expect(titleEl).not.toBeEmpty() + }) + + test('heading hierarchy does not skip levels within main content', async ({ page }) => { + await page.goto(PAGE_PATH) + + // Scope to main content area — nav/sidebar/footer may have their own heading structure + const main = page.locator('main, article, [role="main"]').first() + const headings = await main.locator('h1, h2, h3, h4, h5, h6').all() + expect(headings.length).toBeGreaterThan(0) + + let previousLevel = 0 + for (const heading of headings) { + const tagName = await heading.evaluate((el) => el.tagName.toLowerCase()) + const level = parseInt(tagName.replace('h', ''), 10) + // Level can go up (same or smaller number) freely, but going deeper + // should never skip more than one level + if (level > previousLevel) { + expect(level - previousLevel).toBeLessThanOrEqual(1) + } + previousLevel = level + } + }) + + test('all interactive controls have accessible names', async ({ page }) => { + await page.goto(PAGE_PATH) + + // Search input — Primer TextInput renders as input[type="text"] with role "textbox" + const searchInput = page.locator('[role="search"] input') + await expect(searchInput).toBeVisible() + const searchLabel = + (await searchInput.getAttribute('aria-label')) || + (await searchInput.getAttribute('placeholder')) + expect(searchLabel).toBeTruthy() + + // Filter buttons (ActionMenu triggers) + const buttons = page.locator('[role="search"] button') + const buttonCount = await buttons.count() + expect(buttonCount).toBeGreaterThan(0) + for (let i = 0; i < buttonCount; i++) { + const btn = buttons.nth(i) + const name = (await btn.getAttribute('aria-label')) || (await btn.textContent())?.trim() || '' + expect(name.length).toBeGreaterThan(0) + } + + // Pagination (if present) + const pagination = page.getByRole('navigation', { name: /pagination/i }) + if ((await pagination.count()) > 0) { + await expect(pagination).toHaveAttribute('aria-label', /.+/) + } + }) + + test('provider column cells are row headers', async ({ page }) => { + await page.goto(PAGE_PATH) + + // Primer DataTable uses CSS grid layout — row headers are rendered as + // elements with role="rowheader" (via scope="row" on the cell) + const rowHeaders = page.locator('[role="rowheader"]') + const count = await rowHeaders.count() + expect(count).toBeGreaterThan(0) + }) + + test.describe('narrow viewport', () => { + test.use({ viewport: { width: 320, height: 256 } }) + + test('table content remains accessible via scrolling', async ({ page }) => { + await page.goto(PAGE_PATH) + + const table = page.getByRole('table') + await expect(table).toBeVisible() + + // At narrow viewports, the table should not be hidden or clipped. + // Content must remain reachable even if it overflows horizontally. + // Verify the table itself is not display:none or visibility:hidden + await expect(table).toBeVisible() + + // Verify data cells are present and accessible + const cells = page.locator('[role="rowheader"], [role="cell"]') + expect(await cells.count()).toBeGreaterThan(0) + + // The table's container should allow horizontal scrolling (overflow not hidden) + const overflowX = await table.evaluate((el) => { + const wrapper = el.closest('[class*="OverflowWrapper"]') || el.parentElement + return wrapper ? getComputedStyle(wrapper).overflowX : 'visible' + }) + expect(overflowX).not.toBe('hidden') + }) + }) + + test('color contrast meets 4.5:1 minimum', async ({ page }) => { + // This is primarily covered by the axe scan in playwright-a11y.spec.ts, + // but we include a targeted check here for the table specifically + const { default: AxeBuilder } = await import('@axe-core/playwright') + await page.goto(PAGE_PATH) + + const results = await new AxeBuilder({ page }) + .include('table') + .withRules(['color-contrast']) + .analyze() + + expect(results.violations).toEqual([]) + }) +}) diff --git a/src/pages/[versionId]/code-security/reference/secret-security/supported-secret-scanning-patterns.tsx b/src/pages/[versionId]/code-security/reference/secret-security/supported-secret-scanning-patterns.tsx new file mode 100644 index 000000000000..e4e49ea778b7 --- /dev/null +++ b/src/pages/[versionId]/code-security/reference/secret-security/supported-secret-scanning-patterns.tsx @@ -0,0 +1,4 @@ +export { + default, + getServerSideProps, +} from '@/secret-scanning/pages/supported-secret-scanning-patterns' diff --git a/src/secret-scanning/components/SecretScanningTable.tsx b/src/secret-scanning/components/SecretScanningTable.tsx new file mode 100644 index 000000000000..d942aa472c9d --- /dev/null +++ b/src/secret-scanning/components/SecretScanningTable.tsx @@ -0,0 +1,337 @@ +import React, { useState, useMemo } from 'react' +import { DataTable, Table } from '@primer/react/experimental' +import { TextInput, ActionMenu, ActionList, Pagination } from '@primer/react' +import { useTranslation } from '@/languages/components/useTranslation' +import type { SecretScanningData } from '@/types' + +const PAGE_SIZE = 25 + +type SecretScanningRow = SecretScanningData & { id: string } + +type FilterState = { + search: string + pushProtection: 'all' | 'yes' | 'no' + validityCheck: 'all' | 'yes' | 'no' + partnerAlert: 'all' | 'yes' | 'no' + metadata: 'all' | 'yes' | 'no' + base64: 'all' | 'yes' | 'no' +} + +export function SecretScanningTable({ data }: { data: SecretScanningData[] }) { + const { t } = useTranslation('secret_scanning') + const [filters, setFilters] = useState({ + search: '', + pushProtection: 'all', + validityCheck: 'all', + partnerAlert: 'all', + metadata: 'all', + base64: 'all', + }) + const [currentPage, setCurrentPage] = useState(1) + const [sortColumn, setSortColumn] = useState(undefined) + const [sortDirection, setSortDirection] = useState<'ASC' | 'DESC'>('ASC') + + // Add stable IDs once based on original data order + const dataWithIds: SecretScanningRow[] = useMemo(() => { + return data.map((entry, i) => ({ ...entry, id: `${entry.secretType}-${i}` })) + }, [data]) + + // Client-side filtering — fast because data is ~200-400 entries + const filtered: SecretScanningRow[] = useMemo(() => { + return dataWithIds.filter((entry) => { + // Text search across provider + secret type + if (filters.search) { + const q = filters.search.toLowerCase() + const match = + entry.provider.toLowerCase().includes(q) || + entry.supportedSecret.toLowerCase().includes(q) || + entry.secretType.toLowerCase().includes(q) + if (!match) return false + } + + // Boolean filters + if (filters.pushProtection === 'yes' && !entry.hasPushProtection) return false + if (filters.pushProtection === 'no' && entry.hasPushProtection) return false + if (filters.validityCheck === 'yes' && !entry.hasValidityCheck) return false + if (filters.validityCheck === 'no' && entry.hasValidityCheck) return false + if (filters.partnerAlert === 'yes' && !entry.isPublic) return false + if (filters.partnerAlert === 'no' && entry.isPublic) return false + if (filters.metadata === 'yes' && !entry.hasExtendedMetadata) return false + if (filters.metadata === 'no' && entry.hasExtendedMetadata) return false + if (filters.base64 === 'yes' && !entry.base64Supported) return false + if (filters.base64 === 'no' && entry.base64Supported) return false + + return true + }) + }, [dataWithIds, filters]) + + // Sort the full filtered dataset + const sorted: SecretScanningRow[] = useMemo(() => { + if (!sortColumn) return filtered + return [...filtered].sort((a, b) => { + const aVal = String((a as Record)[sortColumn] ?? '') + const bVal = String((b as Record)[sortColumn] ?? '') + const cmp = aVal.localeCompare(bVal, undefined, { numeric: true, sensitivity: 'base' }) + return sortDirection === 'ASC' ? cmp : -cmp + }) + }, [filtered, sortColumn, sortDirection]) + + // Paginate (Pagination component uses 1-indexed pages) + const pageCount = Math.ceil(sorted.length / PAGE_SIZE) + const pageData = sorted.slice((currentPage - 1) * PAGE_SIZE, currentPage * PAGE_SIZE) + + return ( +
+ {/* Filter bar */} +
+
+ { + setFilters((f) => ({ ...f, pushProtection: v })) + setCurrentPage(1) + }} + /> + { + setFilters((f) => ({ ...f, validityCheck: v })) + setCurrentPage(1) + }} + /> + { + setFilters((f) => ({ ...f, partnerAlert: v })) + setCurrentPage(1) + }} + /> + { + setFilters((f) => ({ ...f, metadata: v })) + setCurrentPage(1) + }} + /> + { + setFilters((f) => ({ ...f, base64: v })) + setCurrentPage(1) + }} + /> +
+ { + setFilters((f) => ({ ...f, search: e.target.value })) + setCurrentPage(1) + }} + /> +
+ + {/* Results count */} +

+ {t('showing_patterns') + .replace('{filtered}', String(filtered.length)) + .replace('{total}', String(data.length))} +

+ + {/* Data table */} +
+ + + {t('table_title')} + + { + setSortColumn(String(columnId)) + setSortDirection(direction) + setCurrentPage(1) + }} + columns={[ + { + header: t('column_provider'), + field: 'provider', + width: '140px', + rowHeader: true, + sortBy: 'alphanumeric', + renderCell: (row) => {row.provider}, + }, + { + header: t('column_secret'), + field: 'supportedSecret', + width: '280px', + renderCell: (row) => { + // The middleware appends HTML for duplicates; strip it. + // Also handle
and
separators in the raw secretType. + const cleanSecretType = row.secretType + .replace(/ Token versions<\/a>/, '') + .replace(/<\/?br\s*\/?>/gi, ', ') + .replace(/,\s*,/g, ',') + return ( + + ) + }, + }, + { + header: t('column_partner'), + field: 'isPublic', + renderCell: (row) => ( + + ), + }, + { + header: t('column_user_alert'), + field: 'isPrivateWithGhas', + renderCell: (row) => ( + + ), + }, + { + header: t('column_push_protection'), + field: 'hasPushProtection', + renderCell: (row) => ( + + ), + }, + { + header: t('column_validity_check'), + field: 'hasValidityCheck', + renderCell: (row) => ( + + ), + }, + { + header: t('column_metadata'), + field: 'hasExtendedMetadata', + renderCell: (row) => ( + + ), + }, + { + header: t('column_base64'), + field: 'base64Supported', + renderCell: (row) => ( + + ), + }, + ]} + /> +
+
+ + {/* Pagination */} + {pageCount > 1 && ( + setCurrentPage(page)} + /> + )} +
+ ) +} + +// Simple ✓/✗ icon with a11y labels +function BoolIcon({ + value, + label, + t, +}: { + value: boolean + label?: string + t: (key: string) => string +}) { + const text = value ? t('bool_yes') : t('bool_no') + const fullLabel = label ? `${label}: ${text}` : text + return ( + + {value ? '✓' : '✗'} + + ) +} + +// Reusable filter dropdown (all / yes / no) +function FilterDropdown({ + label, + value, + onChange, +}: { + label: string + value: 'all' | 'yes' | 'no' + onChange: (v: 'all' | 'yes' | 'no') => void +}) { + const { t } = useTranslation('secret_scanning') + return ( + + + {label}:{' '} + {value === 'all' ? t('filter_all') : value === 'yes' ? t('filter_yes') : t('filter_no')} + + + + {(['all', 'yes', 'no'] as const).map((opt) => ( + onChange(opt)}> + {opt === 'all' ? t('filter_all') : opt === 'yes' ? t('filter_yes') : t('filter_no')} + + ))} + + + + ) +} diff --git a/src/secret-scanning/pages/api/patterns.ts b/src/secret-scanning/pages/api/patterns.ts new file mode 100644 index 000000000000..b0a2c1b08d53 --- /dev/null +++ b/src/secret-scanning/pages/api/patterns.ts @@ -0,0 +1,24 @@ +import type { NextApiRequest, NextApiResponse } from 'next' +import { getSecretScanningData } from '@/secret-scanning/lib/get-secret-scanning-data' +import path from 'path' + +// Lightweight API: serves the pattern data as JSON +// The server does NO HTML rendering — just returns cached JSON +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + const version = (req.query.version as string) || 'fpt' + const filepath = path.join( + process.cwd(), + 'src/secret-scanning/data/pattern-docs', + version, + 'public-docs.yml', + ) + + try { + const data = await getSecretScanningData(filepath) + // Cache aggressively — data only changes on deploy + res.setHeader('Cache-Control', 'public, max-age=3600, s-maxage=86400') + res.json(data) + } catch { + res.status(404).json({ error: 'Version not found' }) + } +} diff --git a/src/secret-scanning/pages/supported-secret-scanning-patterns.tsx b/src/secret-scanning/pages/supported-secret-scanning-patterns.tsx new file mode 100644 index 000000000000..152c7bbcd4d5 --- /dev/null +++ b/src/secret-scanning/pages/supported-secret-scanning-patterns.tsx @@ -0,0 +1,54 @@ +import { GetServerSideProps } from 'next' + +import { + getMainContext, + MainContext, + MainContextT, + addUINamespaces, +} from '@/frame/components/context/MainContext' +import { + getAutomatedPageContextFromRequest, + AutomatedPageContext, + AutomatedPageContextT, +} from '@/automated-pipelines/components/AutomatedPageContext' +import { AutomatedPage } from '@/automated-pipelines/components/AutomatedPage' +import { SecretScanningTable } from '@/secret-scanning/components/SecretScanningTable' +import type { ExtendedRequest, SecretScanningData } from '@/types' + +type Props = { + mainContext: MainContextT + automatedPageContext: AutomatedPageContextT + patterns: SecretScanningData[] +} + +export default function SupportedSecretScanningPatterns({ + mainContext, + automatedPageContext, + patterns, +}: Props) { + return ( + + + } /> + + + ) +} + +export const getServerSideProps: GetServerSideProps = async (context) => { + const req = context.req as unknown as ExtendedRequest + const res = context.res as object + const mainContext = await getMainContext(req, res) + addUINamespaces(req, mainContext.data.ui, ['secret_scanning']) + const automatedPageContext = getAutomatedPageContextFromRequest(req) + + // The middleware already loads secretScanningData into req.context + const patterns = req.context?.secretScanningData ?? [] + return { + props: { + mainContext, + automatedPageContext, + patterns, + }, + } +} From a14134bb41e45567f2fc8dfc7b9d802b3600e69d Mon Sep 17 00:00:00 2001 From: Tim Rogers Date: Fri, 8 May 2026 10:22:07 +0100 Subject: [PATCH 4/5] More flexible secrets and variables for Copilot cloud agent [2026-05-06] (#60993) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- .../configure-secrets-and-variables.md | 83 +++++++++++++++++++ .../customize-the-agent-environment.md | 18 +--- .../extend-cloud-agent-with-mcp.md | 23 +---- .../customize-cloud-agent/index.md | 1 + .../reference/custom-agents-configuration.md | 4 +- .../responsible-use/copilot-cloud-agent.md | 2 +- .../tutorials/cloud-agent/build-guardrails.md | 2 +- .../cloud-agent/give-access-to-resources.md | 9 +- 8 files changed, 98 insertions(+), 44 deletions(-) create mode 100644 content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables.md diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables.md new file mode 100644 index 000000000000..6ee9c02f7c9a --- /dev/null +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables.md @@ -0,0 +1,83 @@ +--- +title: Configure secrets and variables for Copilot cloud agent +shortTitle: Configure secrets and variables +intro: 'Securely pass secrets and variables to {% data variables.copilot.copilot_cloud_agent %} so it can access private resources and configure MCP servers.' +versions: + feature: copilot +contentType: how-tos +category: + - Configure Copilot +--- + +## About secrets and variables for {% data variables.copilot.copilot_cloud_agent %} + +When you delegate a task to {% data variables.copilot.copilot_cloud_agent %}, it works in its own ephemeral development environment, powered by {% data variables.product.prodname_actions %}. You may want to pass secrets and variables to the agent to: + +* Give {% data variables.product.prodname_copilot_short %} access to private resources, such as internal package registries, when it builds, tests, or validates your code in the agent's environment. +* Configure MCP servers, by passing API keys, tokens, or other configuration to the servers. For more information, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/extend-cloud-agent-with-mcp). +* Set environment variables that are available to scripts and tools that {% data variables.product.prodname_copilot_short %} runs in its environment, including in `copilot-setup-steps.yml`. For more information, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment). + +{% data variables.copilot.copilot_cloud_agent %} has its own dedicated **Agents** secrets and variables, alongside the existing **Actions**, **Codespaces**, and **Dependabot** types. You can configure Agents secrets and variables at: + +* The **organization level**, so a single secret or variable can be shared across any or all repositories in your organization. +* The **repository level**, for configuration that only applies to a single repository. + +Variables and secrets that you configure are exposed to {% data variables.product.prodname_copilot_short %} as environment variables, except secrets and variables prefixed with `COPILOT_MCP_`, which are only available to MCP servers. + +> [!NOTE] +> If you previously configured secrets or variables in the `copilot` environment in a repository's {% data variables.product.prodname_actions %} settings, those secrets and variables have been automatically migrated to the new repository-level **Agents** type. You don't need to take any action, and you can manage them from the new location going forward. + +## Configuring repository-level secrets and variables + +You must be a repository administrator to configure Agents secrets and variables for a repository. + +{% data reusables.repositories.navigate-to-repo %} +{% data reusables.repositories.sidebar-settings %} +1. In the "Security" section of the sidebar, click **Secrets and variables**, then click **Agents**. +1. To add a secret, click the **Secrets** tab, then click **New repository secret**. To add a variable, click the **Variables** tab, then click **New repository variable**. +1. Fill in the "Name" and "Value" (or "Secret") fields, and then click **Add secret** or **Add variable**. + +## Configuring organization-level secrets and variables + +You must be an organization owner to configure Agents secrets and variables for an organization. + +{% data reusables.organizations.navigate-to-org %} +{% data reusables.organizations.org_settings %} +1. In the "Security" section of the sidebar, click **Secrets and variables**, then click **Agents**. +1. To add a secret, click the **Secrets** tab, then click **New organization secret**. To add a variable, click the **Variables** tab, then click **New organization variable**. +1. Fill in the "Name" and "Value" (or "Secret") fields. +1. Under "Repository access", choose which repositories in your organization can access the secret or variable: + + * **All repositories**: any repository in the organization can access the secret or variable. + * **Private repositories**: any private or internal repository in the organization can access the secret or variable. + * **Selected repositories**: only the repositories you specify can access the secret or variable. + +1. Click **Add secret** or **Add variable**. + +## Using secrets and variables + +Once configured, Agents secrets and variables are automatically available to {% data variables.copilot.copilot_cloud_agent %} when it works on a task in the repository. They are exposed to the agent as environment variables in its development environment, so they can be used by scripts and tools that {% data variables.product.prodname_copilot_short %} runs, including by your `copilot-setup-steps.yml` workflow. + +Secret values are masked in {% data variables.copilot.copilot_cloud_agent %} session logs. + +> [!NOTE] +> {% data variables.copilot.copilot_cloud_agent %} does not have access to {% data variables.product.prodname_actions %}, {% data variables.product.prodname_codespaces %}, or {% data variables.product.prodname_dependabot %} secrets and variables. Only Agents secrets and variables are passed to the agent. + +## Naming requirements for secrets and variables + +Names must: + +* Only contain alphanumeric characters (`[A-Z]`, `[0-9]`) or underscores (`_`). Spaces are not allowed. +* Not start with the `GITHUB_` prefix. +* Not start with a number. + +Names are case-insensitive. Lowercase letters are converted to uppercase. Names must be unique at the level at which they are created. + +If a variable or secret with the same name exists at multiple levels, the value at the lowest level takes precedence. For example, a repository-level secret will override an organization-level secret with the same name. + +For secrets and variables that you want to pass to MCP servers, the name must begin with the prefix `COPILOT_MCP_`. Only Agents secrets and variables with this prefix are available to your MCP configuration. For more information, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/extend-cloud-agent-with-mcp). + +## Further reading + +* [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment) +* [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/extend-cloud-agent-with-mcp) diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment.md index 17dab87bb29b..7d09af6d2980 100644 --- a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/customize-the-agent-environment.md @@ -31,7 +31,7 @@ You can customize {% data variables.product.prodname_copilot_short %}'s developm In addition, you can: -* [Set environment variables in {% data variables.product.prodname_copilot_short %}'s environment](#setting-environment-variables-in-copilots-environment) +* [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables) * [Disable or customize the agent's firewall](/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-cloud-agent). > [!NOTE] @@ -168,7 +168,7 @@ We recommend that you only use {% data variables.copilot.copilot_cloud_agent %} | `ssl_cert_file` | The path to the SSL certificate presented by your proxy server. You will need to configure this if your proxy intercepts SSL connections. | `/path/to/key.pem` | | `node_extra_ca_certs` | The path to the SSL certificate presented by your proxy server. You will need to configure this if your proxy intercepts SSL connections. | `/path/to/key.pem` | - You can set these environment variables by following the [instructions below](#setting-environment-variables-in-copilots-environment), or by setting them on the runner directly, for example with a custom runner image. For more information on building a custom image, see [AUTOTITLE](/actions/concepts/runners/actions-runner-controller#creating-your-own-runner-image). + You can set these environment variables by creating Agents variables or secrets, or by setting them on the runner directly, for example with a custom runner image. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables) and [AUTOTITLE](/actions/concepts/runners/actions-runner-controller#creating-your-own-runner-image). ## Switching {% data variables.product.prodname_copilot_short %} to a Windows development environment @@ -200,19 +200,7 @@ jobs: lfs: true ``` -## Setting environment variables in {% data variables.product.prodname_copilot_short %}'s environment - -You may want to set environment variables in {% data variables.product.prodname_copilot_short %}'s environment to configure or authenticate tools or dependencies that it has access to. - -You may want to set an environment variable for {% data variables.product.prodname_copilot_short %}, create a {% data variables.product.prodname_actions %} variable or secret in the `copilot` environment. If the value contains sensitive information, for example a password or API key, it's best to use a {% data variables.product.prodname_actions %} secret. - -{% data reusables.repositories.navigate-to-repo %} -{% data reusables.repositories.sidebar-settings %} -{% data reusables.actions.sidebar-environment %} -1. Click the `copilot` environment. -1. To add a secret, under "Environment secrets," click **Add environment secret**. To add a variable, under "Environment variables," click **Add environment variable**. -1. Fill in the "Name" and "Value" fields, and then click **Add secret** or **Add variable** as appropriate. - ## Further reading +* [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables) * [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-firewall) diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp.md index a8220aea2514..78424baba476 100644 --- a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/extend-cloud-agent-with-mcp.md @@ -49,7 +49,7 @@ Repository administrators can configure MCP servers by following these steps: Your configuration will be validated to ensure proper syntax. -1. If your MCP server requires a variable, key, or secret, add a variable or secret to your {% data variables.product.prodname_copilot_short %} environment. Only variables and secrets with names prefixed with `COPILOT_MCP_` will be available to your MCP configuration. See [Setting up a {% data variables.product.prodname_copilot_short %} environment for {% data variables.copilot.copilot_cloud_agent %}](#setting-up-a-copilot-environment-for-copilot-cloud-agent). +1. If your MCP server requires a variable, key, or secret, add an Agents secret or variable for {% data variables.copilot.copilot_cloud_agent %} with a name prefixed with `COPILOT_MCP_`. Only Agents secrets and variables with names prefixed with `COPILOT_MCP_` will be available to your MCP configuration. See [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables). ## Writing a JSON configuration for MCP servers @@ -249,7 +249,7 @@ To use the Azure DevOps MCP server with {% data variables.copilot.copilot_cloud_ ``` This configuration ensures the `azure/login` action is executed when {% data variables.copilot.copilot_cloud_agent %} runs. -1. In your repository’s {% data variables.product.prodname_copilot_short %} environment, add secrets for your `AZURE_CLIENT_ID` and `AZURE_TENANT_ID`. +1. Configure secrets for your `AZURE_CLIENT_ID` and `AZURE_TENANT_ID` as Agents secrets at either the organization or repository level. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables). 1. Configure the Azure DevOps MCP server by adding an `ado` object to your MCP configuration with defined tools you want {% data variables.copilot.copilot_cloud_agent %} to use. ```json copy @@ -313,20 +313,6 @@ To adapt the configuration for {% data variables.copilot.copilot_cloud_agent %}, For more information on MCP in {% data variables.product.prodname_vscode_shortname %}, see the [{% data variables.product.prodname_vscode_shortname %} docs](https://code.visualstudio.com/docs/copilot/chat/mcp-servers). -## Setting up a {% data variables.product.prodname_copilot_short %} environment for {% data variables.copilot.copilot_cloud_agent %} - -Some MCP servers will require keys or secrets. To leverage those servers in {% data variables.copilot.copilot_cloud_agent %}, you can add secrets to an environment for {% data variables.product.prodname_copilot_short %}. This ensures the secrets are properly recognized and passed to the applicable MCP server that you have configured. - -You must be a repository administrator to configure a {% data variables.product.prodname_copilot_short %} environment for your repository. - -{% data reusables.repositories.navigate-to-repo %} -{% data reusables.repositories.sidebar-settings %} -{% data reusables.actions.sidebar-environment %} -{% data reusables.actions.new-environment %} -1. Call the new environment `copilot` and click **Configure environment**. -1. Under "Environment secrets", click **Add environment secret**. -1. Give the secret a name beginning `COPILOT_MCP_`, add the secret value, then click **Add secret**. - ## Validating your MCP configuration Once you've set up your MCP configuration, you should test it to make sure it is set up correctly. @@ -376,10 +362,7 @@ If you want to allow {% data variables.product.prodname_copilot_short %} to acce For more information on toolsets, refer to the [README](https://github.com/github/github-mcp-server?tab=readme-ov-file#available-toolsets) in the {% data variables.product.github %} Remote MCP Server documentation. 1. Click **Save**. -{% data reusables.actions.sidebar-environment %} -1. Click the `copilot` environment. -1. Under "Environment secrets", click **Add environment secret**. -1. Call the secret `COPILOT_MCP_GITHUB_PERSONAL_ACCESS_TOKEN`, enter your {% data variables.product.pat_generic %} in the "Value" field, then click **Add secret**. +1. Add an Agents secret called `COPILOT_MCP_GITHUB_PERSONAL_ACCESS_TOKEN` with your {% data variables.product.pat_generic %} as the value. You can configure this at either the organization or repository level. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables). For information on using the {% data variables.product.github %} MCP server in other environments, see [AUTOTITLE](/copilot/customizing-copilot/using-model-context-protocol/using-the-github-mcp-server). diff --git a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/index.md b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/index.md index 37b77c9e157b..7b239412fcc9 100644 --- a/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/index.md +++ b/content/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/index.md @@ -10,6 +10,7 @@ children: - /extend-cloud-agent-with-mcp - /use-hooks - /customize-the-agent-environment + - /configure-secrets-and-variables - /customize-the-agent-firewall - /test-custom-agents contentType: how-tos diff --git a/content/copilot/reference/custom-agents-configuration.md b/content/copilot/reference/custom-agents-configuration.md index 54cef61abcfa..2152cabc40ea 100644 --- a/content/copilot/reference/custom-agents-configuration.md +++ b/content/copilot/reference/custom-agents-configuration.md @@ -114,9 +114,9 @@ For compatibility, the `stdio` type used by Claude Code and {% data variables.pr ### MCP server environment variables and secrets > [!NOTE] -> If your MCP server requires secrets or environment variables, these must be configured in the {% data variables.product.prodname_copilot_short %} environment in each repository where the {% data variables.copilot.copilot_custom_agent_short %} will be used. For more information on setting up environment variables, see [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-environment#setting-environment-variables-in-copilots-environment). +> If your MCP server requires secrets or environment variables, these must be configured as Agents secrets or variables at either the organization or repository level. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables). -{% data variables.copilot.copilot_custom_agent_caps_short %} MCP configuration supports the same environment variable and secret replacement capabilities as existing repository-level MCP configurations. Similar to repository-level configurations, secrets and variables can be sourced from the "copilot" environment in the repository's settings. The syntax for referencing these values has been expanded to support common patterns used in {% data variables.product.prodname_actions %} and Claude Code. +{% data variables.copilot.copilot_custom_agent_caps_short %} MCP configuration supports the same environment variable and secret replacement capabilities as existing repository-level MCP configurations. Similar to repository-level configurations, secrets and variables can be sourced from Agents secrets and variables for {% data variables.copilot.copilot_cloud_agent %}, configured at either the organization or repository level. The syntax for referencing these values has been expanded to support common patterns used in {% data variables.product.prodname_actions %} and Claude Code. Both the repository-level MCP JSON configuration and the {% data variables.copilot.copilot_custom_agent_short %} YAML configuration support the following syntax patterns: diff --git a/content/copilot/responsible-use/copilot-cloud-agent.md b/content/copilot/responsible-use/copilot-cloud-agent.md index bb2704861cf1..a9efdb87e0b1 100644 --- a/content/copilot/responsible-use/copilot-cloud-agent.md +++ b/content/copilot/responsible-use/copilot-cloud-agent.md @@ -125,7 +125,7 @@ Copilot only has access to the repository where it is working, and cannot access Its permissions are limited, allowing it to push code and read other resources. Built-in protections mean that Copilot can only push to a single branch: the existing pull request branch when triggered via `@copilot`, or otherwise to a new `copilot/` branch. This means that Copilot cannot push directly to your default branch (for example, `main`). -{% data variables.copilot.copilot_cloud_agent %} does not have access to Actions organization or repository secrets or variables during runtime. Only secrets and variables specifically added to the `copilot` environment are passed to the agent. +{% data variables.copilot.copilot_cloud_agent %} does not have access to {% data variables.product.prodname_actions %} secrets or variables during runtime. Only Agents secrets and variables, configured at the organization or repository level, are passed to the agent. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables). ### Ensuring traceability diff --git a/content/copilot/tutorials/cloud-agent/build-guardrails.md b/content/copilot/tutorials/cloud-agent/build-guardrails.md index 703b30814525..92ccbf7fc107 100644 --- a/content/copilot/tutorials/cloud-agent/build-guardrails.md +++ b/content/copilot/tutorials/cloud-agent/build-guardrails.md @@ -51,7 +51,7 @@ To adapt your rulesets for {% data variables.copilot.copilot_cloud_agent %}: Continue to store data and tokens that you _don't_ want {% data variables.product.prodname_copilot_short %} to access as **{% data variables.product.prodname_actions %} variables or secrets**. {% data variables.product.prodname_copilot_short %} won't be able to access these in its sessions or environment setup steps. -If you need to provide data and secrets that {% data variables.copilot.copilot_cloud_agent %} _does_ need, you'll be able to do this in a specific `copilot` environment. +If you need to provide data and secrets that {% data variables.copilot.copilot_cloud_agent %} _does_ need, you'll be able to do this by configuring Agents secrets and variables at the organization or repository level. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables). ### Configure runners diff --git a/content/copilot/tutorials/cloud-agent/give-access-to-resources.md b/content/copilot/tutorials/cloud-agent/give-access-to-resources.md index a5ecda353f19..4b08beb92a82 100644 --- a/content/copilot/tutorials/cloud-agent/give-access-to-resources.md +++ b/content/copilot/tutorials/cloud-agent/give-access-to-resources.md @@ -28,15 +28,14 @@ You want {% data variables.product.prodname_copilot_short %} to: By default, the scope of {% data variables.product.prodname_copilot_short %}'s authentication token is limited to the repository where it's running. This means that {% data variables.product.prodname_copilot_short %} won't be able to authenticate to external systems or access private, organization-scoped packages. -Repository administrators should add variables and secrets that {% data variables.product.prodname_copilot_short %} requires to a dedicated `copilot` {% data variables.product.prodname_actions %} environment. {% data variables.product.prodname_copilot_short %} can access this data in its setup and task execution. It won't be able to access variables or secrets outside this environment, such as organization-wide {% data variables.product.prodname_actions %} secrets. +To provide {% data variables.product.prodname_copilot_short %} with access to secrets, repository administrators can configure Agents secrets. Organization owners can also add Agents secrets at the organization level to share configuration across multiple repositories. {% data variables.product.prodname_copilot_short %} can access this data during its setup and task execution. It won't be able to access {% data variables.product.prodname_actions %}, {% data variables.product.prodname_codespaces %}, or {% data variables.product.prodname_dependabot %} secrets and variables. For more information, see [AUTOTITLE](/copilot/how-tos/copilot-on-github/customize-copilot/customize-cloud-agent/configure-secrets-and-variables). ### Example: Save a secret A repository administrator saves an authentication token for the organization's Sentry instance. -1. Go to the **Environments** section of the repository settings. -1. Create a new environment called `copilot`. -1. Save an access token for your Sentry instance in an environment secret called `COPILOT_MCP_SENTRY_ACCESS_TOKEN`. +1. Go to the **Secrets and variables** > **Agents** section of the repository settings. +1. Save an access token for your Sentry instance in a secret called `COPILOT_MCP_SENTRY_ACCESS_TOKEN`. > [!TIP] We don't need to save a token for our private {% data variables.product.prodname_registry %} registry, which we'll access using the standard {% data variables.product.prodname_actions %} `GITHUB_TOKEN`. However, you would want to save an authentication token if you were using an external package registry. @@ -99,6 +98,6 @@ A developer creates a workflow to install the Node dependencies defined in a rep Now you have seen how access to resources is controlled at the repository and organization levels, consider how much scope you want to give users to manage these settings. 1. **Choose which repositories have access** to {% data variables.copilot.copilot_cloud_agent %}. If you're concerned about a specific repository, you can block it for all users. -1. **Consider who gets admin access** to these repositories. You can control this at the organization level by creating a team with the **All-repository admin** custom role. These users will be able to manage configuration _settings_, such as MCP configuration and `copilot` environments, in every repository. +1. **Consider who gets admin access** to these repositories. You can control this at the organization level by creating a team with the **All-repository admin** custom role. These users will be able to manage configuration _settings_, such as MCP configuration and Agents secrets and variables, in every repository. 1. **Use rulesets and CODEOWNERS files** to control edits of configuration _files_, such as `copilot-setup-steps.yml`, which anyone with write access can edit by default. 1. **Review the default firewall**. The firewall doesn't affect connections to MCP servers or setup steps in `copilot-setup-steps.yml`, but it does limit {% data variables.product.prodname_copilot_short %}'s access to the Internet during task execution. See [AUTOTITLE](/copilot/how-tos/use-copilot-agents/cloud-agent/customize-the-agent-firewall). From a113a4c42ac4d5b1e603bcbe1b98bc69d2b0946d Mon Sep 17 00:00:00 2001 From: Isaac Brown <101839405+isaacmbrown@users.noreply.github.com> Date: Fri, 8 May 2026 14:23:58 +0100 Subject: [PATCH 5/5] Refresh "Set up Copilot for enterprise" article (#61113) Co-authored-by: Laura Coursen --- .../enable-copilot/set-up-for-enterprise.md | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-for-enterprise.md b/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-for-enterprise.md index a4817ee16754..51c6593e0886 100644 --- a/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-for-enterprise.md +++ b/content/copilot/how-tos/copilot-on-github/set-up-copilot/enable-copilot/set-up-for-enterprise.md @@ -2,7 +2,7 @@ title: Setting up GitHub Copilot for your enterprise shortTitle: Set up for enterprise intro: 'Enable {% data variables.product.prodname_copilot %} across your enterprise so developers can write code faster.' -product: 'Enterprises with a {% data variables.copilot.copilot_enterprise_short %} or {% data variables.copilot.copilot_business_short %} plan' +product: '{% data variables.copilot.copilot_business_short %} or {% data variables.copilot.copilot_enterprise_short %}' permissions: Enterprise owners versions: feature: copilot @@ -21,22 +21,28 @@ category: ## Enable {% data variables.product.prodname_copilot %} -{% data reusables.enterprise-accounts.access-enterprise %} -{% data reusables.enterprise-accounts.settings-tab %} -1. Click the **{% octicon "check-circle" aria-hidden="true" aria-label="check-circle" %} Getting Started** tab. -1. Under "Next steps", click **Verify your payment method**. +To purchase {% data variables.product.prodname_copilot %} for your enterprise, [contact {% data variables.product.github %}'s Sales team](https://github.com/enterprise/contact?ref_product=copilot&ref_type=engagement&ref_style=text). -To confirm {% data variables.product.prodname_copilot_short %} is enabled, check your enterprise's **{% octicon "law" aria-hidden="true" aria-label="law" %} AI Controls** tab. +A member of the Sales team will work with you to set up {% data variables.product.prodname_copilot_short %} for your enterprise. ## Set policies -Control which {% data variables.product.prodname_copilot_short %} features are available in your enterprise. See [AUTOTITLE](/copilot/managing-copilot/managing-copilot-for-your-enterprise/managing-policies-and-features-for-copilot-in-your-enterprise). +You will use enterprise policies to manage two aspects of governance: + +* **Availability**: Which {% data variables.product.prodname_copilot_short %} features, models, and MCP servers are available in your enterprise? +* **Controls**: What restrictions apply to these features? For example, will you exclude certain files or block suggestions matching public code? + +Generally, enterprise owners can either set each policy for the whole enterprise or "let organizations decide." With the latter option, users are subject to the policy of the organization where they receive their {% data variables.product.prodname_copilot_short %} license or to the default defined in your "Policies for enterprise-assigned users" setting. + +To manage policies, see [AUTOTITLE](/copilot/managing-copilot/managing-copilot-for-your-enterprise/managing-policies-and-features-for-copilot-in-your-enterprise). ## Configure networking -If your enterprise uses an HTTP proxy server or firewall, add the required URLs to the allowlist. See [AUTOTITLE](/copilot/managing-copilot/managing-github-copilot-in-your-organization/configuring-your-proxy-server-or-firewall-for-copilot). +If your corporate network restricts users' traffic, add the required URLs to the allowlist for your firewall or proxy. See [AUTOTITLE](/copilot/managing-copilot/managing-github-copilot-in-your-organization/configuring-your-proxy-server-or-firewall-for-copilot). -If your environment uses custom SSL certificates, install them on your users' machines. See [AUTOTITLE](/copilot/managing-copilot/configure-personal-settings/configuring-network-settings-for-github-copilot#installing-custom-certificates). +If you route traffic via a proxy server, you may need to ask users to configure proxy settings in their environment. You may also need to install custom certificates on your users' machines. For more information, see [AUTOTITLE](/copilot/concepts/network-settings). + +If your enterprise is on {% data variables.enterprise.data_residency_site %}, users will also need to configure their environment to authenticate from their development environment. See [AUTOTITLE](/copilot/managing-copilot/configure-personal-settings/using-github-copilot-with-an-account-on-ghecom). ## Assign licenses @@ -44,8 +50,6 @@ If your environment uses custom SSL certificates, install them on your users' ma For instructions, see [AUTOTITLE](/copilot/managing-copilot/managing-copilot-for-your-enterprise/managing-access-to-copilot-in-your-enterprise/enabling-copilot-for-organizations-in-your-enterprise). -> [!TIP] If your enterprise is on {% data variables.enterprise.data_residency_site %}, users need additional setup to authenticate from their development environment. See [AUTOTITLE](/copilot/managing-copilot/configure-personal-settings/using-github-copilot-with-an-account-on-ghecom). - ## Next steps {% data reusables.copilot.setup-next-steps %}