diff --git a/assets/images/help/copilot/activity-report-enterprise.png b/assets/images/help/copilot/activity-report-enterprise.png new file mode 100644 index 000000000000..28a340d04f6b Binary files /dev/null and b/assets/images/help/copilot/activity-report-enterprise.png differ diff --git a/assets/images/help/copilot/activity-report-non-ghe.png b/assets/images/help/copilot/activity-report-non-ghe.png new file mode 100644 index 000000000000..ac94003279df Binary files /dev/null and b/assets/images/help/copilot/activity-report-non-ghe.png differ diff --git a/assets/images/help/copilot/activity-report-org.png b/assets/images/help/copilot/activity-report-org.png new file mode 100644 index 000000000000..9e616c246279 Binary files /dev/null and b/assets/images/help/copilot/activity-report-org.png differ diff --git a/content/actions/how-tos/hosting-your-own-runners/managing-self-hosted-runners/using-self-hosted-runners-in-a-workflow.md b/content/actions/how-tos/hosting-your-own-runners/managing-self-hosted-runners/using-self-hosted-runners-in-a-workflow.md index b3ba14051a04..0510d80609b6 100644 --- a/content/actions/how-tos/hosting-your-own-runners/managing-self-hosted-runners/using-self-hosted-runners-in-a-workflow.md +++ b/content/actions/how-tos/hosting-your-own-runners/managing-self-hosted-runners/using-self-hosted-runners-in-a-workflow.md @@ -14,28 +14,6 @@ type: tutorial shortTitle: Use runners in a workflow --- -{% data reusables.actions.enterprise-github-hosted-runners %} - -You can target self-hosted runners for use in a workflow based on the labels assigned to the runners, or their group membership, or a combination of these. - ->[!IMPORTANT]Runner Scale Sets do not support multiple labels, only the name of the runner can be used in place of a label. See [AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners-with-actions-runner-controller/deploying-runner-scale-sets-with-actions-runner-controller). - -## About self-hosted runner labels - -Labels allow you to send workflow jobs to specific types of self-hosted runners, based on their shared characteristics. For example, if your job requires a particular hardware component or software package, you can assign a custom label to a runner and then configure your job to only execute on runners with that label. - -{% data reusables.actions.self-hosted-runner-labels-runs-on %} - -For information on creating custom and default labels, see [AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/using-labels-with-self-hosted-runners). - -## About self-hosted runner groups - -For self-hosted runners defined at the organization {% ifversion ghec or ghes %}or enterprise levels{% else %}level{% endif %}, you can group your runners with shared characteristics into a single runner group and then configure your job to target the runner group. - -To specify a self-hosted runner group for your job, configure `runs-on.group` in your workflow file. - -For information on creating and managing runner groups, see [AUTOTITLE](/actions/hosting-your-own-runners/managing-self-hosted-runners/managing-access-to-self-hosted-runners-using-groups). - {% ifversion repository-actions-runners %} ## Viewing available runners for a repository @@ -99,16 +77,3 @@ These labels operate cumulatively, so a self-hosted runner must have all four la ## Using labels and groups to route jobs {% data reusables.actions.jobs.example-runs-on-labels-and-groups %} - -## Routing precedence for self-hosted runners - -When routing a job to a self-hosted runner, {% data variables.product.prodname_dotcom %} looks for a runner that matches the job's `runs-on` labels and groups: - -* If {% data variables.product.prodname_dotcom %} finds an online and idle runner that matches the job's `runs-on` labels and groups, the job is then assigned and sent to the runner. - * If the runner doesn't pick up the assigned job within 60 seconds, the job is re-queued so that a new runner can accept it. -* If {% data variables.product.prodname_dotcom %} doesn't find an online and idle runner that matches the job's `runs-on` labels and groups, then the job will remain queued until a runner comes online. -* If the job remains queued for more than 24 hours, the job will fail. - -## Workflow run continuity - -{% data reusables.actions.runner-workflow-continuity %} diff --git a/content/actions/how-tos/managing-workflow-runs-and-deployments/managing-workflow-runs/manage-caches.md b/content/actions/how-tos/managing-workflow-runs-and-deployments/managing-workflow-runs/manage-caches.md index 62b0f34792b2..3ed5475423dc 100644 --- a/content/actions/how-tos/managing-workflow-runs-and-deployments/managing-workflow-runs/manage-caches.md +++ b/content/actions/how-tos/managing-workflow-runs-and-deployments/managing-workflow-runs/manage-caches.md @@ -76,7 +76,7 @@ jobs: done echo "Done" env: - GH_TOKEN: {% raw %}${{ secrets.GITHUB_TOKEN }}{% endraw %} + GH_TOKEN: {% raw %}${{ github.token }}{% endraw %} GH_REPO: {% raw %}${{ github.repository }}{% endraw %} BRANCH: refs/pull/{% raw %}${{ github.event.pull_request.number }}{% endraw %}/merge ``` diff --git a/content/actions/how-tos/use-cases-and-examples/index.md b/content/actions/how-tos/use-cases-and-examples/index.md index 471c207e86b4..530a70ace27b 100644 --- a/content/actions/how-tos/use-cases-and-examples/index.md +++ b/content/actions/how-tos/use-cases-and-examples/index.md @@ -10,7 +10,6 @@ redirect_from: - /actions/examples - /actions/use-cases-and-examples children: - - publishing-packages - project-management - using-containerized-services --- diff --git a/content/actions/index.md b/content/actions/index.md index b7975d9bcab9..c1a981bc87d3 100644 --- a/content/actions/index.md +++ b/content/actions/index.md @@ -15,7 +15,7 @@ featuredLinks: - /actions/how-tos/monitoring-and-troubleshooting-workflows guideCards: - /actions/how-tos/writing-workflows/using-workflow-templates - - /actions/how-tos/use-cases-and-examples/publishing-packages/publishing-nodejs-packages + - /actions/tutorials/publishing-packages/publishing-nodejs-packages - /actions/how-tos/writing-workflows/building-and-testing/building-and-testing-powershell popular: - /actions/reference/workflow-syntax-for-github-actions diff --git a/content/actions/reference/self-hosted-runners-reference.md b/content/actions/reference/self-hosted-runners-reference.md index bcd18225f120..cc3b06492d53 100644 --- a/content/actions/reference/self-hosted-runners-reference.md +++ b/content/actions/reference/self-hosted-runners-reference.md @@ -58,6 +58,15 @@ You can use a machine as a self-hosted runner as long as it meets these requirem * `ARM64` - Linux, macOS{% ifversion actions-windows-arm %}, Windows (currently in {% data variables.release-phases.public_preview %}){% endif %}. * `ARM32` - Linux. +## Routing precedence for self-hosted runners + +When routing a job to a self-hosted runner, {% data variables.product.prodname_dotcom %} looks for a runner that matches the job's `runs-on` labels and groups: + +* If {% data variables.product.prodname_dotcom %} finds an online and idle runner that matches the job's `runs-on` labels and groups, the job is then assigned and sent to the runner. + * If the runner doesn't pick up the assigned job within 60 seconds, the job is re-queued so that a new runner can accept it. +* If {% data variables.product.prodname_dotcom %} doesn't find an online and idle runner that matches the job's `runs-on` labels and groups, then the job will remain queued until a runner comes online. +* If the job remains queued for more than 24 hours, the job will fail. + ## Autoscaling You can automatically increase or decrease the number of self-hosted runners in your environment in response to the webhook events you receive with a particular label. diff --git a/content/actions/tutorials/index.md b/content/actions/tutorials/index.md index 8bf6aac7e840..da8e4a2b00e0 100644 --- a/content/actions/tutorials/index.md +++ b/content/actions/tutorials/index.md @@ -17,6 +17,7 @@ children: - /store-and-share-data - /deploying-with-github-actions - /communicating-with-docker-service-containers + - /publishing-packages redirect_from: - /actions/guides --- diff --git a/content/actions/how-tos/use-cases-and-examples/publishing-packages/index.md b/content/actions/tutorials/publishing-packages/index.md similarity index 89% rename from content/actions/how-tos/use-cases-and-examples/publishing-packages/index.md rename to content/actions/tutorials/publishing-packages/index.md index ca9b07dc5411..2e49e92738bf 100644 --- a/content/actions/how-tos/use-cases-and-examples/publishing-packages/index.md +++ b/content/actions/tutorials/publishing-packages/index.md @@ -10,6 +10,7 @@ redirect_from: - /actions/publishing-packages-with-github-actions - /actions/publishing-packages - /actions/use-cases-and-examples/publishing-packages + - /actions/how-tos/use-cases-and-examples/publishing-packages children: - /publishing-docker-images - /publishing-java-packages-with-gradle diff --git a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-docker-images.md b/content/actions/tutorials/publishing-packages/publishing-docker-images.md similarity index 97% rename from content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-docker-images.md rename to content/actions/tutorials/publishing-packages/publishing-docker-images.md index bd5bec5aeb0c..64e51e339832 100644 --- a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-docker-images.md +++ b/content/actions/tutorials/publishing-packages/publishing-docker-images.md @@ -1,12 +1,13 @@ --- title: Publishing Docker images shortTitle: Publish Docker images -intro: 'You can publish Docker images to a registry, such as Docker Hub or {% data variables.product.prodname_registry %}, as part of your continuous integration (CI) workflow.' +intro: 'In this tutorial, you''ll learn how to publish Docker images to a registry, such as Docker Hub or {% data variables.product.prodname_registry %}, as part of your continuous integration (CI) workflow.' redirect_from: - /actions/language-and-framework-guides/publishing-docker-images - /actions/guides/publishing-docker-images - /actions/publishing-packages/publishing-docker-images - /actions/use-cases-and-examples/publishing-packages/publishing-docker-images + - /actions/how-tos/use-cases-and-examples/publishing-packages/publishing-docker-images versions: fpt: '*' ghes: '*' @@ -19,8 +20,6 @@ topics: layout: inline --- -{% data reusables.actions.enterprise-github-hosted-runners %} - ## Introduction This guide shows you how to create a workflow that performs a Docker build, and then publishes Docker images to Docker Hub or {% data variables.product.prodname_registry %}. With a single workflow, you can publish images to a single registry or to multiple registries. diff --git a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle.md b/content/actions/tutorials/publishing-packages/publishing-java-packages-with-gradle.md similarity index 97% rename from content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle.md rename to content/actions/tutorials/publishing-packages/publishing-java-packages-with-gradle.md index e3a81a70dda4..43af51175b39 100644 --- a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle.md +++ b/content/actions/tutorials/publishing-packages/publishing-java-packages-with-gradle.md @@ -1,12 +1,13 @@ --- title: Publishing Java packages with Gradle shortTitle: Publish Java packages with Gradle -intro: You can use Gradle to publish Java packages to a registry as part of your continuous integration (CI) workflow. +intro: In this tutorial, you'll learn how to use Gradle to publish Java packages to a registry as part of your continuous integration (CI) workflow. redirect_from: - /actions/language-and-framework-guides/publishing-java-packages-with-gradle - /actions/guides/publishing-java-packages-with-gradle - /actions/publishing-packages/publishing-java-packages-with-gradle - /actions/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle + - /actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-gradle versions: fpt: '*' ghes: '*' @@ -18,8 +19,6 @@ topics: - Java - Gradle --- - -{% data reusables.actions.enterprise-github-hosted-runners %} ## Introduction diff --git a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-maven.md b/content/actions/tutorials/publishing-packages/publishing-java-packages-with-maven.md similarity index 97% rename from content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-maven.md rename to content/actions/tutorials/publishing-packages/publishing-java-packages-with-maven.md index eca8f0179189..98f3d783defb 100644 --- a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-maven.md +++ b/content/actions/tutorials/publishing-packages/publishing-java-packages-with-maven.md @@ -1,12 +1,13 @@ --- title: Publishing Java packages with Maven shortTitle: Publish Java packages with Maven -intro: You can use Maven to publish Java packages to a registry as part of your continuous integration (CI) workflow. +intro: In this tutorial, you'll learn how to use Maven to publish Java packages to a registry as part of your continuous integration (CI) workflow. redirect_from: - /actions/language-and-framework-guides/publishing-java-packages-with-maven - /actions/guides/publishing-java-packages-with-maven - /actions/publishing-packages/publishing-java-packages-with-maven - /actions/use-cases-and-examples/publishing-packages/publishing-java-packages-with-maven + - /actions/how-tos/use-cases-and-examples/publishing-packages/publishing-java-packages-with-maven versions: fpt: '*' ghes: '*' @@ -19,8 +20,6 @@ topics: - Maven --- -{% data reusables.actions.enterprise-github-hosted-runners %} - ## Introduction {% data reusables.actions.publishing-java-packages-intro %} diff --git a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-nodejs-packages.md b/content/actions/tutorials/publishing-packages/publishing-nodejs-packages.md similarity index 98% rename from content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-nodejs-packages.md rename to content/actions/tutorials/publishing-packages/publishing-nodejs-packages.md index 32b4b9c723da..49ac188c71c1 100644 --- a/content/actions/how-tos/use-cases-and-examples/publishing-packages/publishing-nodejs-packages.md +++ b/content/actions/tutorials/publishing-packages/publishing-nodejs-packages.md @@ -1,13 +1,14 @@ --- title: Publishing Node.js packages shortTitle: Publish Node.js packages -intro: You can publish Node.js packages to a registry as part of your continuous integration (CI) workflow. +intro: In this tutorial, you'll learn how to publish Node.js packages to a registry as part of your continuous integration (CI) workflow. redirect_from: - /actions/automating-your-workflow-with-github-actions/publishing-nodejs-packages - /actions/language-and-framework-guides/publishing-nodejs-packages - /actions/guides/publishing-nodejs-packages - /actions/publishing-packages/publishing-nodejs-packages - /actions/use-cases-and-examples/publishing-packages/publishing-nodejs-packages + - /actions/how-tos/use-cases-and-examples/publishing-packages/publishing-nodejs-packages versions: fpt: '*' ghes: '*' @@ -20,8 +21,6 @@ topics: - JavaScript --- -{% data reusables.actions.enterprise-github-hosted-runners %} - ## Introduction This guide shows you how to create a workflow that publishes Node.js packages to the {% data variables.product.prodname_registry %} and npm registries after continuous integration (CI) tests pass. diff --git a/content/copilot/how-tos/administer/download-activity-report.md b/content/copilot/how-tos/administer/download-activity-report.md new file mode 100644 index 000000000000..b1c889a9fef7 --- /dev/null +++ b/content/copilot/how-tos/administer/download-activity-report.md @@ -0,0 +1,58 @@ +--- +title: Downloading a Copilot activity report for your organization or enterprise +intro: 'Monitor Copilot license usage with a detailed report.' +permissions: Enterprise owners and organization owners +product: '{% data variables.copilot.copilot_enterprise_short %} or {% data variables.copilot.copilot_business_short %}' +versions: + feature: copilot +allowTitleToDifferFromFilename: true +type: how_to +topics: + - Copilot + - Enterprise +shortTitle: Download activity report +--- + +You can download a CSV report for {% data variables.product.prodname_copilot_short %} user activity in your organization. The data in the report allows you to: + +* Accurately monitor {% data variables.product.prodname_copilot_short %} license usage accurately. +* Understand patterns in user authentication. +* Manage licenses and measure key performance indicators. + + + +>[!NOTE] This report is replacing the {% data variables.product.prodname_copilot_short %} **usage report**. The new report adds clarity by introducing authentication information and improving timestamp resolution. The old usage report is closing down and will be removed on October 23rd, 2025. + + + +## Downloading the report for an enterprise + +1. Navigate to your enterprise account. +{% data reusables.enterprise-accounts.policies-tab %} +{% data reusables.enterprise-accounts.copilot-tab %} +1. Next to "Access management", click **Activity report**. + + ![Screenshot of the "Access management" tab in Copilot policies. The "Activity report" button is highlighted with an orange outline.](/assets/images/help/copilot/activity-report-enterprise.png) + +## Downloading the report for an organization + +1. Navigate to your organization settings. +1. In the left sidebar, click **Copilot**, then click **Access**. +1. Click **Get activity report**. + + ![Screenshot of the "Access" page. The "Get activity report" button is highlighted with an orange outline.](/assets/images/help/copilot/activity-report-org.png) + +## Downloading the report for a non-GHE enterprise + +These instructions apply if GitHub has provisioned you with a dedicated enterprise account for managing Copilot Business licenses. See [AUTOTITLE](/enterprise-cloud@latest/admin/copilot-business-only/about-enterprise-accounts-for-copilot-business). + +1. Navigate to your enterprise account. +1. At the top of the page, click **{% octicon "credit-card" aria-hidden="true" aria-label="credit-card" %} Billing and licensing**. +1. Click **Manage seats**. +1. Click **Get activity report**. + + ![Screenshot of the licensing page for Copilot Business. The "Get activity report" button is highlighted with an orange outline.](/assets/images/help/copilot/activity-report-non-ghe.png) + +## Report fields + +For field definitions and details of included features, see [AUTOTITLE](/copilot/reference/metrics-data#copilot-activity-report). diff --git a/content/copilot/how-tos/administer/index.md b/content/copilot/how-tos/administer/index.md index 630285825852..dfdba0967b58 100644 --- a/content/copilot/how-tos/administer/index.md +++ b/content/copilot/how-tos/administer/index.md @@ -9,5 +9,6 @@ topics: children: - /organizations - /enterprises + - /download-activity-report --- diff --git a/content/copilot/how-tos/administer/organizations/reviewing-activity-related-to-github-copilot-in-your-organization/reviewing-user-activity-data-for-copilot-in-your-organization.md b/content/copilot/how-tos/administer/organizations/reviewing-activity-related-to-github-copilot-in-your-organization/reviewing-user-activity-data-for-copilot-in-your-organization.md index 96184a8e7707..ed0338bfc3c4 100644 --- a/content/copilot/how-tos/administer/organizations/reviewing-activity-related-to-github-copilot-in-your-organization/reviewing-user-activity-data-for-copilot-in-your-organization.md +++ b/content/copilot/how-tos/administer/organizations/reviewing-activity-related-to-github-copilot-in-your-organization/reviewing-user-activity-data-for-copilot-in-your-organization.md @@ -18,8 +18,6 @@ redirect_from: - /copilot/managing-copilot/managing-github-copilot-in-your-organization/reviewing-activity-related-to-github-copilot-in-your-organization/reviewing-user-activity-data-for-copilot-in-your-organization --- -## Reviewing user activity data for {% data variables.product.prodname_copilot_short %} - {% data reusables.profile.access_org %} {% data reusables.profile.org_settings %} {% data reusables.copilot.access-settings %} @@ -31,11 +29,14 @@ redirect_from: ![Screenshot of the {% data variables.product.prodname_copilot %} usage overview.](/assets/images/help/copilot/copilot-usage-overview.png) {% endif %} -1. For more detailed information, next to "Access management," click **Get report**. +1. Alternatively, under "Access management," you can use the **Sort** options to sort the list of users by when they last used {% data variables.product.prodname_copilot %}. +1. For more detailed information, you can retrieve the **Activity report**. {% data variables.product.prodname_dotcom %} generates a report for you, which you can download as a CSV file. + + - {% data variables.product.prodname_dotcom %} generates a report for you, which you can download as a CSV file. + >[!NOTE] This report is replacing the {% data variables.product.prodname_copilot_short %} **usage report**. The new report adds clarity by introducing authentication information and improving timestamp resolution. The old usage report is closing down and will be removed on October 23rd, 2025. -1. Alternatively, under "Access management," you can use the **Sort** options to sort the list of users by when they last used {% data variables.product.prodname_copilot %}. + ## Using the API to retrieve assignment information @@ -45,7 +46,7 @@ You can use {% data variables.product.prodname_dotcom %}'s REST API to get detai If you believe a user's `last_activity_at` date should be more recent than shown in the CSV or API report, wait 24 hours and check again. If their recent Copilot usage is still not reflected in their `last_activity_at` date, have the user check that telemetry is enabled in their IDE settings. -For more information about this property, see [AUTOTITLE](/copilot/reference/metrics-data#last_activity_at). +For more information about this property and other data in the activity report, see [AUTOTITLE](/copilot/reference/metrics-data). ## Further reading diff --git a/content/copilot/reference/metrics-data.md b/content/copilot/reference/metrics-data.md index 55404af3cc47..48081a0e0810 100644 --- a/content/copilot/reference/metrics-data.md +++ b/content/copilot/reference/metrics-data.md @@ -44,3 +44,41 @@ Processing new telemetry events and updating a user's `last_activity_at` date ca * After 90 days of no new activity, a user's `last_activity_at` value is set to `nil`. For more information, see [Updating retention period for `last_activity_at` values on the Copilot user management API to 90 days](https://github.blog/changelog/2025-01-17-updating-retention-period-for-last_activity_at-values-on-the-user-management-api-public-preview-to-90-days/) on {% data variables.product.prodname_blog %}. + +## Copilot activity report + +The Copilot activity report shows user activity data for an organization or enterprise. + +Data in the report refreshes automatically every 30 minutes. + +### Fields + +| Field | Description | +|-------|-------------| +| `report_time` | UTC timestamp when the report was generated | +| `login` | GitHub username of the Copilot user | +| `last_authenticated_at` | UTC timestamp of the user's most recent authentication | +| `last_activity_at` | UTC timestamp of the user's most recent Copilot interaction | +| `last_surface_used` | The Copilot feature used most recently:
| + +### Included features + +The activity report provides visibility into usage of all generally available (GA) GitHub Copilot features in the IDE, on GitHub, in GitHub CLI, and on GitHub Mobile. + +#### IDE features + +* Code completions and Next edit suggestions +* Copilot Chat +* Copilot agents + +#### GitHub features + +* Copilot Chat +* Copilot for Docs +* Knowledge base management +* Copilot pull requests +* Copilot code reviews + +### Retention period + +Activity and authentication data are retained for a rolling 90-day period, consistent with the `last_activity_at` field. diff --git a/src/github-apps/scripts/sync.js b/src/github-apps/scripts/sync.js index 312b1a20fdaf..eab79943a1cd 100755 --- a/src/github-apps/scripts/sync.js +++ b/src/github-apps/scripts/sync.js @@ -236,7 +236,7 @@ export async function getProgAccessData(progAccessSource, isRest = false) { const progAccessData = {} for (const operation of progAccessDataRaw) { - progAccessData[operation.operation_ids] = { + const operationData = { userToServerRest: operation.user_to_server.enabled, serverToServer: operation.server_to_server.enabled, fineGrainedPat: operation.user_to_server.enabled && !operation.disabled_for_patv2, @@ -247,6 +247,12 @@ export async function getProgAccessData(progAccessSource, isRest = false) { allowsPublicRead: operation.allows_public_read, basicAuth: operation.basic_auth, } + + // Handle comma-separated operation IDs + const operationIds = operation.operation_ids.split(',').map((id) => id.trim()) + for (const operationId of operationIds) { + progAccessData[operationId] = operationData + } } return { progAccessData, progActorResources } diff --git a/src/github-apps/tests/sync.js b/src/github-apps/tests/sync.js new file mode 100644 index 000000000000..e04489c7d136 --- /dev/null +++ b/src/github-apps/tests/sync.js @@ -0,0 +1,246 @@ +import { describe, expect, test } from 'vitest' +import { getProgAccessData } from '../scripts/sync' + +describe('getProgAccessData', () => { + test('handles single operationIds correctly', async () => { + const mockProgAccessDataRaw = [ + { + operation_ids: 'single-operation-id', + user_to_server: { enabled: true }, + server_to_server: { enabled: true }, + disabled_for_patv2: false, + permission_sets: [{ actions: 'read' }], + allows_permissionless_access: false, + allows_public_read: true, + basic_auth: false, + }, + ] + + const mockProgActorResources = {} + + const result = await processProgAccessDataMock(mockProgAccessDataRaw, mockProgActorResources) + + expect(result.progAccessData).toHaveProperty('single-operation-id') + expect(result.progAccessData['single-operation-id']).toEqual({ + userToServerRest: true, + serverToServer: true, + fineGrainedPat: true, + permissions: [{ actions: 'read' }], + allowPermissionlessAccess: false, + allowsPublicRead: true, + basicAuth: false, + }) + }) + + test('handles comma-separated operationIds correctly', async () => { + const mockProgAccessDataRaw = [ + { + operation_ids: 'teams/remove-repo-in-org,teams/remove-repo-legacy', + user_to_server: { enabled: true }, + server_to_server: { enabled: true }, + disabled_for_patv2: false, + permission_sets: [ + { + administration: 'write', + members: 'read', + metadata: 'read', + }, + ], + allows_permissionless_access: false, + allows_public_read: false, + basic_auth: false, + }, + ] + + const mockProgActorResources = {} + + const result = await processProgAccessDataMock(mockProgAccessDataRaw, mockProgActorResources) + + // Both operation IDs should exist + expect(result.progAccessData).toHaveProperty('teams/remove-repo-in-org') + expect(result.progAccessData).toHaveProperty('teams/remove-repo-legacy') + + // Both should have identical data + const expectedData = { + userToServerRest: true, + serverToServer: true, + fineGrainedPat: true, + permissions: [ + { + administration: 'write', + members: 'read', + metadata: 'read', + }, + ], + allowPermissionlessAccess: false, + allowsPublicRead: false, + basicAuth: false, + } + + expect(result.progAccessData['teams/remove-repo-in-org']).toEqual(expectedData) + expect(result.progAccessData['teams/remove-repo-legacy']).toEqual(expectedData) + }) + + test('handles multiple comma-separated operationIds correctly', async () => { + const mockProgAccessDataRaw = [ + { + operation_ids: 'operation1,operation2,operation3', + user_to_server: { enabled: false }, + server_to_server: { enabled: true }, + disabled_for_patv2: true, + permission_sets: [{ metadata: 'read' }], + allows_permissionless_access: true, + allows_public_read: false, + basic_auth: true, + }, + ] + + const mockProgActorResources = {} + + const result = await processProgAccessDataMock(mockProgAccessDataRaw, mockProgActorResources) + + // All three operation IDs should exist + expect(result.progAccessData).toHaveProperty('operation1') + expect(result.progAccessData).toHaveProperty('operation2') + expect(result.progAccessData).toHaveProperty('operation3') + + // All should have identical data + const expectedData = { + userToServerRest: false, + serverToServer: true, + fineGrainedPat: false, // false because user_to_server.enabled is false + permissions: [{ metadata: 'read' }], + allowPermissionlessAccess: true, + allowsPublicRead: false, + basicAuth: true, + } + + expect(result.progAccessData['operation1']).toEqual(expectedData) + expect(result.progAccessData['operation2']).toEqual(expectedData) + expect(result.progAccessData['operation3']).toEqual(expectedData) + }) + + test('handles comma-separated operationIds with whitespace correctly', async () => { + const mockProgAccessDataRaw = [ + { + operation_ids: 'operation-a, operation-b , operation-c', + user_to_server: { enabled: true }, + server_to_server: { enabled: false }, + disabled_for_patv2: false, + permission_sets: [], + allows_permissionless_access: false, + allows_public_read: true, + basic_auth: false, + }, + ] + + const mockProgActorResources = {} + + const result = await processProgAccessDataMock(mockProgAccessDataRaw, mockProgActorResources) + + // All operation IDs should exist (whitespace should be trimmed) + expect(result.progAccessData).toHaveProperty('operation-a') + expect(result.progAccessData).toHaveProperty('operation-b') + expect(result.progAccessData).toHaveProperty('operation-c') + + const expectedData = { + userToServerRest: true, + serverToServer: false, + fineGrainedPat: true, + permissions: [], + allowPermissionlessAccess: false, + allowsPublicRead: true, + basicAuth: false, + } + + expect(result.progAccessData['operation-a']).toEqual(expectedData) + expect(result.progAccessData['operation-b']).toEqual(expectedData) + expect(result.progAccessData['operation-c']).toEqual(expectedData) + }) + + test('handles mixed single and comma-separated operationIds correctly', async () => { + const mockProgAccessDataRaw = [ + { + operation_ids: 'single-operation', + user_to_server: { enabled: true }, + server_to_server: { enabled: true }, + disabled_for_patv2: false, + permission_sets: [{ actions: 'read' }], + allows_permissionless_access: false, + allows_public_read: true, + basic_auth: false, + }, + { + operation_ids: 'comma-op1,comma-op2', + user_to_server: { enabled: true }, + server_to_server: { enabled: false }, + disabled_for_patv2: true, + permission_sets: [{ metadata: 'write' }], + allows_permissionless_access: true, + allows_public_read: false, + basic_auth: true, + }, + ] + + const mockProgActorResources = {} + + const result = await processProgAccessDataMock(mockProgAccessDataRaw, mockProgActorResources) + + // Should have 3 total entries + expect(Object.keys(result.progAccessData)).toHaveLength(3) + expect(result.progAccessData).toHaveProperty('single-operation') + expect(result.progAccessData).toHaveProperty('comma-op1') + expect(result.progAccessData).toHaveProperty('comma-op2') + + // Single operation should have its own data + expect(result.progAccessData['single-operation']).toEqual({ + userToServerRest: true, + serverToServer: true, + fineGrainedPat: true, + permissions: [{ actions: 'read' }], + allowPermissionlessAccess: false, + allowsPublicRead: true, + basicAuth: false, + }) + + // Comma-separated operations should have identical data + const expectedCommaData = { + userToServerRest: true, + serverToServer: false, + fineGrainedPat: false, // false because disabled_for_patv2 is true + permissions: [{ metadata: 'write' }], + allowPermissionlessAccess: true, + allowsPublicRead: false, + basicAuth: true, + } + + expect(result.progAccessData['comma-op1']).toEqual(expectedCommaData) + expect(result.progAccessData['comma-op2']).toEqual(expectedCommaData) + }) +}) + +// Helper function to simulate the data processing logic from sync.js +// without needing to set up the full file system or remote API calls +async function processProgAccessDataMock(progAccessDataRaw, progActorResources) { + const progAccessData = {} + + for (const operation of progAccessDataRaw) { + const operationData = { + userToServerRest: operation.user_to_server.enabled, + serverToServer: operation.server_to_server.enabled, + fineGrainedPat: operation.user_to_server.enabled && !operation.disabled_for_patv2, + permissions: operation.permission_sets || [], + allowPermissionlessAccess: operation.allows_permissionless_access, + allowsPublicRead: operation.allows_public_read, + basicAuth: operation.basic_auth, + } + + // Handle comma-separated operation IDs + const operationIds = operation.operation_ids.split(',').map((id) => id.trim()) + for (const operationId of operationIds) { + progAccessData[operationId] = operationData + } + } + + return { progAccessData, progActorResources } +} diff --git a/src/secret-scanning/data/public-docs.yml b/src/secret-scanning/data/public-docs.yml index 5bbe665e7a28..5b13719860a6 100644 --- a/src/secret-scanning/data/public-docs.yml +++ b/src/secret-scanning/data/public-docs.yml @@ -353,6 +353,39 @@ hasPushProtection: false hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure AI Services Key + secretType: azure_ai_services_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Anomaly Detector EE Key + secretType: azure_anomaly_detector_ee_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Anomaly Detector Key + secretType: azure_anomaly_detector_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Apim Direct Management Key secretType: azure_apim_direct_management_key @@ -448,6 +481,17 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Cognitive Services Key + secretType: azure_cognitive_services_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Communication Services Connection String secretType: azure_communication_services_connection_string @@ -471,6 +515,17 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Computer Vision Key + secretType: azure_computer_vision_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Registry Key Identifiable secretType: azure_container_registry_key_identifiable @@ -483,6 +538,28 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Content Moderator Key + secretType: azure_content_moderator_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Content Safety Key + secretType: azure_content_safety_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Cosmosdb Key Identifiable secretType: azure_cosmosdb_key_identifiable @@ -495,6 +572,28 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Custom Vision Prediction Key + secretType: azure_custom_vision_prediction_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Custom Vision Training Key + secretType: azure_custom_vision_training_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure DevOps Personal Access Token secretType: azure_devops_personal_access_token @@ -507,6 +606,17 @@ hasPushProtection: true hasValidityCheck: false isduplicate: true +- provider: Azure + supportedSecret: Azure Dummy Key + secretType: azure_dummy_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Event Grid Key Identifiable secretType: azure_event_grid_key_identifiable @@ -530,6 +640,17 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Face Key + secretType: azure_face_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Fluid Relay Key secretType: azure_fluid_relay_key @@ -541,6 +662,17 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Form Recognizer Key + secretType: azure_form_recognizer_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Function Key secretType: azure_function_key @@ -553,6 +685,50 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Health Decision Support Key + secretType: azure_health_decision_support_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Health Insights Key + secretType: azure_health_insights_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Immersive Reader Key + secretType: azure_immersive_reader_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Internal All In One Key + secretType: azure_internal_all_in_one_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure IoT Device Connection String secretType: azure_iot_device_connection_string @@ -625,6 +801,39 @@ hasPushProtection: false hasValidityCheck: '{% ifversion fpt or ghes %}false{% else %}true{% endif %}' isduplicate: false +- provider: Azure + supportedSecret: Azure Knowledge Key + secretType: azure_knowledge_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Luis Authoring Key + secretType: azure_luis_authoring_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Luis Key + secretType: azure_luis_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Microsoft Azure Service Management Certificate secretType: azure_management_certificate @@ -648,6 +857,17 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Metrics Advisor Key + secretType: azure_metrics_advisor_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Mixed Reality Key secretType: azure_mixed_reality_key @@ -705,6 +925,39 @@ hasPushProtection: true hasValidityCheck: false isduplicate: false +- provider: Azure + supportedSecret: Azure Personalizer Key + secretType: azure_personalizer_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure QnA Maker Key + secretType: azure_qna_maker_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure QnA Maker V2 Key + secretType: azure_qna_maker_v2_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure Relay Key Identifiable secretType: azure_relay_key_identifiable @@ -788,6 +1041,28 @@ hasPushProtection: true hasValidityCheck: false isduplicate: true +- provider: Azure + supportedSecret: Azure Speech Services Key + secretType: azure_speech_services_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Speech Translation Key + secretType: azure_speech_translation_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Azure SQL Connection String secretType: azure_sql_connection_string @@ -835,6 +1110,39 @@ hasPushProtection: true hasValidityCheck: false isduplicate: true +- provider: Azure + supportedSecret: Azure Text Analytics Key + secretType: azure_text_analytics_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Text Translation Key + secretType: azure_text_translation_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false +- provider: Azure + supportedSecret: Azure Video Intelligence Key + secretType: azure_video_intelligence_key + versions: + fpt: '*' + ghec: '*' + isPublic: true + isPrivateWithGhas: true + hasPushProtection: true + hasValidityCheck: false + isduplicate: false - provider: Azure supportedSecret: Microsoft Azure Web App Bot Key secretType: azure_web_app_bot_key diff --git a/src/secret-scanning/lib/config.json b/src/secret-scanning/lib/config.json index 86b03bf2a634..637e5335ec99 100644 --- a/src/secret-scanning/lib/config.json +++ b/src/secret-scanning/lib/config.json @@ -1,5 +1,5 @@ { - "sha": "656c184564b10879e349c25088fdbcd77191830b", - "blob-sha": "441e546f626e8c18568b923c62102e053fc01366", + "sha": "db8346944d60d2d72fba4e9be1d4841d6faa7c46", + "blob-sha": "47b1522d0147d4ebff104234581a09607eef501d", "targetFilename": "code-security/secret-scanning/introduction/supported-secret-scanning-patterns" } \ No newline at end of file