diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json index 3d2cd15b..c2271923 100644 --- a/.github/aw/actions-lock.json +++ b/.github/aw/actions-lock.json @@ -5,6 +5,11 @@ "version": "v8", "sha": "ed597411d8f924073f98dfc5c65a23a2325f34cd" }, + "github/gh-aw-actions/setup@v0.61.0": { + "repo": "github/gh-aw-actions/setup", + "version": "v0.61.0", + "sha": "df014dd7d03b638e860b2aeca95c833fd97c8cf1" + }, "github/gh-aw/actions/setup@v0.43.23": { "repo": "github/gh-aw/actions/setup", "version": "v0.43.23", diff --git a/.github/toolchain-inventory.yml b/.github/toolchain-inventory.yml new file mode 100644 index 00000000..ed72ff1d --- /dev/null +++ b/.github/toolchain-inventory.yml @@ -0,0 +1,52 @@ +# MSDO Toolchain Inventory +# Source of truth for tools monitored by the breach monitor workflow +# Derived from src/msdo-helpers.ts Tools enum +# +# All versions are noted as "latest (runtime-resolved)" because the MSDO CLI +# resolves tool versions dynamically via NuGet at runtime. + +tools: + - name: bandit + description: Python security linter (finds common security issues in Python code) + ecosystem: pypi + version: latest (runtime-resolved) + + - name: binskim + description: Binary static analysis tool for Windows/Linux binaries + ecosystem: nuget + version: latest (runtime-resolved) + + - name: checkov + description: Infrastructure-as-code security scanner + ecosystem: pypi + version: latest (runtime-resolved) + + - name: container-mapping + description: Container image mapping and inventory + ecosystem: nuget + version: latest (runtime-resolved) + + - name: eslint + description: JavaScript/TypeScript linter with security rules + ecosystem: npm + version: latest (runtime-resolved) + + - name: templateanalyzer + description: ARM/Bicep template security analyzer + ecosystem: nuget + version: latest (runtime-resolved) + + - name: terrascan + description: Terraform/IaC security scanner + ecosystem: github + version: latest (runtime-resolved) + + - name: trivy + description: Comprehensive vulnerability scanner (containers, filesystems, repos) + ecosystem: github + version: latest (runtime-resolved) + + - name: antimalware + description: Windows antimalware scanner (Windows runners only) + platform: windows-only + version: latest (runtime-resolved) diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 9b96a881..1f0fab42 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1,4 +1,3 @@ -# # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -13,7 +12,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.43.23). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.61.0). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -22,10 +21,12 @@ # For more information: https://github.github.com/gh-aw/introduction/overview/ # # -# frontmatter-hash: c9666efbc02df1203e30cb36336b938b70894f48c58634c5bb84b713c1d0a3f9 +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e0a10012ec11f9360eb65d497093ec0ba53c0a1f14cfbb5e21200dcc08055474","compiler_version":"v0.61.0","strict":true} name: "CI Doctor" "on": + # roles: # Roles processed as role check in pre-activation job + # - write # Roles processed as role check in pre-activation job workflow_dispatch: workflow_run: # zizmor: ignore[dangerous-triggers] - workflow_run trigger is secured with role and fork validation @@ -57,13 +58,53 @@ jobs: outputs: comment_id: "" comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@9382be3ca9ac18917e111a99d4e6bbff58d0dccc # v0.43.23 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "latest" + GH_AW_INFO_CLI_VERSION: "v0.61.0" + GH_AW_INFO_WORKFLOW_NAME: "CI Doctor" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["github"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.24.2" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 - name: Check workflow file timestamps - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_WORKFLOW_FILE: "ci-doctor.lock.yml" with: @@ -72,6 +113,134 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + Tools: add_comment, create_issue, create_pull_request, add_labels, missing_tool, missing_data, noop + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/safe_outputs_create_pull_request.md" + cat << 'GH_AW_PROMPT_EOF' + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + {{#runtime-import .github/workflows/ci-doctor.md}} + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 agent: needs: activation @@ -94,14 +263,16 @@ jobs: GH_AW_WORKFLOW_ID_SANITIZED: cidoctor outputs: checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} has_patch: ${{ steps.collect_output.outputs.has_patch }} - model: ${{ steps.generate_aw_info.outputs.model }} + inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }} + model: ${{ needs.activation.outputs.model }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@9382be3ca9ac18917e111a99d4e6bbff58d0dccc # v0.43.23 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -110,6 +281,10 @@ jobs: persist-credentials: false - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure gh CLI for GitHub Enterprise + run: bash /opt/gh-aw/actions/configure_gh_for_ghe.sh + env: + GH_TOKEN: ${{ github.token }} - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -117,6 +292,7 @@ jobs: run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" + git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" @@ -124,8 +300,8 @@ jobs: - name: Checkout PR branch id: checkout-pr if: | - github.event.pull_request - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: @@ -135,242 +311,46 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - - name: Generate agentic run info - id: generate_aw_info - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.2 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} with: script: | - const fs = require('fs'); - - const awInfo = { - engine_id: "copilot", - engine_name: "GitHub Copilot CLI", - model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", - version: "", - agent_version: "0.0.409", - cli_version: "v0.43.23", - workflow_name: "CI Doctor", - experimental: false, - supports_tools_allowlist: true, - supports_http_transport: true, - run_id: context.runId, - run_number: context.runNumber, - run_attempt: process.env.GITHUB_RUN_ATTEMPT, - repository: context.repo.owner + '/' + context.repo.repo, - ref: context.ref, - sha: context.sha, - actor: context.actor, - event_name: context.eventName, - staged: false, - allowed_domains: ["github"], - firewall_enabled: true, - awf_version: "v0.17.0", - awmg_version: "", - steps: { - firewall: "squid" - }, - created_at: new Date().toISOString() - }; - - // Write to /tmp/gh-aw directory to avoid inclusion in PR - const tmpPath = '/tmp/gh-aw/aw_info.json'; - fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); - console.log('Generated aw_info.json at:', tmpPath); - console.log(JSON.stringify(awInfo, null, 2)); - - // Set model as output for reuse in other steps/jobs - core.setOutput('model', awInfo.model); - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.409 - - name: Install awf binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.17.0 + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.17.0 ghcr.io/github/gh-aw-firewall/squid:0.17.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":1},"add_labels":{"allowed":["ci-failure","flaky-test","build-failure","dependency-issue","needs-maintainer"],"max":3},"create_issue":{"max":1},"create_pull_request":{},"missing_data":{},"missing_tool":{}} + {"add_comment":{"max":1},"add_labels":{"allowed":["ci-failure","flaky-test","build-failure","dependency-issue","needs-maintainer"],"max":3},"create_issue":{"max":1},"create_pull_request":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF - cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' - [ - { - "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.", - "type": "string" - }, - "labels": { - "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.", - "items": { - "type": "string" - }, - "type": "array" - }, - "parent": { - "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.", - "type": [ - "number", - "string" - ] - }, - "temporary_id": { - "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 8 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.", - "pattern": "^aw_[A-Za-z0-9]{4,8}$", - "type": "string" - }, - "title": { - "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.", - "type": "string" - } - }, - "required": [ - "title", - "body" - ], - "type": "object" - }, - "name": "create_issue" - }, - { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. CONSTRAINTS: Maximum 1 comment(s) can be added.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation.", - "type": "string" - }, - "item_number": { - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool will attempt to resolve the target from the current workflow context (triggering issue, PR, or discussion).", - "type": "number" - } - }, - "required": [ - "body" - ], - "type": "object" - }, - "name": "add_comment" - }, - { - "description": "Create a new GitHub pull request to propose code changes. Use this after making file edits to submit them for review and merging. The PR will be created from the current branch with your committed changes. For code review comments on an existing PR, use create_pull_request_review_comment instead. CONSTRAINTS: Maximum 1 pull request(s) can be created.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "Detailed PR description in Markdown. Include what changes were made, why, testing notes, and any breaking changes. Do NOT repeat the title as a heading.", - "type": "string" - }, - "branch": { - "description": "Source branch name containing the changes. If omitted, uses the current working branch.", - "type": "string" - }, - "labels": { - "description": "Labels to categorize the PR (e.g., 'enhancement', 'bugfix'). Labels must exist in the repository.", - "items": { - "type": "string" - }, - "type": "array" - }, - "title": { - "description": "Concise PR title describing the changes. Follow repository conventions (e.g., conventional commits). The title appears as the main heading.", - "type": "string" - } - }, - "required": [ - "title", - "body" - ], - "type": "object" - }, - "name": "create_pull_request" - }, - { - "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Only these labels are allowed: [ci-failure flaky-test build-failure dependency-issue needs-maintainer].", - "inputSchema": { - "additionalProperties": false, - "properties": { - "item_number": { - "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the item that triggered this workflow.", - "type": "number" - }, - "labels": { - "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "name": "add_labels" - }, - { - "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "reason": { - "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", - "type": "string" - }, - "tool": { - "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", - "type": "string" - } - }, - "required": [ - "reason" - ], - "type": "object" - }, - "name": "missing_tool" + - name: Write Safe Outputs Tools + run: | + cat > /opt/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF' + { + "description_suffixes": { + "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added.", + "add_labels": " CONSTRAINTS: Only these labels are allowed: [\"ci-failure\" \"flaky-test\" \"build-failure\" \"dependency-issue\" \"needs-maintainer\"].", + "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created.", + "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created." }, - { - "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "context": { - "description": "Additional context about the missing data or where it should come from (max 256 characters).", - "type": "string" - }, - "data_type": { - "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", - "type": "string" - }, - "reason": { - "description": "Explanation of why this data is needed to complete the task (max 256 characters).", - "type": "string" - } - }, - "required": [], - "type": "object" - }, - "name": "missing_data" - } - ] - GH_AW_SAFE_OUTPUTS_TOOLS_EOF + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' { "add_comment": { @@ -384,6 +364,10 @@ jobs: }, "item_number": { "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 } } }, @@ -391,7 +375,7 @@ jobs: "defaultMax": 5, "fields": { "item_number": { - "issueOrPRNumber": true + "issueNumberOrTemporaryId": true }, "labels": { "required": true, @@ -399,6 +383,10 @@ jobs: "itemType": "string", "itemSanitize": true, "itemMaxLength": 128 + }, + "repo": { + "type": "string", + "maxLength": 256 } } }, @@ -450,12 +438,19 @@ jobs: "sanitize": true, "maxLength": 256 }, + "draft": { + "type": "boolean" + }, "labels": { "type": "array", "itemType": "string", "itemSanitize": true, "itemMaxLength": 128 }, + "repo": { + "type": "string", + "maxLength": 256 + }, "title": { "required": true, "type": "string", @@ -464,6 +459,31 @@ jobs: } } }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, "missing_tool": { "defaultMax": 20, "fields": { @@ -484,9 +504,21 @@ jobs: "maxLength": 128 } } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } } } GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + node /opt/gh-aw/actions/generate_safe_outputs_tools.cjs - name: Generate Safe Outputs MCP Server Config id: safe-outputs-config run: | @@ -525,12 +557,14 @@ jobs: bash /opt/gh-aw/actions/start_safe_outputs_server.sh - - name: Start MCP gateway + - name: Start MCP Gateway id: start-mcp-gateway env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }} + GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | set -eo pipefail @@ -544,10 +578,11 @@ jobs: export MCP_GATEWAY_API_KEY export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.4' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -555,11 +590,18 @@ jobs: "mcpServers": { "github": { "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v0.30.3", + "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", "GITHUB_TOOLSETS": "issues,actions" + }, + "guard-policies": { + "allow-only": { + "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY", + "repos": "$GITHUB_MCP_GUARD_REPOS" + } } }, "safeoutputs": { @@ -567,6 +609,13 @@ jobs: "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", "headers": { "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } } } }, @@ -578,145 +627,13 @@ jobs: } } GH_AW_MCP_CONFIG_EOF - - name: Generate workflow overview - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); - await generateWorkflowOverview(core); - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - run: | - bash /opt/gh-aw/actions/create_prompt_first.sh - cat << 'GH_AW_PROMPT_EOF' > "$GH_AW_PROMPT" - - GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/xpia.md" >> "$GH_AW_PROMPT" - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT" - cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT" - cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" - - GitHub API Access Instructions - - The gh CLI is NOT authenticated. Do NOT use gh commands for GitHub operations. - - - To create or modify GitHub resources (issues, discussions, pull requests, etc.), you MUST call the appropriate safe output tool. Simply writing content will NOT work - the workflow requires actual tool calls. - - Temporary IDs: Some safe output tools support a temporary ID field (usually named temporary_id) so you can reference newly-created items elsewhere in the SAME agent output (for example, using #aw_abc1 in a later body). - - **IMPORTANT - temporary_id format rules:** - - If you DON'T need to reference the item later, OMIT the temporary_id field entirely (it will be auto-generated if needed) - - If you DO need cross-references/chaining, you MUST match this EXACT validation regex: /^aw_[A-Za-z0-9]{3,8}$/i - - Format: aw_ prefix followed by 3 to 8 alphanumeric characters (A-Z, a-z, 0-9, case-insensitive) - - Valid alphanumeric characters: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 - - INVALID examples: aw_ab (too short), aw_123456789 (too long), aw_test-id (contains hyphen), aw_id_123 (contains underscore) - - VALID examples: aw_abc, aw_abc1, aw_Test123, aw_A1B2C3D4, aw_12345678 - - To generate valid IDs: use 3-8 random alphanumeric characters or omit the field to let the system auto-generate - - Do NOT invent other aw_* formats — downstream steps will reject them with validation errors matching against /^aw_[A-Za-z0-9]{3,8}$/i. - - Discover available tools from the safeoutputs MCP server. - - **Critical**: Tool calls write structured data that downstream jobs process. Without tool calls, follow-up actions will be skipped. - - **Note**: If you made no other safe output tool calls during this workflow execution, call the "noop" tool to provide a status message indicating completion or that no actions were needed. - - - - The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ - {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" - {{#runtime-import .github/workflows/ci-doctor.md}} - GH_AW_PROMPT_EOF - - name: Substitute placeholders - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - script: | - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE - } - }); - - name: Interpolate variables and render templates - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh + name: activation + path: /tmp/gh-aw - name: Clean git credentials + continue-on-error: true run: bash /opt/gh-aw/actions/clean_git_credentials.sh - name: Execute GitHub Copilot CLI id: agentic_execution @@ -724,21 +641,37 @@ jobs: timeout-minutes: 20 run: | set -o pipefail - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.17.0 --skip-pull \ - -- '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \ - 2>&1 | tee /tmp/gh-aw/agent-stdio.log + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json - GH_AW_MODEL_AGENT_COPILOT: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_VERSION: v0.61.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] XDG_CONFIG_HOME: /home/runner + - name: Detect inference access error + id: detect-inference-error + if: always() + continue-on-error: true + run: bash /opt/gh-aw/actions/detect_inference_access_error.sh - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -746,6 +679,7 @@ jobs: run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" + git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" @@ -767,7 +701,7 @@ jobs: else echo "No session-state directory found at $SESSION_STATE_DIR" fi - - name: Stop MCP gateway + - name: Stop MCP Gateway if: always() continue-on-error: true env: @@ -778,7 +712,7 @@ jobs: bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - name: Redact secrets in logs if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); @@ -791,19 +725,21 @@ jobs: SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Safe Outputs + - name: Append agent step summary if: always() - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: safe-output - path: ${{ env.GH_AW_SAFE_OUTPUTS }} - if-no-files-found: warn + run: bash /opt/gh-aw/actions/append_agent_step_summary.sh + - name: Copy Safe Outputs + if: always() + run: | + mkdir -p /tmp/gh-aw + cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true - name: Ingest agent output id: collect_output - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -812,24 +748,9 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); await main(); - - name: Upload sanitized agent output - if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: agent-output - path: ${{ env.GH_AW_AGENT_OUTPUT }} - if-no-files-found: warn - - name: Upload engine output files - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - name: Parse agent logs for step summary if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ with: @@ -838,9 +759,9 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); await main(); - - name: Parse MCP gateway logs for step summary + - name: Parse MCP Gateway logs for step summary if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); @@ -856,28 +777,159 @@ jobs: # Fix permissions on firewall logs so they can be uploaded as artifacts # AWF runs with sudo, creating files owned by root sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true - awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi - name: Upload agent artifacts if: always() continue-on-error: true - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: agent-artifacts + name: agent path: | /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/aw_info.json + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log /tmp/gh-aw/mcp-logs/ /tmp/gh-aw/sandbox/firewall/logs/ /tmp/gh-aw/agent-stdio.log /tmp/gh-aw/agent/ - /tmp/gh-aw/aw.patch + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json + /tmp/gh-aw/aw-*.patch if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + WORKFLOW_NAME: "CI Doctor" + WORKFLOW_DESCRIPTION: "No description provided" + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PHASE: detection + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.61.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: detection + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi conclusion: needs: - activation - agent - - detection - safe_outputs if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim @@ -886,28 +938,48 @@ jobs: discussions: write issues: write pull-requests: write + concurrency: + group: "gh-aw-conclusion-ci-doctor" + cancel-in-progress: false outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@9382be3ca9ac18917e111a99d4e6bbff58d0dccc # v0.43.23 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions - name: Download agent output artifact + id: download-agent-output continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ + name: agent + path: /tmp/gh-aw/ - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "CI Doctor" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); - name: Record Missing Tool id: missing_tool - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "CI Doctor" @@ -920,15 +992,21 @@ jobs: await main(); - name: Handle Agent Failure id: handle_agent_failure - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "CI Doctor" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "ci-doctor" - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.agent.outputs.secret_verification_result }} + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} + GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "false" + GH_AW_TIMEOUT_MINUTES: "20" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -938,12 +1016,14 @@ jobs: await main(); - name: Handle No-Op Message id: handle_noop_message - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "CI Doctor" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "false" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -953,7 +1033,7 @@ jobs: await main(); - name: Handle Create Pull Request Error id: handle_create_pr_error - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "CI Doctor" @@ -965,138 +1045,20 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/handle_create_pr_error.cjs'); await main(); - - name: Update reaction comment with completion status - id: conclusion - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} - GH_AW_COMMENT_REPO: ${{ needs.activation.outputs.comment_repo }} - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_WORKFLOW_NAME: "CI Doctor" - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.result }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/notify_comment_error.cjs'); - await main(); - - detection: - needs: agent - if: needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true' - runs-on: ubuntu-latest - permissions: {} - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}" - timeout-minutes: 10 - outputs: - success: ${{ steps.parse_results.outputs.success }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@9382be3ca9ac18917e111a99d4e6bbff58d0dccc # v0.43.23 - with: - destination: /opt/gh-aw/actions - - name: Download agent artifacts - continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 - with: - name: agent-artifacts - path: /tmp/gh-aw/threat-detection/ - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 - with: - name: agent-output - path: /tmp/gh-aw/threat-detection/ - - name: Echo agent output types - env: - AGENT_OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} - run: | - echo "Agent output-types: $AGENT_OUTPUT_TYPES" - - name: Setup threat detection - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - WORKFLOW_NAME: "CI Doctor" - WORKFLOW_DESCRIPTION: "No description provided" - HAS_PATCH: ${{ needs.agent.outputs.has_patch }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); - await main(); - - name: Ensure threat-detection directory and log - run: | - mkdir -p /tmp/gh-aw/threat-detection - touch /tmp/gh-aw/threat-detection/detection.log - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.409 - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - # --allow-tool shell(cat) - # --allow-tool shell(grep) - # --allow-tool shell(head) - # --allow-tool shell(jq) - # --allow-tool shell(ls) - # --allow-tool shell(tail) - # --allow-tool shell(wc) - timeout-minutes: 20 - run: | - set -o pipefail - COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" - mkdir -p /tmp/ - mkdir -p /tmp/gh-aw/ - mkdir -p /tmp/gh-aw/agent/ - mkdir -p /tmp/gh-aw/sandbox/agent/logs/ - copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-tool 'shell(cat)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(jq)' --allow-tool 'shell(ls)' --allow-tool 'shell(tail)' --allow-tool 'shell(wc)' --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$COPILOT_CLI_INSTRUCTION"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"} 2>&1 | tee /tmp/gh-aw/threat-detection/detection.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - GH_AW_MODEL_DETECTION_COPILOT: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Parse threat detection results - id: parse_results - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); - await main(); - - name: Upload threat detection log - if: always() - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: threat-detection.log - path: /tmp/gh-aw/threat-detection/detection.log - if-no-files-found: ignore pre_activation: runs-on: ubuntu-slim outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} + matched_command: '' steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@9382be3ca9ac18917e111a99d4e6bbff58d0dccc # v0.43.23 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions - name: Check team membership for workflow id: check_membership - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_REQUIRED_ROLES: write with: @@ -1111,8 +1073,7 @@ jobs: needs: - activation - agent - - detection - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') runs-on: ubuntu-slim permissions: contents: write @@ -1121,41 +1082,53 @@ jobs: pull-requests: write timeout-minutes: 15 env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/ci-doctor" GH_AW_ENGINE_ID: "copilot" GH_AW_WORKFLOW_ID: "ci-doctor" GH_AW_WORKFLOW_NAME: "CI Doctor" outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }} + created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }} + created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }} + created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }} process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@9382be3ca9ac18917e111a99d4e6bbff58d0dccc # v0.43.23 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions - name: Download agent output artifact + id: download-agent-output continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ + name: agent + path: /tmp/gh-aw/ - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" - name: Download patch artifact continue-on-error: true - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent-artifacts + name: agent path: /tmp/gh-aw/ - name: Checkout repository if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_pull_request')) uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - token: ${{ github.token }} + ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} + token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} persist-credentials: false fetch-depth: 1 - name: Configure Git credentials @@ -1163,20 +1136,25 @@ jobs: env: REPO_NAME: ${{ github.repository }} SERVER_URL: ${{ github.server_url }} - GIT_TOKEN: ${{ github.token }} + GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" + git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" echo "Git configured with standard GitHub Actions identity" - name: Process Safe Outputs id: process_safe_outputs - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"allowed\":[\"ci-failure\",\"flaky-test\",\"build-failure\",\"dependency-issue\",\"needs-maintainer\"]},\"create_issue\":{\"max\":1},\"create_pull_request\":{\"base_branch\":\"${{ github.ref_name }}\",\"max\":1,\"max_patch_size\":1024},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":1},\"add_labels\":{\"allowed\":[\"ci-failure\",\"flaky-test\",\"build-failure\",\"dependency-issue\",\"needs-maintainer\"]},\"create_issue\":{\"max\":1},\"create_pull_request\":{\"max\":1,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"]},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1184,4 +1162,11 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); + - name: Upload Safe Output Items Manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn diff --git a/.github/workflows/ci-doctor.md b/.github/workflows/ci-doctor.md index 790c5e67..e4772f77 100644 --- a/.github/workflows/ci-doctor.md +++ b/.github/workflows/ci-doctor.md @@ -1,6 +1,12 @@ --- # CI Doctor - GitHub Agentic Workflow # Investigates failed CI workflows and opens diagnostic issues +# +# MAINTENANCE NOTE: after running `gh aw compile` with gh-aw v0.61.0, verify +# that the `actions/github-script` SHA in the generated .lock.yml stays pinned +# to v9.0.0 (`3a2844b7e9c422d3c10d287c895573f7108da1b3`). v0.61.0's bundled +# scaffolding emits the older v8 SHA and would silently revert PR #244. See +# PR #252 for context. on: workflow_run: @@ -8,8 +14,7 @@ on: types: [completed] branches: [main, 'release/**'] workflow_dispatch: - -roles: [write] + roles: [write] engine: id: copilot @@ -28,10 +33,12 @@ tools: lockdown: false toolsets: [issues, actions] fetch: - allowed-domains: [] + allowed: [] safe-outputs: - noop: false + noop: + report-as-issue: false + report-failure-as-issue: false create-issue: max: 1 add-labels: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e129a116..d738944d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 - name: Set up Node.js uses: actions/setup-node@v6 diff --git a/.github/workflows/msdo-breach-monitor.lock.yml b/.github/workflows/msdo-breach-monitor.lock.yml new file mode 100644 index 00000000..948c0df6 --- /dev/null +++ b/.github/workflows/msdo-breach-monitor.lock.yml @@ -0,0 +1,1017 @@ +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw (v0.61.0). DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# Not all edits will cause changes to this file. +# +# For more information: https://github.github.com/gh-aw/introduction/overview/ +# +# +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"73ddd2b5a2fc15ff120245519bd10f342dd3d1a0925df30be6453378664b4c29","compiler_version":"v0.61.0","strict":true} + +name: "MSDO Toolchain Breach Monitor" +"on": + # roles: # Roles processed as role check in pre-activation job + # - write # Roles processed as role check in pre-activation job + workflow_dispatch: + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "MSDO Toolchain Breach Monitor" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Setup Scripts + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 + with: + destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "latest" + GH_AW_INFO_CLI_VERSION: "v0.61.0" + GH_AW_INFO_WORKFLOW_NAME: "MSDO Toolchain Breach Monitor" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["github","python","dotnet","nvd.nist.gov","osv.dev"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.24.2" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 + - name: Check workflow file timestamps + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_WORKFLOW_FILE: "msdo-breach-monitor.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + Tools: create_issue, add_labels, missing_tool, missing_data, noop + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + {{#runtime-import .github/workflows/msdo-breach-monitor.md}} + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + issues: read + env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + GH_AW_ASSETS_ALLOWED_EXTS: "" + GH_AW_ASSETS_BRANCH: "" + GH_AW_ASSETS_MAX_SIZE_KB: 0 + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_WORKFLOW_ID_SANITIZED: msdobreachmonitor + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} + has_patch: ${{ steps.collect_output.outputs.has_patch }} + inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }} + model: ${{ needs.activation.outputs.model }} + output: ${{ steps.collect_output.outputs.output }} + output_types: ${{ steps.collect_output.outputs.output_types }} + steps: + - name: Setup Scripts + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure gh CLI for GitHub Enterprise + run: bash /opt/gh-aw/actions/configure_gh_for_ghe.sh + env: + GH_TOKEN: ${{ github.token }} + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.2 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine + - name: Write Safe Outputs Config + run: | + mkdir -p /opt/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/safeoutputs + mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs + cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' + {"add_labels":{"allowed":["security-breach","supply-chain","toolchain-alert","critical","high","medium"],"max":3},"create_issue":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + GH_AW_SAFE_OUTPUTS_CONFIG_EOF + - name: Write Safe Outputs Tools + run: | + cat > /opt/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF' + { + "description_suffixes": { + "add_labels": " CONSTRAINTS: Only these labels are allowed: [\"security-breach\" \"supply-chain\" \"toolchain-alert\" \"critical\" \"high\" \"medium\"].", + "create_issue": " CONSTRAINTS: Maximum 1 issue(s) can be created." + }, + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF + cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' + { + "add_labels": { + "defaultMax": 5, + "fields": { + "item_number": { + "issueNumberOrTemporaryId": true + }, + "labels": { + "required": true, + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "create_issue": { + "defaultMax": 1, + "fields": { + "body": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + }, + "labels": { + "type": "array", + "itemType": "string", + "itemSanitize": true, + "itemMaxLength": 128 + }, + "parent": { + "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 + }, + "temporary_id": { + "type": "string" + }, + "title": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 + } + } + }, + "missing_tool": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 512 + }, + "reason": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "tool": { + "type": "string", + "sanitize": true, + "maxLength": 128 + } + } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } + } + } + GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + node /opt/gh-aw/actions/generate_safe_outputs_tools.cjs + - name: Generate Safe Outputs MCP Server Config + id: safe-outputs-config + run: | + # Generate a secure random API key (360 bits of entropy, 40+ chars) + # Mask immediately to prevent timing vulnerabilities + API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${API_KEY}" + + PORT=3001 + + # Set outputs for next steps + { + echo "safe_outputs_api_key=${API_KEY}" + echo "safe_outputs_port=${PORT}" + } >> "$GITHUB_OUTPUT" + + echo "Safe Outputs MCP server will run on port ${PORT}" + + - name: Start Safe Outputs MCP HTTP Server + id: safe-outputs-start + env: + DEBUG: '*' + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }} + GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json + GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json + GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs + run: | + # Environment variables are set above to prevent template injection + export DEBUG + export GH_AW_SAFE_OUTPUTS_PORT + export GH_AW_SAFE_OUTPUTS_API_KEY + export GH_AW_SAFE_OUTPUTS_TOOLS_PATH + export GH_AW_SAFE_OUTPUTS_CONFIG_PATH + export GH_AW_MCP_LOG_DIR + + bash /opt/gh-aw/actions/start_safe_outputs_server.sh + + - name: Start MCP Gateway + id: start-mcp-gateway + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} + GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }} + GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" + export DEBUG="*" + + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' + + mkdir -p /home/runner/.copilot + cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.32.0", + "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "issues" + }, + "guard-policies": { + "allow-only": { + "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY", + "repos": "$GITHUB_MCP_GUARD_REPOS" + } + } + }, + "safeoutputs": { + "type": "http", + "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", + "headers": { + "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + GH_AW_MCP_CONFIG_EOF + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: activation + path: /tmp/gh-aw + - name: Clean git credentials + continue-on-error: true + run: bash /opt/gh-aw/actions/clean_git_credentials.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,*.pythonhosted.org,*.vsblob.vsassets.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crates.io,dc.services.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,files.pythonhosted.org,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,lfs.github.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,nvd.nist.gov,objects.githubusercontent.com,oneocsp.microsoft.com,osv.dev,pip.pypa.io,pkgs.dev.azure.com,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,static.crates.io,telemetry.enterprise.githubcopilot.com,www.microsoft.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PHASE: agent + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_VERSION: v0.61.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Detect inference access error + id: detect-inference-error + if: always() + continue-on-error: true + run: bash /opt/gh-aw/actions/detect_inference_access_error.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + git config --global am.keepcr true + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP Gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Append agent step summary + if: always() + run: bash /opt/gh-aw/actions/append_agent_step_summary.sh + - name: Copy Safe Outputs + if: always() + run: | + mkdir -p /tmp/gh-aw + cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true + - name: Ingest agent output + id: collect_output + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,*.vsblob.vsassets.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crates.io,dc.services.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,files.pythonhosted.org,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,lfs.github.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,nvd.nist.gov,objects.githubusercontent.com,oneocsp.microsoft.com,osv.dev,pip.pypa.io,pkgs.dev.azure.com,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,static.crates.io,telemetry.enterprise.githubcopilot.com,www.microsoft.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); + await main(); + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP Gateway logs for step summary + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: agent + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json + if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + WORKFLOW_NAME: "MSDO Toolchain Breach Monitor" + WORKFLOW_DESCRIPTION: "No description provided" + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PHASE: detection + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.61.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: detection + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi + + conclusion: + needs: + - activation + - agent + - safe_outputs + if: (always()) && (needs.agent.result != 'skipped') + runs-on: ubuntu-slim + permissions: + contents: read + issues: write + pull-requests: write + concurrency: + group: "gh-aw-conclusion-msdo-breach-monitor" + cancel-in-progress: false + outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} + tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} + total_count: ${{ steps.missing_tool.outputs.total_count }} + steps: + - name: Setup Scripts + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "MSDO Toolchain Breach Monitor" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); + - name: Record Missing Tool + id: missing_tool + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "MSDO Toolchain Breach Monitor" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/missing_tool.cjs'); + await main(); + - name: Handle Agent Failure + id: handle_agent_failure + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "MSDO Toolchain Breach Monitor" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_WORKFLOW_ID: "msdo-breach-monitor" + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} + GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "false" + GH_AW_TIMEOUT_MINUTES: "20" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_agent_failure.cjs'); + await main(); + - name: Handle No-Op Message + id: handle_noop_message + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_WORKFLOW_NAME: "MSDO Toolchain Breach Monitor" + GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "false" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); + await main(); + + safe_outputs: + needs: agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') + runs-on: ubuntu-slim + permissions: + contents: read + issues: write + pull-requests: write + timeout-minutes: 15 + env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/msdo-breach-monitor" + GH_AW_ENGINE_ID: "copilot" + GH_AW_WORKFLOW_ID: "msdo-breach-monitor" + GH_AW_WORKFLOW_NAME: "MSDO Toolchain Breach Monitor" + outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} + create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} + created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }} + created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }} + process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} + process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} + steps: + - name: Setup Scripts + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 + with: + destination: /opt/gh-aw/actions + - name: Download agent output artifact + id: download-agent-output + continue-on-error: true + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: agent + path: /tmp/gh-aw/ + - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' + run: | + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" + - name: Process Safe Outputs + id: process_safe_outputs + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.pythonhosted.org,*.vsblob.vsassets.io,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crates.io,dc.services.visualstudio.com,dist.nuget.org,docs.github.com,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,files.pythonhosted.org,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,index.crates.io,lfs.github.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,nvd.nist.gov,objects.githubusercontent.com,oneocsp.microsoft.com,osv.dev,pip.pypa.io,pkgs.dev.azure.com,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.npmjs.org,repo.anaconda.com,repo.continuum.io,static.crates.io,telemetry.enterprise.githubcopilot.com,www.microsoft.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"security-breach\",\"supply-chain\",\"toolchain-alert\",\"critical\",\"high\",\"medium\"]},\"create_issue\":{\"max\":1},\"missing_data\":{},\"missing_tool\":{}}" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); + await main(); + - name: Upload Safe Output Items Manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn + diff --git a/.github/workflows/msdo-breach-monitor.md b/.github/workflows/msdo-breach-monitor.md new file mode 100644 index 00000000..2607ea3c --- /dev/null +++ b/.github/workflows/msdo-breach-monitor.md @@ -0,0 +1,215 @@ +--- +# MSDO Toolchain Breach Monitor - GitHub Agentic Workflow +# Nightly supply chain breach monitor for MSDO toolchain dependencies +# +# MAINTENANCE NOTE: after running `gh aw compile` with gh-aw v0.61.0, verify +# that the `actions/github-script` SHA in the generated .lock.yml stays pinned +# to v9.0.0 (`3a2844b7e9c422d3c10d287c895573f7108da1b3`). v0.61.0's bundled +# scaffolding emits the older v8 SHA and would silently revert PR #244. See +# PR #252 for context. + +on: + workflow_dispatch: + # Triggered by toolchain-version-probe after committing fresh versions. + # No schedule here — the probe owns the daily cadence and guarantees + # toolchain-versions.json is fresh before this workflow reads it. + roles: [write] + +engine: + id: copilot + +permissions: + contents: read + issues: read + +network: + allowed: + - github + - python + - dotnet + - nvd.nist.gov + - osv.dev + +tools: + github: + lockdown: false + toolsets: [issues] + fetch: + allowed: + - raw.githubusercontent.com + - nvd.nist.gov + - services.nvd.nist.gov + - osv.dev + - pypi.org + - api.nuget.org + - registry.npmjs.org + +safe-outputs: + noop: + report-as-issue: false + report-failure-as-issue: false + create-issue: + max: 1 + add-labels: + allowed: [security-breach, supply-chain, toolchain-alert, critical, high, medium] + +--- + +# MSDO Toolchain Breach Monitor + +You are a supply chain security monitor for the **Microsoft Security DevOps Action** repository (`microsoft/security-devops-action`). + +## Your Toolchain + +The MSDO CLI resolves tool versions dynamically at runtime. The tools and their package registries are: + +| Tool | Ecosystem | Upstream repo | +|------|-----------|---------------| +| bandit | PyPI (`bandit`) | PyCQA/bandit | +| binskim | NuGet (`Microsoft.CST.BinSkim`) | microsoft/binskim | +| checkov | PyPI (`checkov`) | bridgecrewio/checkov | +| container-mapping | NuGet (internal) | microsoft internal | +| eslint | npm (`eslint`) | eslint/eslint | +| templateanalyzer | NuGet (`Microsoft.Azure.Templates.Analyzer`) | Azure/template-analyzer | +| terrascan | GitHub releases | tenable/terrascan | +| trivy | GitHub releases | aquasecurity/trivy | +| antimalware | Windows Defender (built-in) | N/A | + +## Your Task + +Monitor for supply chain security incidents affecting any tool in the MSDO toolchain. + +### Step 0: Load resolved tool versions + +The `toolchain-version-probe` workflow runs weekly, installs every tool through the real MSDO CLI, and records exactly which package version was resolved into `.github/toolchain-versions.json`. These are the versions MSDO users actually download — not registry "latest", but the version pinned in MSDO's `.gdntool` configs. + +**Read the file from this repository (the probe pushes to a dedicated branch to avoid branch protection on main):** +``` +GET https://api.github.com/repos/microsoft/security-devops-action/contents/.github/toolchain-versions.json?ref=bot/toolchain-versions +``` +Decode the base64 `content` field. The `tools` object maps each tool name to its resolved version. The `generated_at` field tells you when the probe last ran. + +**If the file is missing or older than 14 days**, fall back to registry queries: +- **trivy**: `GET https://api.github.com/repos/aquasecurity/trivy/releases/latest` → `.tag_name` +- **terrascan**: `GET https://api.github.com/repos/tenable/terrascan/releases/latest` → `.tag_name` +- **bandit**: `GET https://pypi.org/pypi/bandit/json` → `.info.version` +- **checkov**: `GET https://pypi.org/pypi/checkov/json` → `.info.version` +- **eslint**: `GET https://registry.npmjs.org/eslint/latest` → `.version` +- **binskim**: `GET https://api.nuget.org/v3/registration5/microsoft.codeanalysis.binskim/index.json` → last page, last item `.catalogEntry.version` +- **templateanalyzer**: `GET https://api.nuget.org/v3/registration5/microsoft.azure.templates.analyzer/index.json` → last page, last item `.catalogEntry.version` + +Record the resolved versions — you will reference them in your advisory checks below. + +### Step 1: Check advisories — use the exact API endpoints below + +Search each ecosystem's advisory database using the **resolved version** from Step 0. Look for vulnerabilities that affect that specific version or any version within the last 7 days. + +**GitHub Advisory Database (REQUIRED for each ecosystem):** +``` +GET https://api.github.com/advisories?type=reviewed&ecosystem=pip&per_page=30 +GET https://api.github.com/advisories?type=reviewed&ecosystem=go&per_page=30 +GET https://api.github.com/advisories?type=reviewed&ecosystem=npm&per_page=30 +GET https://api.github.com/advisories?type=reviewed&ecosystem=nuget&per_page=30 +``` +Filter results by date (last 7 days) and check if any advisory mentions the tool name or its resolved version. + +**Also check the upstream repos directly for recent security advisories:** +``` +GET https://api.github.com/repos/aquasecurity/trivy/security-advisories?per_page=10 +GET https://api.github.com/repos/tenable/terrascan/security-advisories?per_page=10 +``` + +**OSV Database:** +``` +POST https://api.osv.dev/v1/query body: {"package":{"name":"trivy","ecosystem":"Go"}} +POST https://api.osv.dev/v1/query body: {"package":{"name":"checkov","ecosystem":"PyPI"}} +POST https://api.osv.dev/v1/query body: {"package":{"name":"bandit","ecosystem":"PyPI"}} +``` + +**NVD — search for recent CVEs (last 7 days):** +``` +GET https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=trivy&pubStartDate=<7-days-ago>T00:00:00.000 +GET https://services.nvd.nist.gov/rest/json/cves/2.0?keywordSearch=terrascan&pubStartDate=<7-days-ago>T00:00:00.000 +``` + +### Step 2: Check repository health (maintenance and archival) + +For each tool with a public GitHub repo, check its maintenance status: + +``` +GET https://api.github.com/repos/tenable/terrascan +GET https://api.github.com/repos/aquasecurity/trivy +GET https://api.github.com/repos/bridgecrewio/checkov +GET https://api.github.com/repos/PyCQA/bandit +GET https://api.github.com/repos/eslint/eslint +GET https://api.github.com/repos/microsoft/binskim +GET https://api.github.com/repos/Azure/template-analyzer +``` + +Flag a tool if **any** of the following are true: +- `archived: true` — repo is archived (immediately flag as HIGH regardless of age) +- `pushed_at` is more than 6 months ago — no recent activity +- Latest release is more than 12 months old + +### Step 3: Assess impact + +For each finding, determine severity using the resolved version from Step 0 and the advisory's affected range: + +- **CRITICAL** — our pinned version exactly equals a known-bad version (e.g. the advisory names `trivy 0.69.3` and we have `0.69.3`), OR the supply chain was directly compromised (hijacked package, malicious release artifact). +- **HIGH** — our pinned version falls within the advisory's affected range but is not the exact named version (e.g. advisory says `>= 0.68.0, < 0.69.4` and we have `0.69.3`); or our pinned version is older than the version where the fix was released, even if no exact match. +- **MEDIUM** — theoretical / low-exploitability / version not confirmed in range. + +Also determine: +- **Triage — are we actually exposed?** Cross-reference the advisory description with how MSDO uses the tool. Note whether the vulnerable code path (e.g. a specific CLI flag, network listener, or parser) is reachable via normal MSDO execution. +- **Impact on MSDO**: Does this affect users of `microsoft/security-devops-action`? +- **Exploitability**: Active exploitation, PoC available, or theoretical? + +### Step 4: Check for duplicate issues before reporting + +Search for existing issues in this repository: +``` +GET https://api.github.com/repos/microsoft/security-devops-action/issues?labels=toolchain-alert&state=open +GET https://api.github.com/repos/microsoft/security-devops-action/issues?labels=toolchain-alert&state=closed&since=<30-days-ago> +``` + +For each finding, check whether the **specific CVE ID or GHSA ID** appears in any open or recently-closed (last 30 days) issue title or body. If it does, **skip that finding** — it has already been reported. + +### Step 5: Report or stay silent + +**If NO new incidents are found (or all are already reported):** +- Call `noop` with a one-line summary of what was checked. Silence means everything is clean. + +**If a new incident IS found:** +- Create exactly ONE issue combining all new findings. + +**Issue format:** + +**Title:** `[Toolchain Alert] : ` + +**Body:** +- **Affected tool(s)**: Name and resolved version from Step 0 +- **Severity**: Critical / High / Medium (with rationale — exact match vs. range match) +- **Summary**: What happened (2–3 sentences) +- **CVE/Advisory IDs**: GHSA-XXXX or CVE-XXXX — include full NVD link and CVSS base score +- **Vulnerability description**: What the CVE actually does — attack vector, what an attacker can achieve +- **Vulnerable version range**: Which versions are affected and which version contains the fix +- **Triage — are MSDO users exposed?**: Explain whether the vulnerable code path is reachable via normal MSDO usage. State explicitly: *"Exposed"* / *"Likely not exposed"* / *"Cannot determine"* with reasoning. +- **Impact on MSDO**: How this affects users of `microsoft/security-devops-action` +- **Recommended action**: Concrete steps for maintainers (e.g. bump pinned version in `.gdntool`, block the release) +- **Sources**: Links to advisories, NVD entries, upstream repo issues + +**Labels:** +- `security-breach` — confirmed supply chain compromise (hijacked package, malicious release, tag force-push) +- `supply-chain` — dependency-related incident (dependency confusion, typosquatting) +- `toolchain-alert` — ALWAYS applied +- Severity: `critical`, `high`, or `medium` + +## Rules + +1. **Stay silent when clean** — noop if nothing new found +2. **One issue per run** — combine all findings into one issue +3. **No re-reporting** — skip any finding whose CVE/GHSA ID already appears in an open or recently-closed (30 days) issue +4. **Ongoing = always report** — if a prior issue is open and the incident is still unresolved (C2 still active, malicious package still up), do NOT noop just because the original event was > 7 days ago +5. **Archived repo = always flag** — flag any archived tool repo as HIGH, regardless of when it was archived +6. **False positive discipline** — not every CVE warrants an alert; focus on incidents where the resolved version from Step 0 falls within the vulnerable range, or where the supply chain (package index, release artifact, repo tags) was directly compromised +7. **Be specific** — include CVE/GHSA IDs, exact version numbers, and advisory links diff --git a/.github/workflows/msdo-issue-assistant.lock.yml b/.github/workflows/msdo-issue-assistant.lock.yml index 70da196a..e412326d 100644 --- a/.github/workflows/msdo-issue-assistant.lock.yml +++ b/.github/workflows/msdo-issue-assistant.lock.yml @@ -1,4 +1,3 @@ -# # ___ _ _ # / _ \ | | (_) # | |_| | __ _ ___ _ __ | |_ _ ___ @@ -13,7 +12,7 @@ # \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ # \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ # -# This file was automatically generated by gh-aw (v0.43.23). DO NOT EDIT. +# This file was automatically generated by gh-aw (v0.61.0). DO NOT EDIT. # # To update this file, edit the corresponding .md file and run: # gh aw compile @@ -22,7 +21,7 @@ # For more information: https://github.github.com/gh-aw/introduction/overview/ # # -# frontmatter-hash: ec5b4527a6199a05f3a36752477dac71dd9eaaa688d63482f19275f580ae9b5f +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"ad862ac3404b6a5b9235e75266770d3df954d43cd43d766661be878e414e622b","compiler_version":"v0.61.0","strict":true} name: "MSDO Issue Triage Assistant" "on": @@ -32,12 +31,13 @@ name: "MSDO Issue Triage Assistant" issues: types: - opened + # roles: all # Roles processed as role check in pre-activation job workflow_dispatch: permissions: {} concurrency: - group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number }}" + group: "gh-aw-${{ github.workflow }}-${{ github.event.issue.number || github.run_id }}" run-name: "MSDO Issue Triage Assistant" @@ -47,15 +47,58 @@ jobs: permissions: contents: read outputs: + body: ${{ steps.sanitized.outputs.body }} comment_id: "" comment_repo: "" + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + text: ${{ steps.sanitized.outputs.text }} + title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@32b3a711a9ee97d38e3989c90af0385aff0066a7 # v0.57.2 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions + - name: Generate agentic run info + id: generate_aw_info + env: + GH_AW_INFO_ENGINE_ID: "copilot" + GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI" + GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_INFO_VERSION: "" + GH_AW_INFO_AGENT_VERSION: "latest" + GH_AW_INFO_CLI_VERSION: "v0.61.0" + GH_AW_INFO_WORKFLOW_NAME: "MSDO Issue Triage Assistant" + GH_AW_INFO_EXPERIMENTAL: "false" + GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true" + GH_AW_INFO_STAGED: "false" + GH_AW_INFO_ALLOWED_DOMAINS: '["github"]' + GH_AW_INFO_FIREWALL_ENABLED: "true" + GH_AW_INFO_AWF_VERSION: "v0.24.2" + GH_AW_INFO_AWMG_VERSION: "" + GH_AW_INFO_FIREWALL_TYPE: "squid" + GH_AW_COMPILED_STRICT: "true" + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs'); + await main(core, context); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Checkout .github and .agents folders + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + sparse-checkout: | + .github + .agents + sparse-checkout-cone-mode: true + fetch-depth: 1 - name: Check workflow file timestamps - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_WORKFLOW_FILE: "msdo-issue-assistant.lock.yml" with: @@ -64,6 +107,144 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); await main(); + - name: Compute current body text + id: sanitized + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/compute_text.cjs'); + await main(); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + { + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/xpia.md" + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" + cat "/opt/gh-aw/prompts/markdown.md" + cat "/opt/gh-aw/prompts/safe_outputs_prompt.md" + cat << 'GH_AW_PROMPT_EOF' + + Tools: add_comment, add_labels, missing_tool, missing_data, noop + + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + GH_AW_PROMPT_EOF + cat "/opt/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" + if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then + cat "/opt/gh-aw/prompts/pr_context_prompt.md" + fi + cat << 'GH_AW_PROMPT_EOF' + + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' + {{#runtime-import .github/workflows/msdo-issue-assistant.md}} + GH_AW_PROMPT_EOF + } > "$GH_AW_PROMPT" + - name: Interpolate variables and render templates + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Substitute placeholders + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, + GH_AW_IS_PR_COMMENT: process.env.GH_AW_IS_PR_COMMENT + } + }); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Upload activation artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: activation + path: | + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/aw-prompts/prompt.txt + retention-days: 1 agent: needs: activation @@ -83,14 +264,16 @@ jobs: GH_AW_WORKFLOW_ID_SANITIZED: msdoissueassistant outputs: checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }} + detection_success: ${{ steps.detection_conclusion.outputs.success }} has_patch: ${{ steps.collect_output.outputs.has_patch }} - model: ${{ steps.generate_aw_info.outputs.model }} + inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }} + model: ${{ needs.activation.outputs.model }} output: ${{ steps.collect_output.outputs.output }} output_types: ${{ steps.collect_output.outputs.output_types }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@32b3a711a9ee97d38e3989c90af0385aff0066a7 # v0.57.2 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions - name: Checkout repository @@ -99,6 +282,10 @@ jobs: persist-credentials: false - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure gh CLI for GitHub Enterprise + run: bash /opt/gh-aw/actions/configure_gh_for_ghe.sh + env: + GH_TOKEN: ${{ github.token }} - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -106,6 +293,7 @@ jobs: run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" + git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" @@ -113,8 +301,8 @@ jobs: - name: Checkout PR branch id: checkout-pr if: | - github.event.pull_request - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + (github.event.pull_request) || (github.event.issue.pull_request) + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: @@ -124,168 +312,44 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); await main(); - - name: Generate agentic run info - id: generate_aw_info - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh latest + env: + GH_HOST: github.com + - name: Install AWF binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.24.2 + - name: Determine automatic lockdown mode for GitHub MCP Server + id: determine-automatic-lockdown + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} with: script: | - const fs = require('fs'); - - const awInfo = { - engine_id: "copilot", - engine_name: "GitHub Copilot CLI", - model: process.env.GH_AW_MODEL_AGENT_COPILOT || "", - version: "", - agent_version: "0.0.409", - cli_version: "v0.43.23", - workflow_name: "MSDO Issue Triage Assistant", - experimental: false, - supports_tools_allowlist: true, - supports_http_transport: true, - run_id: context.runId, - run_number: context.runNumber, - run_attempt: process.env.GITHUB_RUN_ATTEMPT, - repository: context.repo.owner + '/' + context.repo.repo, - ref: context.ref, - sha: context.sha, - actor: context.actor, - event_name: context.eventName, - staged: false, - allowed_domains: ["github"], - firewall_enabled: true, - awf_version: "v0.17.0", - awmg_version: "", - steps: { - firewall: "squid" - }, - created_at: new Date().toISOString() - }; - - // Write to /tmp/gh-aw directory to avoid inclusion in PR - const tmpPath = '/tmp/gh-aw/aw_info.json'; - fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); - console.log('Generated aw_info.json at:', tmpPath); - console.log(JSON.stringify(awInfo, null, 2)); - - // Set model as output for reuse in other steps/jobs - core.setOutput('model', awInfo.model); - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.409 - - name: Install awf binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.17.0 + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.17.0 ghcr.io/github/gh-aw-firewall/squid:0.17.0 ghcr.io/github/gh-aw-mcpg:v0.1.4 ghcr.io/github/github-mcp-server:v0.30.3 node:lts-alpine + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.24.2 ghcr.io/github/gh-aw-firewall/api-proxy:0.24.2 ghcr.io/github/gh-aw-firewall/squid:0.24.2 ghcr.io/github/gh-aw-mcpg:v0.1.15 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine - name: Write Safe Outputs Config run: | mkdir -p /opt/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":4},"add_labels":{"allowed":["type:bug","type:feature","type:docs","type:question","type:security","type:maintenance","status:triage","status:waiting-on-author","status:repro-needed","status:team-review"],"max":3},"missing_data":{},"missing_tool":{}} + {"add_comment":{"max":4},"add_labels":{"allowed":["type:bug","type:feature","type:docs","type:question","type:security","type:maintenance","status:triage","status:waiting-on-author","status:repro-needed","status:team-review","area:action","area:msdo-cli","area:ci","area:container-mapping"],"max":3},"missing_data":{},"missing_tool":{},"noop":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF - cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' - [ - { - "description": "Add a comment to an existing GitHub issue, pull request, or discussion. Use this to provide feedback, answer questions, or add information to an existing conversation. For creating new items, use create_issue, create_discussion, or create_pull_request instead. CONSTRAINTS: Maximum 4 comment(s) can be added.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "body": { - "description": "The comment text in Markdown format. This is the 'body' field - do not use 'comment_body' or other variations. Provide helpful, relevant information that adds value to the conversation.", - "type": "string" - }, - "item_number": { - "description": "The issue, pull request, or discussion number to comment on. This is the numeric ID from the GitHub URL (e.g., 123 in github.com/owner/repo/issues/123). If omitted, the tool will attempt to resolve the target from the current workflow context (triggering issue, PR, or discussion).", - "type": "number" - } - }, - "required": [ - "body" - ], - "type": "object" - }, - "name": "add_comment" - }, - { - "description": "Add labels to an existing GitHub issue or pull request for categorization and filtering. Labels must already exist in the repository. For creating new issues with labels, use create_issue with the labels property instead. CONSTRAINTS: Only these labels are allowed: [type:bug type:feature type:docs type:question type:security type:maintenance status:triage status:waiting-on-author status:repro-needed status:team-review].", - "inputSchema": { - "additionalProperties": false, - "properties": { - "item_number": { - "description": "Issue or PR number to add labels to. This is the numeric ID from the GitHub URL (e.g., 456 in github.com/owner/repo/issues/456). If omitted, adds labels to the item that triggered this workflow.", - "type": "number" - }, - "labels": { - "description": "Label names to add (e.g., ['bug', 'priority-high']). Labels must exist in the repository.", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "name": "add_labels" - }, - { - "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "reason": { - "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).", - "type": "string" - }, - "tool": { - "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.", - "type": "string" - } - }, - "required": [ - "reason" - ], - "type": "object" - }, - "name": "missing_tool" + - name: Write Safe Outputs Tools + run: | + cat > /opt/gh-aw/safeoutputs/tools_meta.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF' + { + "description_suffixes": { + "add_comment": " CONSTRAINTS: Maximum 4 comment(s) can be added.", + "add_labels": " CONSTRAINTS: Only these labels are allowed: [\"type:bug\" \"type:feature\" \"type:docs\" \"type:question\" \"type:security\" \"type:maintenance\" \"status:triage\" \"status:waiting-on-author\" \"status:repro-needed\" \"status:team-review\" \"area:action\" \"area:msdo-cli\" \"area:ci\" \"area:container-mapping\"]." }, - { - "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.", - "inputSchema": { - "additionalProperties": false, - "properties": { - "alternatives": { - "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).", - "type": "string" - }, - "context": { - "description": "Additional context about the missing data or where it should come from (max 256 characters).", - "type": "string" - }, - "data_type": { - "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.", - "type": "string" - }, - "reason": { - "description": "Explanation of why this data is needed to complete the task (max 256 characters).", - "type": "string" - } - }, - "required": [], - "type": "object" - }, - "name": "missing_data" - } - ] - GH_AW_SAFE_OUTPUTS_TOOLS_EOF + "repo_params": {}, + "dynamic_tools": [] + } + GH_AW_SAFE_OUTPUTS_TOOLS_META_EOF cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF' { "add_comment": { @@ -299,6 +363,10 @@ jobs: }, "item_number": { "issueOrPRNumber": true + }, + "repo": { + "type": "string", + "maxLength": 256 } } }, @@ -306,7 +374,7 @@ jobs: "defaultMax": 5, "fields": { "item_number": { - "issueOrPRNumber": true + "issueNumberOrTemporaryId": true }, "labels": { "required": true, @@ -314,6 +382,35 @@ jobs: "itemType": "string", "itemSanitize": true, "itemMaxLength": 128 + }, + "repo": { + "type": "string", + "maxLength": 256 + } + } + }, + "missing_data": { + "defaultMax": 20, + "fields": { + "alternatives": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "context": { + "type": "string", + "sanitize": true, + "maxLength": 256 + }, + "data_type": { + "type": "string", + "sanitize": true, + "maxLength": 128 + }, + "reason": { + "type": "string", + "sanitize": true, + "maxLength": 256 } } }, @@ -337,9 +434,21 @@ jobs: "maxLength": 128 } } + }, + "noop": { + "defaultMax": 1, + "fields": { + "message": { + "required": true, + "type": "string", + "sanitize": true, + "maxLength": 65000 + } + } } } GH_AW_SAFE_OUTPUTS_VALIDATION_EOF + node /opt/gh-aw/actions/generate_safe_outputs_tools.cjs - name: Generate Safe Outputs MCP Server Config id: safe-outputs-config run: | @@ -378,12 +487,14 @@ jobs: bash /opt/gh-aw/actions/start_safe_outputs_server.sh - - name: Start MCP gateway + - name: Start MCP Gateway id: start-mcp-gateway env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }} GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }} + GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }} + GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }} GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} run: | set -eo pipefail @@ -397,10 +508,11 @@ jobs: export MCP_GATEWAY_API_KEY export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288" export DEBUG="*" export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.4' + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.1.15' mkdir -p /home/runner/.copilot cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh @@ -408,11 +520,18 @@ jobs: "mcpServers": { "github": { "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v0.30.3", + "container": "ghcr.io/github/github-mcp-server:v0.32.0", "env": { + "GITHUB_HOST": "\${GITHUB_SERVER_URL}", "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", "GITHUB_READ_ONLY": "1", "GITHUB_TOOLSETS": "issues" + }, + "guard-policies": { + "allow-only": { + "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY", + "repos": "$GITHUB_MCP_GUARD_REPOS" + } } }, "safeoutputs": { @@ -420,6 +539,13 @@ jobs: "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT", "headers": { "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}" + }, + "guard-policies": { + "write-sink": { + "accept": [ + "*" + ] + } } } }, @@ -431,151 +557,13 @@ jobs: } } GH_AW_MCP_CONFIG_EOF - - name: Generate workflow overview - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); - await generateWorkflowOverview(core); - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }} - run: | - bash /opt/gh-aw/actions/create_prompt_first.sh - cat << 'GH_AW_PROMPT_EOF' > "$GH_AW_PROMPT" - - GH_AW_PROMPT_EOF - cat "/opt/gh-aw/prompts/xpia.md" >> "$GH_AW_PROMPT" - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT" - cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT" - cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" - - GitHub API Access Instructions - - The gh CLI is NOT authenticated. Do NOT use gh commands for GitHub operations. - - - To create or modify GitHub resources (issues, discussions, pull requests, etc.), you MUST call the appropriate safe output tool. Simply writing content will NOT work - the workflow requires actual tool calls. - - Temporary IDs: Some safe output tools support a temporary ID field (usually named temporary_id) so you can reference newly-created items elsewhere in the SAME agent output (for example, using #aw_abc1 in a later body). - - **IMPORTANT - temporary_id format rules:** - - If you DON'T need to reference the item later, OMIT the temporary_id field entirely (it will be auto-generated if needed) - - If you DO need cross-references/chaining, you MUST match this EXACT validation regex: /^aw_[A-Za-z0-9]{3,8}$/i - - Format: aw_ prefix followed by 3 to 8 alphanumeric characters (A-Z, a-z, 0-9, case-insensitive) - - Valid alphanumeric characters: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 - - INVALID examples: aw_ab (too short), aw_123456789 (too long), aw_test-id (contains hyphen), aw_id_123 (contains underscore) - - VALID examples: aw_abc, aw_abc1, aw_Test123, aw_A1B2C3D4, aw_12345678 - - To generate valid IDs: use 3-8 random alphanumeric characters or omit the field to let the system auto-generate - - Do NOT invent other aw_* formats — downstream steps will reject them with validation errors matching against /^aw_[A-Za-z0-9]{3,8}$/i. - - Discover available tools from the safeoutputs MCP server. - - **Critical**: Tool calls write structured data that downstream jobs process. Without tool calls, follow-up actions will be skipped. - - **Note**: If you made no other safe output tool calls during this workflow execution, call the "noop" tool to provide a status message indicating completion or that no actions were needed. - - - - The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ - {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - GH_AW_PROMPT_EOF - if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then - cat "/opt/gh-aw/prompts/pr_context_prompt.md" >> "$GH_AW_PROMPT" - fi - cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" - - GH_AW_PROMPT_EOF - cat << 'GH_AW_PROMPT_EOF' >> "$GH_AW_PROMPT" - {{#runtime-import .github/workflows/msdo-issue-assistant.md}} - GH_AW_PROMPT_EOF - - name: Substitute placeholders - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - GH_AW_IS_PR_COMMENT: ${{ github.event.issue.pull_request && 'true' || '' }} + - name: Download activation artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - script: | - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE, - GH_AW_IS_PR_COMMENT: process.env.GH_AW_IS_PR_COMMENT - } - }); - - name: Interpolate variables and render templates - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh + name: activation + path: /tmp/gh-aw - name: Clean git credentials + continue-on-error: true run: bash /opt/gh-aw/actions/clean_git_credentials.sh - name: Execute GitHub Copilot CLI id: agentic_execution @@ -583,21 +571,37 @@ jobs: timeout-minutes: 20 run: | set -o pipefail - sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains '*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.17.0 --skip-pull \ - -- '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \ - 2>&1 | tee /tmp/gh-aw/agent-stdio.log + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: COPILOT_AGENT_RUNNER_TYPE: STANDALONE COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json - GH_AW_MODEL_AGENT_COPILOT: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }} + GH_AW_PHASE: agent GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} + GH_AW_VERSION: v0.61.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] XDG_CONFIG_HOME: /home/runner + - name: Detect inference access error + id: detect-inference-error + if: always() + continue-on-error: true + run: bash /opt/gh-aw/actions/detect_inference_access_error.sh - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} @@ -605,6 +609,7 @@ jobs: run: | git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global user.name "github-actions[bot]" + git config --global am.keepcr true # Re-authenticate git with GitHub token SERVER_URL_STRIPPED="${SERVER_URL#https://}" git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" @@ -626,7 +631,7 @@ jobs: else echo "No session-state directory found at $SESSION_STATE_DIR" fi - - name: Stop MCP gateway + - name: Stop MCP Gateway if: always() continue-on-error: true env: @@ -637,7 +642,7 @@ jobs: bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - name: Redact secrets in logs if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); @@ -650,19 +655,21 @@ jobs: SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload Safe Outputs + - name: Append agent step summary if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: safe-output - path: ${{ env.GH_AW_SAFE_OUTPUTS }} - if-no-files-found: warn + run: bash /opt/gh-aw/actions/append_agent_step_summary.sh + - name: Copy Safe Outputs + if: always() + run: | + mkdir -p /tmp/gh-aw + cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true - name: Ingest agent output id: collect_output - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + if: always() + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }} - GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} with: @@ -671,24 +678,9 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/collect_ndjson_output.cjs'); await main(); - - name: Upload sanitized agent output - if: always() && env.GH_AW_AGENT_OUTPUT - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: agent-output - path: ${{ env.GH_AW_AGENT_OUTPUT }} - if-no-files-found: warn - - name: Upload engine output files - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - name: Parse agent logs for step summary if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ with: @@ -697,9 +689,9 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); await main(); - - name: Parse MCP gateway logs for step summary + - name: Parse MCP Gateway logs for step summary if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); @@ -715,27 +707,158 @@ jobs: # Fix permissions on firewall logs so they can be uploaded as artifacts # AWF runs with sudo, creating files owned by root sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true - awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step) + if command -v awf &> /dev/null; then + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + else + echo 'AWF binary not installed, skipping firewall log summary' + fi - name: Upload agent artifacts if: always() continue-on-error: true uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: - name: agent-artifacts + name: agent path: | /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/aw_info.json + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log /tmp/gh-aw/mcp-logs/ /tmp/gh-aw/sandbox/firewall/logs/ /tmp/gh-aw/agent-stdio.log /tmp/gh-aw/agent/ + /tmp/gh-aw/safeoutputs.jsonl + /tmp/gh-aw/agent_output.json if-no-files-found: ignore + # --- Threat Detection (inline) --- + - name: Check if detection needed + id: detection_guard + if: always() + env: + OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }} + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + run: | + if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then + echo "run_detection=true" >> "$GITHUB_OUTPUT" + echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH" + else + echo "run_detection=false" >> "$GITHUB_OUTPUT" + echo "Detection skipped: no agent outputs or patches to analyze" + fi + - name: Clear MCP configuration for detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + rm -f /tmp/gh-aw/mcp-config/mcp-servers.json + rm -f /home/runner/.copilot/mcp-config.json + rm -f "$GITHUB_WORKSPACE/.gemini/settings.json" + - name: Prepare threat detection files + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection/aw-prompts + cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true + cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true + for f in /tmp/gh-aw/aw-*.patch; do + [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true + done + echo "Prepared threat detection files:" + ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true + - name: Setup threat detection + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + WORKFLOW_NAME: "MSDO Issue Triage Assistant" + WORKFLOW_DESCRIPTION: "No description provided" + HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); + await main(); + - name: Ensure threat-detection directory and log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + run: | + mkdir -p /tmp/gh-aw/threat-detection + touch /tmp/gh-aw/threat-detection/detection.log + - name: Execute GitHub Copilot CLI + if: always() && steps.detection_guard.outputs.run_detection == 'true' + id: detection_agentic_execution + # Copilot CLI tool arguments (sorted): + # --allow-tool shell(cat) + # --allow-tool shell(grep) + # --allow-tool shell(head) + # --allow-tool shell(jq) + # --allow-tool shell(ls) + # --allow-tool shell(tail) + # --allow-tool shell(wc) + timeout-minutes: 20 + run: | + set -o pipefail + touch /tmp/gh-aw/agent-step-summary.md + # shellcheck disable=SC1003 + sudo -E awf --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.24.2 --skip-pull --enable-api-proxy \ + -- /bin/bash -c '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} + GH_AW_PHASE: detection + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_VERSION: v0.61.0 + GITHUB_API_URL: ${{ github.api_url }} + GITHUB_AW: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md + GITHUB_WORKSPACE: ${{ github.workspace }} + GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_AUTHOR_NAME: github-actions[bot] + GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com + GIT_COMMITTER_NAME: github-actions[bot] + XDG_CONFIG_HOME: /home/runner + - name: Parse threat detection results + id: parse_detection_results + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); + await main(); + - name: Upload threat detection log + if: always() && steps.detection_guard.outputs.run_detection == 'true' + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: detection + path: /tmp/gh-aw/threat-detection/detection.log + if-no-files-found: ignore + - name: Set detection conclusion + id: detection_conclusion + if: always() + env: + RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }} + DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }} + run: | + if [[ "$RUN_DETECTION" != "true" ]]; then + echo "conclusion=skipped" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection was not needed, marking as skipped" + elif [[ "$DETECTION_SUCCESS" == "true" ]]; then + echo "conclusion=success" >> "$GITHUB_OUTPUT" + echo "success=true" >> "$GITHUB_OUTPUT" + echo "Detection passed successfully" + else + echo "conclusion=failure" >> "$GITHUB_OUTPUT" + echo "success=false" >> "$GITHUB_OUTPUT" + echo "Detection found issues" + fi conclusion: needs: - activation - agent - - detection - safe_outputs if: (always()) && (needs.agent.result != 'skipped') runs-on: ubuntu-slim @@ -744,28 +867,48 @@ jobs: discussions: write issues: write pull-requests: write + concurrency: + group: "gh-aw-conclusion-msdo-issue-assistant" + cancel-in-progress: false outputs: + noop_message: ${{ steps.noop.outputs.noop_message }} tools_reported: ${{ steps.missing_tool.outputs.tools_reported }} total_count: ${{ steps.missing_tool.outputs.total_count }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@32b3a711a9ee97d38e3989c90af0385aff0066a7 # v0.57.2 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions - name: Download agent output artifact + id: download-agent-output continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ + name: agent + path: /tmp/gh-aw/ - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" + - name: Process No-Op Messages + id: noop + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} + GH_AW_NOOP_MAX: "1" + GH_AW_WORKFLOW_NAME: "MSDO Issue Triage Assistant" + with: + github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/noop.cjs'); + await main(); - name: Record Missing Tool id: missing_tool - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "MSDO Issue Triage Assistant" @@ -778,15 +921,19 @@ jobs: await main(); - name: Handle Agent Failure id: handle_agent_failure - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "MSDO Issue Triage Assistant" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_WORKFLOW_ID: "msdo-issue-assistant" - GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.agent.outputs.secret_verification_result }} + GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }} GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }} + GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} + GH_AW_GROUP_REPORTS: "false" + GH_AW_FAILURE_REPORT_AS_ISSUE: "false" + GH_AW_TIMEOUT_MINUTES: "20" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -796,12 +943,14 @@ jobs: await main(); - name: Handle No-Op Message id: handle_noop_message - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} GH_AW_WORKFLOW_NAME: "MSDO Issue Triage Assistant" GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} + GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} + GH_AW_NOOP_REPORT_AS_ISSUE: "false" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -809,129 +958,10 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/handle_noop_message.cjs'); await main(); - - name: Update reaction comment with completion status - id: conclusion - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_COMMENT_ID: ${{ needs.activation.outputs.comment_id }} - GH_AW_COMMENT_REPO: ${{ needs.activation.outputs.comment_repo }} - GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - GH_AW_WORKFLOW_NAME: "MSDO Issue Triage Assistant" - GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} - GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.result }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/notify_comment_error.cjs'); - await main(); - - detection: - needs: agent - if: needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true' - runs-on: ubuntu-latest - permissions: {} - timeout-minutes: 10 - outputs: - success: ${{ steps.parse_results.outputs.success }} - steps: - - name: Setup Scripts - uses: github/gh-aw/actions/setup@32b3a711a9ee97d38e3989c90af0385aff0066a7 # v0.57.2 - with: - destination: /opt/gh-aw/actions - - name: Download agent artifacts - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 - with: - name: agent-artifacts - path: /tmp/gh-aw/threat-detection/ - - name: Download agent output artifact - continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 - with: - name: agent-output - path: /tmp/gh-aw/threat-detection/ - - name: Echo agent output types - env: - AGENT_OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }} - run: | - echo "Agent output-types: $AGENT_OUTPUT_TYPES" - - name: Setup threat detection - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - WORKFLOW_NAME: "MSDO Issue Triage Assistant" - WORKFLOW_DESCRIPTION: "No description provided" - HAS_PATCH: ${{ needs.agent.outputs.has_patch }} - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/setup_threat_detection.cjs'); - await main(); - - name: Ensure threat-detection directory and log - run: | - mkdir -p /tmp/gh-aw/threat-detection - touch /tmp/gh-aw/threat-detection/detection.log - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.409 - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - # --allow-tool shell(cat) - # --allow-tool shell(grep) - # --allow-tool shell(head) - # --allow-tool shell(jq) - # --allow-tool shell(ls) - # --allow-tool shell(tail) - # --allow-tool shell(wc) - timeout-minutes: 20 - run: | - set -o pipefail - COPILOT_CLI_INSTRUCTION="$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" - mkdir -p /tmp/ - mkdir -p /tmp/gh-aw/ - mkdir -p /tmp/gh-aw/agent/ - mkdir -p /tmp/gh-aw/sandbox/agent/logs/ - copilot --add-dir /tmp/ --add-dir /tmp/gh-aw/ --add-dir /tmp/gh-aw/agent/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --allow-tool 'shell(cat)' --allow-tool 'shell(grep)' --allow-tool 'shell(head)' --allow-tool 'shell(jq)' --allow-tool 'shell(ls)' --allow-tool 'shell(tail)' --allow-tool 'shell(wc)' --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$COPILOT_CLI_INSTRUCTION"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"} 2>&1 | tee /tmp/gh-aw/threat-detection/detection.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - GH_AW_MODEL_DETECTION_COPILOT: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }} - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Parse threat detection results - id: parse_results - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_threat_detection_results.cjs'); - await main(); - - name: Upload threat detection log - if: always() - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 - with: - name: threat-detection.log - path: /tmp/gh-aw/threat-detection/detection.log - if-no-files-found: ignore safe_outputs: - needs: - - agent - - detection - if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') + needs: agent + if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true') runs-on: ubuntu-slim permissions: contents: read @@ -940,36 +970,46 @@ jobs: pull-requests: write timeout-minutes: 15 env: + GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/msdo-issue-assistant" GH_AW_ENGINE_ID: "copilot" GH_AW_WORKFLOW_ID: "msdo-issue-assistant" GH_AW_WORKFLOW_NAME: "MSDO Issue Triage Assistant" outputs: + code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }} + code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }} + comment_id: ${{ steps.process_safe_outputs.outputs.comment_id }} + comment_url: ${{ steps.process_safe_outputs.outputs.comment_url }} create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }} create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }} process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }} process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }} steps: - name: Setup Scripts - uses: github/gh-aw/actions/setup@32b3a711a9ee97d38e3989c90af0385aff0066a7 # v0.57.2 + uses: github/gh-aw-actions/setup@df014dd7d03b638e860b2aeca95c833fd97c8cf1 # v0.61.0 with: destination: /opt/gh-aw/actions - name: Download agent output artifact + id: download-agent-output continue-on-error: true - uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: agent-output - path: /tmp/gh-aw/safeoutputs/ + name: agent + path: /tmp/gh-aw/ - name: Setup agent output environment variable + if: steps.download-agent-output.outcome == 'success' run: | - mkdir -p /tmp/gh-aw/safeoutputs/ - find "/tmp/gh-aw/safeoutputs/" -type f -print - echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV" + mkdir -p /tmp/gh-aw/ + find "/tmp/gh-aw/" -type f -print + echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_ENV" - name: Process Safe Outputs id: process_safe_outputs - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":4},\"add_labels\":{\"allowed\":[\"type:bug\",\"type:feature\",\"type:docs\",\"type:question\",\"type:security\",\"type:maintenance\",\"status:triage\",\"status:waiting-on-author\",\"status:repro-needed\",\"status:team-review\"]},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,codeload.github.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,lfs.github.com,objects.githubusercontent.com,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" + GITHUB_SERVER_URL: ${{ github.server_url }} + GITHUB_API_URL: ${{ github.api_url }} + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"max\":4},\"add_labels\":{\"allowed\":[\"type:bug\",\"type:feature\",\"type:docs\",\"type:question\",\"type:security\",\"type:maintenance\",\"status:triage\",\"status:waiting-on-author\",\"status:repro-needed\",\"status:team-review\",\"area:action\",\"area:msdo-cli\",\"area:ci\",\"area:container-mapping\"]},\"missing_data\":{},\"missing_tool\":{}}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -977,4 +1017,11 @@ jobs: setupGlobals(core, github, context, exec, io); const { main } = require('/opt/gh-aw/actions/safe_output_handler_manager.cjs'); await main(); + - name: Upload Safe Output Items Manifest + if: always() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: safe-output-items + path: /tmp/safe-output-items.jsonl + if-no-files-found: warn diff --git a/.github/workflows/msdo-issue-assistant.md b/.github/workflows/msdo-issue-assistant.md index d1b78d83..8f5ba757 100644 --- a/.github/workflows/msdo-issue-assistant.md +++ b/.github/workflows/msdo-issue-assistant.md @@ -1,6 +1,12 @@ --- # MSDO Issue Assistant - GitHub Agentic Workflow # Automatically triage and respond to issues using wiki knowledge +# +# MAINTENANCE NOTE: after running `gh aw compile` with gh-aw v0.61.0, verify +# that the `actions/github-script` SHA in the generated .lock.yml stays pinned +# to v9.0.0 (`3a2844b7e9c422d3c10d287c895573f7108da1b3`). v0.61.0's bundled +# scaffolding emits the older v8 SHA and would silently revert PR #244. See +# PR #252 for context. on: issues: @@ -8,8 +14,7 @@ on: issue_comment: types: [created] workflow_dispatch: - -roles: all + roles: all engine: id: copilot @@ -27,15 +32,17 @@ tools: lockdown: false toolsets: [issues] fetch: - allowed-domains: + allowed: - raw.githubusercontent.com safe-outputs: - noop: false + noop: + report-as-issue: false + report-failure-as-issue: false add-comment: max: 4 add-labels: - allowed: ["type:bug", "type:feature", "type:docs", "type:question", "type:security", "type:maintenance", "status:triage", "status:waiting-on-author", "status:repro-needed", "status:team-review"] + allowed: ["type:bug", "type:feature", "type:docs", "type:question", "type:security", "type:maintenance", "status:triage", "status:waiting-on-author", "status:repro-needed", "status:team-review", "area:action", "area:msdo-cli", "area:ci", "area:container-mapping"] --- @@ -45,9 +52,10 @@ You are an issue triage assistant for the **Microsoft Security DevOps (MSDO)** C ## Your Knowledge Base -Before responding, fetch wiki content from: +Use the fetch tool to retrieve these wiki pages before responding: - https://raw.githubusercontent.com/wiki/microsoft/security-devops-action/Home.md - https://raw.githubusercontent.com/wiki/microsoft/security-devops-action/FAQ.md +- https://raw.githubusercontent.com/wiki/microsoft/security-devops-action/Tool-Configuration.md MSDO is a command line tool that integrates security analysis tools into CI/CD pipelines. @@ -63,6 +71,77 @@ MSDO is a command line tool that integrates security analysis tools into CI/CD p **Wiki reference:** https://github.com/microsoft/security-devops-action/wiki +## Tool Configuration Reference + +MSDO supports passing arguments to individual tools via environment variables or `.gdnconfig` files. + +**Environment variable pattern:** `GDN__` + +Where `` is uppercase and `` is PascalCase with no separators. + +**Common examples:** + +Checkov: +```yaml +env: + GDN_CHECKOV_DOWNLOADEXTERNALMODULES: "true" # download external Terraform modules + GDN_CHECKOV_FRAMEWORK: "terraform" # limit scan to specific framework + GDN_CHECKOV_SKIPCHECK: "CKV_AWS_1,CKV_AWS_2" # skip specific checks + GDN_CHECKOV_CONFIGFILE: ".checkov.yml" # use a checkov config file +``` + +Trivy: +```yaml +env: + GDN_TRIVY_SEVERITIES: "HIGH,CRITICAL" # filter by severity + GDN_TRIVY_IGNOREUNFIXED: "true" # ignore unfixed vulnerabilities + GDN_TRIVY_SCANNERS: "vuln,secret" # specify scanner types +``` + +ESLint: +```yaml +env: + GDN_ESLINT_CONFIGURATIONFILE: ".eslintrc.js" # custom ESLint config + GDN_ESLINT_QUIET: "true" # suppress warnings +``` + +Terrascan: +```yaml +env: + GDN_TERRASCAN_IACTYPE: "terraform" # specify IaC type + GDN_TERRASCAN_SEVERITY: "HIGH" # minimum severity + GDN_TERRASCAN_SKIPRULES: "AC_AWS_001" # skip specific rules +``` + +**`.gdnconfig` alternative** (for complex multi-tool configs): +```json +{ + "fileVersion": "1.0.0", + "jobs": [{ + "tools": [{ + "tool": { "name": "checkov" }, + "arguments": { + "DownloadExternalModules": { "values": ["true"] }, + "Framework": { "values": ["terraform"] } + } + }] + }] +} +``` + +Referenced via: +```yaml +- uses: microsoft/security-devops-action@latest + with: + config: '.msdo.gdnconfig' +``` + +When a user asks about tool-specific flags or arguments: +1. Suggest the environment variable approach first (simplest) +2. Mention `.gdnconfig` as an alternative for complex setups +3. Link to the [Tool Configuration wiki page](https://github.com/microsoft/security-devops-action/wiki/Tool-Configuration) +4. Add the `area:msdo-cli` label since tool configuration is handled by the CLI + ## Your Task When a new issue is opened or a user comments: @@ -107,12 +186,13 @@ Keep responses: - learn.microsoft.com - docs.microsoft.com - aka.ms -3. **Stay on topic** - Only respond to issues related to MSDO, security-devops-action, or the supported security tools. If an issue is unrelated (e.g. general GitHub Actions questions, unrelated security tools, off-topic discussions), do not respond. -4. **Don't respond** if: +3. **Stay on topic** - Only respond to issues related to MSDO, security-devops-action, or the supported security tools. If an issue is unrelated (e.g. general GitHub Actions questions, unrelated security tools, off-topic discussions), call `noop` with a reason — see rule 4. +4. **Call `noop` instead of staying silent** when any of these apply. Pass a one-line reason so the decision is auditable: - The issue is not related to MSDO or security-devops-action + - The issue title starts with `[aw]` or is labeled `agentic-workflows` (auto-generated failure reports, not user issues) - The issue is closed - The commenter is not the issue author (unless it's a new issue) - - You've already responded twice and there is no new technical information in the latest user message + - You have already responded twice and there is no new technical information in the latest user message - The issue has a `status:team-review` label (a maintainer is handling it) 5. **Be honest** - if you don't know something, say so and suggest checking the wiki or waiting for a maintainer @@ -121,19 +201,25 @@ Keep responses: **User asks:** "What tools does MSDO support?" **Response:** MSDO supports these security analysis tools: antimalware (Windows only), bandit, binskim, checkov, eslint, templateanalyzer, terrascan, and trivy. Tools are automatically detected based on your repository content, or you can specify them explicitly. See the [Tools documentation](https://github.com/microsoft/security-devops-action/wiki) for details. +**User asks:** "How do I pass --download-external-modules to checkov?" +**Response:** You can enable this by setting an environment variable in your workflow: `GDN_CHECKOV_DOWNLOADEXTERNALMODULES: "true"` in the `env:` block of the MSDO action step. MSDO supports passing arguments to tools via the `GDN__` pattern. See the [Tool Configuration](https://github.com/microsoft/security-devops-action/wiki/Tool-Configuration) wiki page for more examples. + **User reports:** "Trivy is failing with container image not found" **Response:** This error typically occurs when Docker isn't available. Trivy requires Docker for container scanning. Please ensure you have `docker/setup-buildx-action@v3` in your workflow before the MSDO action. Can you share your workflow YAML so I can help verify the configuration? -## Do NOT Respond Examples +## Noop Examples **Off-topic issue:** "How do I set up GitHub Actions for deploying to AWS?" -→ Do not respond. This is unrelated to MSDO. +→ Call `noop` with reason "off-topic — unrelated to MSDO". **Issue labeled `status:team-review`:** Any issue with this label. -→ Do not respond. A maintainer is already handling it. +→ Call `noop` with reason "status:team-review — maintainer is handling it". **Repeated comments with no new info:** User says "Any update?" or "bump" after you already responded. -→ Do not respond. No new technical information to act on. +→ Call `noop` with reason "no new technical information since prior response". **Non-author comment on existing issue:** A third party comments "I have the same problem." -→ Do not respond. The commenter is not the issue author. +→ Call `noop` with reason "commenter is not the issue author". + +**Workflow failure issue (auto-generated):** Title starts with `[aw]` (e.g. "[aw] MSDO Issue Triage Assistant failed") or labeled `agentic-workflows`. +→ Call `noop` with reason "auto-generated failure report, not a user issue". diff --git a/.github/workflows/official-build.yml b/.github/workflows/official-build.yml index 016e6bde..99ce274d 100644 --- a/.github/workflows/official-build.yml +++ b/.github/workflows/official-build.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v6.0.2 - name: Extract branch name shell: bash diff --git a/.github/workflows/on-push-verification.yml b/.github/workflows/on-push-verification.yml index 86138cb4..4bd35a88 100644 --- a/.github/workflows/on-push-verification.yml +++ b/.github/workflows/on-push-verification.yml @@ -22,7 +22,7 @@ jobs: steps: # Checkout your code repository to scan - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 # Run analyzers - uses: ./ diff --git a/.github/workflows/sample-workflow.yml b/.github/workflows/sample-workflow.yml index 7f2411a8..77c6cf47 100644 --- a/.github/workflows/sample-workflow.yml +++ b/.github/workflows/sample-workflow.yml @@ -20,7 +20,7 @@ jobs: steps: # Checkout your code repository to scan - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 # Run analyzers - name: Run Microsoft Security DevOps Analysis diff --git a/.github/workflows/self-hosted-validation-v1.yml b/.github/workflows/self-hosted-validation-v1.yml index 67d1254c..3471076b 100644 --- a/.github/workflows/self-hosted-validation-v1.yml +++ b/.github/workflows/self-hosted-validation-v1.yml @@ -14,7 +14,7 @@ jobs: steps: # Checkout your code repository to scan - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 # Run MSDO v1 - name: Run MSDO diff --git a/.github/workflows/self-hosted-validation-v2.yml b/.github/workflows/self-hosted-validation-v2.yml deleted file mode 100644 index de57d5e4..00000000 --- a/.github/workflows/self-hosted-validation-v2.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Defender CLI v2 self-hosted validation -on: - push: - branches: [main, 'release/**'] - workflow_dispatch: - -permissions: - id-token: write - security-events: write - -jobs: - defender-image-scan: - name: Image Scan (mdc policy) - - runs-on: self-hosted - - steps: - - - uses: actions/checkout@v6 - - - name: Run Defender CLI - Image Scan - uses: ./v2/ - id: defender - with: - command: 'image' - imageName: 'ubuntu:latest' - policy: 'mdc' - break: 'false' - pr-summary: 'true' - - - name: Upload results to Security tab - uses: github/codeql-action/upload-sarif@v3 - if: always() - with: - sarif_file: ${{ steps.defender.outputs.sarifFile }} diff --git a/.github/workflows/toolchain-version-probe.yml b/.github/workflows/toolchain-version-probe.yml new file mode 100644 index 00000000..ecc8c564 --- /dev/null +++ b/.github/workflows/toolchain-version-probe.yml @@ -0,0 +1,126 @@ +name: MSDO Toolchain Version Probe + +# Runs MSDO to install tools as a side effect, then scrapes the install +# directories to record exact resolved versions into toolchain-versions.json. +# The breach monitor reads this file instead of guessing "latest" from registries. +# +# Guardian installs all tool wrappers as NuGet packages into: +# /home/runner/work/_msdo/packages/nuget/{PackageName}.{version}/ +# ESLint is installed via npm into: +# /home/runner/work/_msdo/packages/node_modules/eslint/ +# Package names confirmed from run 23433052319. + +on: + schedule: + - cron: '0 4 * * 1' # Weekly Monday 04:00 UTC + workflow_dispatch: + +permissions: + contents: write + +jobs: + probe: + name: Resolve and record MSDO tool versions + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + # Run MSDO — scan may find nothing (no real targets), that's fine. + # Side effect: Guardian downloads all tool packages into _msdo/packages/nuget/. + - name: Install MSDO tools + uses: microsoft/security-devops-action@main + continue-on-error: true + with: + tools: bandit,binskim,checkov,eslint,templateanalyzer,terrascan,trivy + + - name: Collect resolved tool versions from install dirs + run: | + python3 - <<'PYEOF' + import os, json, re, pathlib, datetime + + NUGET_DIR = pathlib.Path('/home/runner/work/_msdo/packages/nuget') + NPM_DIR = pathlib.Path('/home/runner/work/_msdo/packages/node_modules') + + VER_PAT = re.compile(r'^(.+?)\.(v?\d+\.\d+(?:\.\d+)*(?:[-+][0-9A-Za-z.-]+)?)$', re.IGNORECASE) + + # Guardian NuGet wrapper package names → canonical tool names. + # Confirmed from run 23433052319 (_msdo/packages/nuget/ directory listing). + PKG_TO_TOOL = { + 'microsoft.guardian.banditredist_linux_amd64': 'bandit', + 'microsoft.codeanalysis.binskim': 'binskim', + 'microsoft.guardian.checkovredist_linux_amd64': 'checkov', + 'azure.templates.analyzer.commandline.linux-x64': 'templateanalyzer', + 'microsoft.guardian.terrascanredist_linux_amd64': 'terrascan', + 'microsoft.guardian.trivyredist_linux_amd64': 'trivy', + } + + # Internal packages — skip + SKIP_PKGS = { + 'microsoft.security.devops.cli', + 'microsoft.security.devops.cli.linux-x64', + 'microsoft.security.devops.cli.linux-arm64', + 'microsoft.security.devops.cli.win-x64', + 'microsoft.security.devops.policy.names', + 'microsoft.security.devops.policy.github', + } + + tools = {} + raw_dirs = [] + + if NUGET_DIR.exists(): + entries = sorted(d.name for d in NUGET_DIR.iterdir() if d.is_dir()) + raw_dirs = entries + for name in entries: + m = VER_PAT.match(name) + if not m: + continue + pkg_lower = m.group(1).lower() + version = m.group(2) + if pkg_lower in SKIP_PKGS: + continue + canonical = PKG_TO_TOOL.get(pkg_lower) + if canonical: + tools[canonical] = version + + # ESLint: installed via npm, read version from package.json + eslint_pkg = NPM_DIR / 'eslint' / 'package.json' + if eslint_pkg.exists(): + tools['eslint'] = json.loads(eslint_pkg.read_text())['version'] + + print('raw_dirs:', raw_dirs) + print('resolved:', tools) + + if not tools: + raise SystemExit('ERROR: no versions resolved — _msdo/packages/nuget/ empty or missing. Aborting.') + + missing = (set(PKG_TO_TOOL.values()) | {'eslint'}) - set(tools.keys()) + if missing: + print(f'WARNING: expected tools not found: {sorted(missing)}') + + output = { + 'generated_at': datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'), + 'msdo_cli_version': os.environ.get('MSDO_INSTALLEDVERSION', 'unknown'), + 'tools': tools, + 'raw_dirs': raw_dirs, + } + + out = pathlib.Path('.github/toolchain-versions.json') + out.parent.mkdir(parents=True, exist_ok=True) + out.write_text(json.dumps(output, indent=2) + '\n') + print(json.dumps(output, indent=2)) + PYEOF + + - name: Commit updated versions + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add .github/toolchain-versions.json + if git diff --cached --quiet; then + echo "toolchain-versions.json unchanged — nothing to commit" + else + git commit -m "chore(ci): update toolchain-versions.json [skip ci]" + # Push to dedicated unprotected branch — main has branch protection + # requiring PRs. The breach monitor reads from this branch via API. + git push origin HEAD:bot/toolchain-versions --force + fi diff --git a/docs/superpowers/plans/2026-04-24-agentic-workflows-noop-fix.md b/docs/superpowers/plans/2026-04-24-agentic-workflows-noop-fix.md new file mode 100644 index 00000000..b01b180f --- /dev/null +++ b/docs/superpowers/plans/2026-04-24-agentic-workflows-noop-fix.md @@ -0,0 +1,605 @@ +# Agentic Workflows noop Fix — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Stop the three gh-aw agentic workflows from filing false-positive `[aw] ... failed` issues that page the on-call IcM rotation. + +**Architecture:** For all three agentic workflows, enable the `noop` safe output with `report-as-issue: false` (lets the agent explicitly signal "nothing to do" without itself filing an issue) and set `safe-outputs.report-failure-as-issue: false` (blocks the auto-filed failure issue even when no output is produced). Update the `msdo-issue-assistant` prompt so its "don't respond" rules now direct the agent to call the `noop` tool explicitly. Regenerate the three `.lock.yml` files with `gh aw compile` and ship everything in one PR. + +> **Note on syntax:** gh-aw v0.61.0 rejects `noop: true` as a boolean. The correct YAML shape is an object: `noop:\n report-as-issue: false`. All YAML blocks below use that shape. If you see `noop: true` anywhere, the compile will fail with "value must be false. Expected format: {...}". + +> **Post-implementation addenda (for traceability):** +> - The `gh aw compile` step with v0.61.0 silently downgrades `actions/github-script` from v9.0.0 (per PR #244) back to v8. The v9.0.0 SHA (`3a2844b7e9c422d3c10d287c895573f7108da1b3`) was restored via sed after compile. A maintenance note to this effect is embedded as a YAML comment at the top of each `.md` source file. +> - A second commit (after the initial review) extended the `msdo-issue-assistant` prompt edits beyond what Tasks 5-6 specified: rule 3 was updated to redirect to rule 4, and the four pre-existing "Do NOT Respond Examples" arrows were changed from "→ Do not respond" to "→ Call `noop` with reason ...". The `## Do NOT Respond Examples` heading was also renamed to `## Noop Examples`. These changes eliminated an internal contradiction between rule 3 and rule 4 and made the examples match the new noop-centric behaviour. They are not reflected in the task descriptions below. + +**Tech Stack:** GitHub Actions, gh-aw CLI v0.61.0, YAML, Markdown prompts. + +**Spec:** [docs/superpowers/specs/2026-04-24-agentic-workflows-noop-fix-design.md](../specs/2026-04-24-agentic-workflows-noop-fix-design.md) + +**Branch:** `fix/agentic-workflows-noop` (already created; the spec is committed there as `e6b5cfa`) + +--- + +## Task 1: Verify gh-aw CLI is installed at the right version + +**Files:** none (local tooling check) + +- [ ] **Step 1: Check the gh-aw version** + +Run: +```bash +gh aw version +``` + +Expected output contains `v0.61.0` (this matches the version recorded in the existing lock-file headers at [.github/workflows/msdo-issue-assistant.lock.yml:15](../../.github/workflows/msdo-issue-assistant.lock.yml#L15)). + +If `gh aw` is not installed, install it first: +```bash +gh extension install github/gh-aw +``` + +If a different version is installed, upgrade: +```bash +gh extension upgrade gh-aw +``` + +- [ ] **Step 2: Confirm we are on the right branch** + +Run: +```bash +git branch --show-current +``` + +Expected: `fix/agentic-workflows-noop` + +If not on that branch: +```bash +git checkout fix/agentic-workflows-noop +``` + +--- + +## Task 2: Edit `ci-doctor.md` safe-outputs + +**Files:** +- Modify: [.github/workflows/ci-doctor.md](../../.github/workflows/ci-doctor.md) (lines 32-40) + +- [ ] **Step 1: Replace the `safe-outputs` block** + +In [.github/workflows/ci-doctor.md](../../.github/workflows/ci-doctor.md), replace this exact block: + +```yaml +safe-outputs: + noop: false + create-issue: + max: 1 + add-labels: + allowed: [ci-failure, flaky-test, build-failure, dependency-issue, needs-maintainer] + add-comment: null + create-pull-request: null +``` + +With: + +```yaml +safe-outputs: + noop: + report-as-issue: false + report-failure-as-issue: false + create-issue: + max: 1 + add-labels: + allowed: [ci-failure, flaky-test, build-failure, dependency-issue, needs-maintainer] + add-comment: null + create-pull-request: null +``` + +Changes: `noop: false` replaced with the `noop:\n report-as-issue: false` object form (enables the noop tool without having it file its own issue), plus `report-failure-as-issue: false` inserted as the next key. + +- [ ] **Step 2: Verify the edit** + +Run: +```bash +grep -nE "noop|report-failure-as-issue|report-as-issue" .github/workflows/ci-doctor.md +``` + +Expected output: +``` +33: noop: +34: report-as-issue: false +35: report-failure-as-issue: false +``` + +--- + +## Task 3: Edit `msdo-breach-monitor.md` safe-outputs + +**Files:** +- Modify: [.github/workflows/msdo-breach-monitor.md](../../.github/workflows/msdo-breach-monitor.md) (lines 41-47) + +- [ ] **Step 1: Replace the `safe-outputs` block** + +In [.github/workflows/msdo-breach-monitor.md](../../.github/workflows/msdo-breach-monitor.md), replace this exact block: + +```yaml +safe-outputs: + noop: false + create-issue: + max: 1 + add-labels: + allowed: [security-breach, supply-chain, toolchain-alert, critical, high, medium] +``` + +With: + +```yaml +safe-outputs: + noop: + report-as-issue: false + report-failure-as-issue: false + create-issue: + max: 1 + add-labels: + allowed: [security-breach, supply-chain, toolchain-alert, critical, high, medium] +``` + +Changes: `noop: false` replaced with the `noop:\n report-as-issue: false` object form, plus `report-failure-as-issue: false` inserted as the next key. + +- [ ] **Step 2: Verify the edit** + +Run: +```bash +grep -nE "noop|report-failure-as-issue|report-as-issue" .github/workflows/msdo-breach-monitor.md +``` + +Expected output: +``` +42: noop: +43: report-as-issue: false +44: report-failure-as-issue: false +``` + +--- + +## Task 4: Edit `msdo-issue-assistant.md` safe-outputs + +**Files:** +- Modify: [.github/workflows/msdo-issue-assistant.md](../../.github/workflows/msdo-issue-assistant.md) (lines 32-38) + +- [ ] **Step 1: Replace the `safe-outputs` block** + +In [.github/workflows/msdo-issue-assistant.md](../../.github/workflows/msdo-issue-assistant.md), replace this exact block: + +```yaml +safe-outputs: + noop: false + add-comment: + max: 4 + add-labels: + allowed: ["type:bug", "type:feature", "type:docs", "type:question", "type:security", "type:maintenance", "status:triage", "status:waiting-on-author", "status:repro-needed", "status:team-review", "area:action", "area:msdo-cli", "area:ci", "area:container-mapping"] +``` + +With: + +```yaml +safe-outputs: + noop: + report-as-issue: false + report-failure-as-issue: false + add-comment: + max: 4 + add-labels: + allowed: ["type:bug", "type:feature", "type:docs", "type:question", "type:security", "type:maintenance", "status:triage", "status:waiting-on-author", "status:repro-needed", "status:team-review", "area:action", "area:msdo-cli", "area:ci", "area:container-mapping"] +``` + +Changes: `noop: false` replaced with the `noop:\n report-as-issue: false` object form, plus `report-failure-as-issue: false` inserted as the next key. + +- [ ] **Step 2: Verify the edit** + +Run: +```bash +grep -nE "noop|report-failure-as-issue|report-as-issue" .github/workflows/msdo-issue-assistant.md +``` + +Expected output (the first three lines — additional matches will appear later in the file inside the prompt text): +``` +33: noop: +34: report-as-issue: false +35: report-failure-as-issue: false +``` + +--- + +## Task 5: Update `msdo-issue-assistant.md` rule 4 to call noop explicitly + +**Files:** +- Modify: [.github/workflows/msdo-issue-assistant.md](../../.github/workflows/msdo-issue-assistant.md) (around lines 182-188, inside the `## Important Rules` section) + +- [ ] **Step 1: Replace the rule-4 block** + +Replace this exact block: + +```markdown +4. **Don't respond** if: + - The issue is not related to MSDO or security-devops-action + - The issue is closed + - The commenter is not the issue author (unless it's a new issue) + - You've already responded twice and there is no new technical information in the latest user message + - The issue has a `status:team-review` label +``` + +With: + +```markdown +4. **Call `noop` instead of staying silent** when any of these apply. Pass a one-line reason so the decision is auditable: + - The issue is not related to MSDO or security-devops-action + - The issue title starts with `[aw]` or is labeled `agentic-workflows` (auto-generated failure reports, not user issues) + - The issue is closed + - The commenter is not the issue author (unless it's a new issue) + - You have already responded twice and there is no new technical information in the latest user message + - The issue has a `status:team-review` label +``` + +Changes: title reworded from "Don't respond" to "Call `noop` instead of staying silent"; new bullet added for `[aw]`-title / `agentic-workflows`-label issues. + +- [ ] **Step 2: Verify the edit** + +Run: +```bash +grep -n "Call \`noop\` instead" .github/workflows/msdo-issue-assistant.md +``` + +Expected: one match pointing to the rule-4 line. + +--- + +## Task 6: Add `[aw]` example to `msdo-issue-assistant.md` "Do NOT Respond Examples" + +**Files:** +- Modify: [.github/workflows/msdo-issue-assistant.md](../../.github/workflows/msdo-issue-assistant.md) (at the end of the `## Do NOT Respond Examples` section, currently ending around line 213) + +- [ ] **Step 1: Append a new example at the end of the section** + +Find this existing last entry in the `## Do NOT Respond Examples` section: + +```markdown +**Non-author comment on existing issue:** A third party comments "I have the same problem." +→ Do not respond. The commenter is not the issue author. +``` + +Append **after** that block (preserve a blank line before the new entry): + +```markdown + +**Workflow failure issue (auto-generated):** Title starts with `[aw]` (e.g. "[aw] MSDO Issue Triage Assistant failed") or labeled `agentic-workflows`. +→ Call `noop` with reason "auto-generated failure report, not a user issue". +``` + +- [ ] **Step 2: Verify the edit** + +Run: +```bash +grep -n "Workflow failure issue" .github/workflows/msdo-issue-assistant.md +``` + +Expected: one match, appearing after the `Non-author comment on existing issue` example. + +Also run: +```bash +tail -5 .github/workflows/msdo-issue-assistant.md +``` + +Expected: the tail shows the new example as the last content in the file. + +--- + +## Task 7: Regenerate all three lock files + +**Files:** +- Modify (via compile): [.github/workflows/ci-doctor.lock.yml](../../.github/workflows/ci-doctor.lock.yml) +- Modify (via compile): [.github/workflows/msdo-breach-monitor.lock.yml](../../.github/workflows/msdo-breach-monitor.lock.yml) +- Modify (via compile): [.github/workflows/msdo-issue-assistant.lock.yml](../../.github/workflows/msdo-issue-assistant.lock.yml) + +- [ ] **Step 1: Run `gh aw compile`** + +Run from repo root: +```bash +gh aw compile +``` + +Expected: the command exits 0 and reports recompiling the three workflows. Any non-zero exit or schema error indicates the YAML edits are malformed — fix the `.md` files and retry. + +- [ ] **Step 2: Inspect the lock-file diff** + +Run: +```bash +git diff -- .github/workflows/*.lock.yml | head -120 +``` + +Expected: three lock files touched. In each diff, the `frontmatter_hash` near the top of the lock file changes (because the `.md` frontmatter changed). Look for new handler wiring for the noop safe output, and the absence of a `handle_missing_safe_outputs` or similar failure-issue step (because `report-failure-as-issue: false` disables it). + +If the diff shows only the `frontmatter_hash` change and no handler wiring change, the schema interpretation of `noop`/`report-failure-as-issue` may differ from expectation — pause and escalate before committing. + +--- + +## Task 8: Commit the changes + +**Files:** all six touched files in this commit. + +- [ ] **Step 1: Stage all changes** + +Run: +```bash +git add .github/workflows/ci-doctor.md \ + .github/workflows/ci-doctor.lock.yml \ + .github/workflows/msdo-breach-monitor.md \ + .github/workflows/msdo-breach-monitor.lock.yml \ + .github/workflows/msdo-issue-assistant.md \ + .github/workflows/msdo-issue-assistant.lock.yml +``` + +- [ ] **Step 2: Verify the staged diff** + +Run: +```bash +git diff --cached --stat +``` + +Expected: six files listed, three `.md` and three `.lock.yml`. + +- [ ] **Step 3: Commit with the project's oneliner style** + +Run: +```bash +git commit -m "fix(ci): enable noop on agentic workflows to stop IcM page spam" +``` + +No Co-Authored-By line; no multi-line body. + +- [ ] **Step 4: Verify the commit landed** + +Run: +```bash +git log --oneline -2 +``` + +Expected top commit: `fix(ci): enable noop on agentic workflows to stop IcM page spam`. +Second commit from top should be the earlier spec commit (`docs: add spec for agentic-workflows noop fix`). + +--- + +## Task 9: Push the branch and open the PR + +**Files:** none (GitHub operations). + +- [ ] **Step 1: Push the branch** + +Run: +```bash +git push -u origin fix/agentic-workflows-noop +``` + +Expected: branch published to `origin` with tracking configured. + +- [ ] **Step 2: Create the PR** + +Run (use `DimaBir` as the author account per the user's PR-account preference — if the git remote is already using that identity, a plain `gh pr create` is fine; otherwise the user handles account selection manually before this step): + +```bash +gh pr create \ + --repo microsoft/security-devops-action \ + --base main \ + --head fix/agentic-workflows-noop \ + --title "fix(ci): enable noop on agentic workflows to stop IcM page spam" \ + --body "$(cat <<'EOF' +## Summary +- Enables `safe-outputs.noop` (with `report-as-issue: false`) on all three agentic workflows so the agent can explicitly signal "nothing to do" instead of exiting silent. +- Sets `safe-outputs.report-failure-as-issue: false` so edge-case silent exits no longer file `[aw] ... failed` issues that page the IcM on-call rotation. +- Updates the `msdo-issue-assistant` prompt to call `noop` in its existing "don't respond" conditions and to recognise auto-generated `[aw]` failure issues. + +Fixes the false-positive failure loop documented in #247 and in [docs/superpowers/specs/2026-04-24-agentic-workflows-noop-fix-design.md](docs/superpowers/specs/2026-04-24-agentic-workflows-noop-fix-design.md). + +## Test plan +- [ ] `gh aw compile` recompiles all three workflows cleanly +- [ ] `msdo-issue-assistant` negative path: post a comment on an off-topic or `[aw]`-titled issue on the PR branch — no new `[aw] ... failed` issue filed, no comment posted +- [ ] `msdo-issue-assistant` positive path: open a test issue asking a real MSDO question — bot replies normally with wiki citations and `area:msdo-cli` label +- [ ] `ci-doctor` negative path: dispatch against a successful CI run — noop, no issue filed +- [ ] `msdo-breach-monitor` negative path: `workflow_dispatch` with no new CVEs — noop, no issue filed +EOF +)" +``` + +No `🤖 Generated with Claude Code` footer (per user preference). + +- [ ] **Step 3: Capture the PR URL** + +`gh pr create` prints the PR URL on success. Record it for the validation tasks below. + +--- + +## Task 10: Negative-path validation — `msdo-issue-assistant` + +**Files:** none (exercises the PR-branch workflow). + +- [ ] **Step 1: Trigger the bot against a known don't-respond case** + +Option A — post a comment on issue #247 (`[aw]`-titled, will exercise the new rule): + +```bash +gh issue comment 247 --repo microsoft/security-devops-action --body "test: verifying fix/agentic-workflows-noop — expect noop" +``` + +Option B — open a new test issue with clearly off-topic content, e.g. title "How do I deploy to AWS?" body "not MSDO-related, just testing". Close it after the run completes. + +Note: the workflow runs off whatever is merged on the default branch for new issues, **unless** gh-aw activation is configured to pick up the PR head. If the run still uses the current `main` version, either (a) merge first and validate post-merge, or (b) on a fork/test repo, push the branch and re-open the same test issue. For this repo, merging first is the likely path — log this as a deliberate choice in the PR review. + +- [ ] **Step 2: Observe the workflow run** + +Run: +```bash +gh run list --repo microsoft/security-devops-action --workflow "MSDO Issue Triage Assistant" --limit 5 +``` + +Expected: newest run's conclusion is `success`. Then inspect that specific run: + +```bash +gh run view --repo microsoft/security-devops-action --log | grep -E "noop|safe output|agent_output|failure" +``` + +Expected markers: +- Evidence of the `noop` handler firing (log line referencing `noop` or `handle_noop`). +- No `"Agent succeeded but produced no safe outputs"` line. +- No step that creates or comments on a `[aw] ... failed` issue. + +- [ ] **Step 3: Confirm no new `[aw]` issue was filed** + +Run: +```bash +gh issue list --repo microsoft/security-devops-action --search "[aw] MSDO Issue Triage Assistant failed" --state open --limit 5 +``` + +Expected: only the pre-existing #247 listed (or none, if it was closed). No newer `[aw]` issues. + +--- + +## Task 11: Positive-path validation — `msdo-issue-assistant` + +**Files:** none (exercises the workflow). + +- [ ] **Step 1: Open a test issue with a real MSDO question** + +Run: +```bash +gh issue create --repo microsoft/security-devops-action \ + --title "How do I pass --download-external-modules to checkov?" \ + --body "I want checkov (run via MSDO) to fetch external Terraform modules. How do I enable this?" +``` + +- [ ] **Step 2: Wait for the bot to respond (up to ~3 minutes), then inspect** + +Run: +```bash +gh issue view --repo microsoft/security-devops-action --comments +``` + +Expected: +- One new comment from the bot citing the wiki, mentioning `GDN_CHECKOV_DOWNLOADEXTERNALMODULES` or linking the Tool Configuration wiki page. +- The issue has the `area:msdo-cli` label applied. +- No `[aw] ... failed` issue created for this run. + +- [ ] **Step 3: Close the test issue** + +Run: +```bash +gh issue close --repo microsoft/security-devops-action --comment "test issue — closing" +``` + +--- + +## Task 12: Negative-path validation — `ci-doctor` + +**Files:** none. + +- [ ] **Step 1: Find a successful CI run on main** + +Run: +```bash +gh run list --repo microsoft/security-devops-action --workflow CI --branch main --status success --limit 3 +``` + +Expected: at least one green CI run. Record its run ID. + +- [ ] **Step 2: Manually dispatch `ci-doctor` against it (pre-merge, from the fix branch)** + +Run: +```bash +gh workflow run "CI Doctor" --repo microsoft/security-devops-action --ref fix/agentic-workflows-noop +``` + +Using `--ref fix/agentic-workflows-noop` makes GitHub pick up the updated `.lock.yml` on the PR branch, so this exercises the fix pre-merge. + +Wait ~1-2 minutes. Then: + +```bash +gh run list --repo microsoft/security-devops-action --workflow "CI Doctor" --limit 3 +``` + +Expected: newest run's conclusion is `success`. + +- [ ] **Step 3: Confirm no new `[aw]` or CI Doctor diagnostic issue was filed** + +Run: +```bash +gh issue list --repo microsoft/security-devops-action \ + --search "[aw] CI Doctor failed OR [CI Doctor]" \ + --state open --limit 5 +``` + +Expected: no newer entries than the pre-existing baseline. If CI Doctor found nothing new to diagnose (green run), it must have noop'd cleanly. + +--- + +## Task 13: Negative-path validation — `msdo-breach-monitor` + +**Files:** none. + +- [ ] **Step 1: Dispatch the monitor (pre-merge, from the fix branch)** + +Run: +```bash +gh workflow run "MSDO Toolchain Breach Monitor" --repo microsoft/security-devops-action --ref fix/agentic-workflows-noop +``` + +Using `--ref fix/agentic-workflows-noop` makes GitHub pick up the updated `.lock.yml` on the PR branch. + +- [ ] **Step 2: Observe the workflow run** + +Run: +```bash +gh run list --repo microsoft/security-devops-action --workflow "MSDO Toolchain Breach Monitor" --limit 3 +``` + +Expected: newest run's conclusion is `success`. + +Inspect the log for the noop call: + +```bash +gh run view --repo microsoft/security-devops-action --log | grep -E "noop|no new incidents|toolchain-alert" +``` + +Expected: evidence of a noop call (unless a genuine CVE in the window would produce a `toolchain-alert` issue — which is a positive-path outcome, not a failure). + +- [ ] **Step 3: Confirm no new `[aw] MSDO Toolchain Breach Monitor failed` issue was filed** + +Run: +```bash +gh issue list --repo microsoft/security-devops-action \ + --search "[aw] MSDO Toolchain Breach Monitor failed" \ + --state open --limit 5 +``` + +Expected: no newer entries. + +--- + +## Task 14: Complete the PR + +**Files:** none (GitHub). + +- [ ] **Step 1: Tick the PR's Test plan checkboxes** + +In the PR description, tick each checkbox that the validation tasks confirmed. + +Run: +```bash +gh pr view --repo microsoft/security-devops-action +``` + +Edit description via: +```bash +gh pr edit --repo microsoft/security-devops-action --body "" +``` + +- [ ] **Step 2: Hand off for human review and merge** + +The PR is now complete. Post a short summary comment and leave the merge to the repository maintainer per normal review process. The user will close #247 manually after merge. diff --git a/docs/superpowers/specs/2026-04-24-agentic-workflows-noop-fix-design.md b/docs/superpowers/specs/2026-04-24-agentic-workflows-noop-fix-design.md new file mode 100644 index 00000000..753e0ca3 --- /dev/null +++ b/docs/superpowers/specs/2026-04-24-agentic-workflows-noop-fix-design.md @@ -0,0 +1,155 @@ +# Agentic Workflows — `noop` Fix Design + +**Date:** 2026-04-24 +**Tracking issue:** [#247 — [aw] MSDO Issue Triage Assistant failed](https://github.com/microsoft/security-devops-action/issues/247) + +## Problem + +Three agentic workflows in `.github/workflows/` each set `safe-outputs.noop: false` +while their prompts instruct the agent to call `noop` or stay silent under +various conditions: + +| Workflow | File | Silent-path trigger | +|---|---|---| +| MSDO Issue Triage Assistant | `msdo-issue-assistant.md` | "Don't respond if" rules (off-topic issue, closed, non-author, `status:team-review`, already-responded, etc.) | +| CI Doctor | `ci-doctor.md` | "If the workflow succeeded, do nothing (noop)"; duplicate-issue check | +| MSDO Toolchain Breach Monitor | `msdo-breach-monitor.md` | "Call `noop` with a one-line summary" when no new CVEs | + +With `noop` disabled, the agent has no way to signal "intentional no-op." gh-aw +reads no `agent_output.json`, treats the run as failure, and files an issue +titled `[aw] failed`. + +In this repository, **every new GitHub issue opens a CRI IcM ticket that pages +on-call**. Each false-positive failure issue is therefore a human page. For +`msdo-issue-assistant`, the failure issue itself (#247) carries the +`agentic-workflows` label and re-triggers the bot on every comment, producing +a self-sustaining spam loop. + +Evidence from run `24783399971`: + +``` +Agent conclusion: success +Error reading agent output file: ENOENT: no such file or directory, open '/tmp/gh-aw/agent_output.json' +Agent succeeded but produced no safe outputs +Found existing issue #247: https://github.com/microsoft/security-devops-action/issues/247 +Added comment to existing issue #247 +``` + +## Goals + +1. Stop paging IcM on-call for false-positive agent failures. +2. Let each bot explicitly signal "nothing to do" when its prompt says to. +3. Preserve normal behaviour on genuine user questions and genuine incidents. + +## Non-goals + +- Closing issue #247 (user closes manually after merge). +- Adding non-paging alerting for real agent failures (future work). +- Fixing the unrelated broken `ContainerMapping` tests on main. +- Changes to non-agentic workflows. + +## Approach + +Hybrid fix — enable `noop` as the root-cause fix, add `report-failure-as-issue: false` +as a safety net so any edge case that still produces no output never pages IcM. + +### Changes to `safe-outputs` (all three `.md` files) + +```yaml +safe-outputs: + noop: true # was: false + report-failure-as-issue: false # new + # ... existing keys (add-comment, add-labels, create-issue, etc.) unchanged +``` + +Semantics: +- `noop: true` registers the `noop` safe-output handler so the agent can call it + with a reason. gh-aw records a successful no-op run and does not treat it as + failure. +- `report-failure-as-issue: false` prevents gh-aw from filing an issue when the + run ends in failure or with no outputs. Genuine failures remain visible in the + Actions tab. + +### Additional edits in `msdo-issue-assistant.md` only + +The prompt currently uses `## Important Rules → Don't respond if` and +`## Do NOT Respond Examples`. Update both to direct the agent to call `noop` +explicitly. + +**Rule replacement** (replace the existing rule 4 "Don't respond if" block): + +```markdown +4. **Call `noop` instead of staying silent** when any of these apply. Pass a + one-line reason so the decision is auditable: + - The issue is not related to MSDO or security-devops-action + - The issue title starts with `[aw]` or is labeled `agentic-workflows` + (auto-generated failure reports, not user issues) + - The issue is closed + - The commenter is not the issue author (unless it's a new issue) + - You have already responded twice and there is no new technical + information in the latest user message + - The issue has a `status:team-review` label +``` + +**New entry in "Do NOT Respond Examples"** (append): + +```markdown +**Workflow failure issue (auto-generated):** Title starts with `[aw]` +(e.g. "[aw] MSDO Issue Triage Assistant failed") or labeled +`agentic-workflows`. +→ Call `noop` with reason "auto-generated failure report, not a user issue". +``` + +No prompt edits in `ci-doctor.md` or `msdo-breach-monitor.md` — their prompts +already say "call noop" / "do nothing (noop)" and will work correctly once +`noop: true` is set. + +### Lock-file regeneration + +After `.md` edits, run `gh aw compile` locally (gh-aw CLI v0.61.0, +matching the version recorded in the existing lock-file header) to +regenerate the three `.lock.yml` files. Both `.md` and `.lock.yml` go in +the same PR so reviewers can diff intent against generated output. + +## Validation + +Existing unit tests on main are broken (ContainerMapping) and do not cover +agentic-workflow behaviour. Validation is behavioural, via `workflow_dispatch` +runs on the PR branch: + +1. **Compile check:** `gh aw compile` succeeds without error; lock-file diff + contains the expected `noop` handler wiring and no other unintended changes. +2. **`msdo-issue-assistant` negative path:** on the PR branch, post a comment + on an existing off-topic issue or on issue #247 itself (this fires the + `issue_comment: created` trigger against the PR-branch workflow via the + normal gh-aw activation flow). Expect the run to succeed, no new comment + posted, no new `[aw] ... failed` issue filed. +3. **`msdo-issue-assistant` positive path:** open a test issue with a real MSDO + question (for example "how do I pass `--download-external-modules` to + checkov?"). Expect the bot to reply normally, citing the wiki, applying the + `area:msdo-cli` label. +4. **`ci-doctor` negative path:** trigger a CI run that succeeds on `main` or a + `release/**` branch (the workflow auto-fires on `workflow_run: CI completed`), + or dispatch manually and point it at a successful run. Expect noop, no + issue filed. +5. **`msdo-breach-monitor` negative path:** `workflow_dispatch` manually when + no new CVEs are in the advisory window. Expect noop, no issue filed. + +If any dry run still files a `[aw] ... failed` issue, the safety net +(`report-failure-as-issue: false`) has not taken effect — investigate before +merging. + +## Rollout + +- One PR on branch `fix/agentic-workflows-noop`, base `main`. +- PR title: `fix(ci): enable noop on agentic workflows to stop IcM page spam`. +- Merge once dry-run validation passes. +- User closes #247 manually after merge. + +## Risks + +| Risk | Mitigation | +|---|---| +| `report-failure-as-issue: false` hides a real agent failure | Accepted trade-off — false positives page IcM; real failures remain in Actions tab and can be wired to non-paging alerts later | +| gh-aw v0.61.0 interprets `noop: true` differently than expected | Lock-file diff is reviewed before merge; fall back to `report-failure-as-issue: false` only (Approach 2) if the generated handler looks wrong | +| Prompt edits cause `msdo-issue-assistant` to noop on cases users want a reply on | Conditions are identical to existing "Don't respond" rules — behaviour unchanged, only the exit mechanism becomes explicit. Positive-path dry run catches regressions | diff --git a/lib/v2/container-mapping.js b/lib/v2/container-mapping.js deleted file mode 100644 index 14a8c2a5..00000000 --- a/lib/v2/container-mapping.js +++ /dev/null @@ -1,268 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ContainerMapping = void 0; -const https = __importStar(require("https")); -const core = __importStar(require("@actions/core")); -const exec = __importStar(require("@actions/exec")); -const os = __importStar(require("os")); -const sendReportRetryCount = 1; -const GetScanContextURL = "https://dfdinfra-afdendpoint-prod-d5fqbucbg7fue0cf.z01.azurefd.net/github/v1/auth-push/GetScanContext?context=authOnly"; -const ContainerMappingURL = "https://dfdinfra-afdendpoint-prod-d5fqbucbg7fue0cf.z01.azurefd.net/github/v1/container-mappings"; -class ContainerMapping { - constructor() { - this.succeedOnError = true; - } - runPreJob() { - try { - core.info("::group::Microsoft Defender for DevOps container mapping pre-job - https://go.microsoft.com/fwlink/?linkid=2231419"); - this._runPreJob(); - } - catch (error) { - core.info("Error in Container Mapping pre-job: " + error); - } - finally { - core.info("::endgroup::"); - } - } - _runPreJob() { - const startTime = new Date().toISOString(); - core.saveState('PreJobStartTime', startTime); - core.info(`PreJobStartTime: ${startTime}`); - } - runMain() { - return __awaiter(this, void 0, void 0, function* () { - }); - } - runPostJob() { - return __awaiter(this, void 0, void 0, function* () { - try { - core.info("::group::Microsoft Defender for DevOps container mapping post-job - https://go.microsoft.com/fwlink/?linkid=2231419"); - yield this._runPostJob(); - } - catch (error) { - core.info("Error in Container Mapping post-job: " + error); - } - finally { - core.info("::endgroup::"); - } - }); - } - _runPostJob() { - return __awaiter(this, void 0, void 0, function* () { - let startTime = core.getState('PreJobStartTime'); - if (startTime.length <= 0) { - startTime = new Date(new Date().getTime() - 10000).toISOString(); - core.debug(`PreJobStartTime not defined, using now-10secs`); - } - core.info(`PreJobStartTime: ${startTime}`); - let reportData = { - dockerVersion: "", - dockerEvents: [], - dockerImages: [] - }; - let bearerToken = yield core.getIDToken() - .then((token) => { return token; }) - .catch((error) => { - throw new Error("Unable to get token: " + error); - }); - if (!bearerToken) { - throw new Error("Empty OIDC token received"); - } - var callerIsOnboarded = yield this.checkCallerIsCustomer(bearerToken, sendReportRetryCount); - if (!callerIsOnboarded) { - core.info("Client is not onboarded to Defender for DevOps. Skipping container mapping workload."); - return; - } - core.info("Client is onboarded for container mapping."); - let dockerVersionOutput = yield exec.getExecOutput('docker --version'); - if (dockerVersionOutput.exitCode != 0) { - core.info(`Unable to get docker version: ${dockerVersionOutput}`); - core.info(`Skipping container mapping since docker not found/available.`); - return; - } - reportData.dockerVersion = dockerVersionOutput.stdout.trim(); - yield this.execCommand(`docker events --since ${startTime} --until ${new Date().toISOString()} --filter event=push --filter type=image --format ID={{.ID}}`, reportData.dockerEvents) - .catch((error) => { - throw new Error("Unable to get docker events: " + error); - }); - yield this.execCommand(`docker images --format CreatedAt={{.CreatedAt}}::Repo={{.Repository}}::Tag={{.Tag}}::Digest={{.Digest}}`, reportData.dockerImages) - .catch((error) => { - throw new Error("Unable to get docker images: " + error); - }); - core.debug("Finished data collection, starting API calls."); - var reportSent = yield this.sendReport(JSON.stringify(reportData), bearerToken, sendReportRetryCount); - if (!reportSent) { - throw new Error("Unable to send report to backend service"); - } - ; - core.info("Container mapping data sent successfully."); - }); - } - execCommand(command, listener) { - return __awaiter(this, void 0, void 0, function* () { - return exec.getExecOutput(command) - .then((result) => { - if (result.exitCode != 0) { - return Promise.reject(`Command execution failed: ${result}`); - } - result.stdout.trim().split(os.EOL).forEach(element => { - if (element.length > 0) { - listener.push(element); - } - }); - }); - }); - } - sendReport(data, bearerToken, retryCount = 0) { - return __awaiter(this, void 0, void 0, function* () { - core.debug(`attempting to send report: ${data}`); - return yield this._sendReport(data, bearerToken) - .then(() => { - return true; - }) - .catch((error) => __awaiter(this, void 0, void 0, function* () { - if (retryCount == 0) { - return false; - } - else { - core.info(`Retrying API call due to error: ${error}.\nRetry count: ${retryCount}`); - retryCount--; - return yield this.sendReport(data, bearerToken, retryCount); - } - })); - }); - } - _sendReport(data, bearerToken) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - let apiTime = Date.now(); - let options = { - method: 'POST', - timeout: 2500, - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + bearerToken, - 'Content-Length': Buffer.byteLength(data, 'utf8') - } - }; - core.debug(`${options['method'].toUpperCase()} ${ContainerMappingURL}`); - const req = https.request(ContainerMappingURL, options, (res) => { - let resData = ''; - res.on('data', (chunk) => { - resData += chunk.toString(); - }); - res.on('end', () => { - core.debug('API calls finished. Time taken: ' + (Date.now() - apiTime) + "ms"); - core.debug(`Status code: ${res.statusCode} ${res.statusMessage}`); - core.debug('Response headers: ' + JSON.stringify(res.headers)); - if (resData.length > 0) { - core.debug('Response: ' + resData); - } - if (res.statusCode < 200 || res.statusCode >= 300) { - return reject(`Received Failed Status code when calling url: ${res.statusCode} ${resData}`); - } - resolve(); - }); - }); - req.on('error', (error) => { - reject(new Error(`Error calling url: ${error}`)); - }); - req.write(data); - req.end(); - }); - }); - } - checkCallerIsCustomer(bearerToken, retryCount = 0) { - return __awaiter(this, void 0, void 0, function* () { - return yield this._checkCallerIsCustomer(bearerToken) - .then((statusCode) => __awaiter(this, void 0, void 0, function* () { - if (statusCode == 200) { - return true; - } - else if (statusCode == 403) { - return false; - } - else { - core.debug(`Unexpected status code: ${statusCode}`); - return yield this.retryCall(bearerToken, retryCount); - } - })) - .catch((error) => __awaiter(this, void 0, void 0, function* () { - core.info(`Unexpected error: ${error}.`); - return yield this.retryCall(bearerToken, retryCount); - })); - }); - } - retryCall(bearerToken, retryCount) { - return __awaiter(this, void 0, void 0, function* () { - if (retryCount == 0) { - core.info(`All retries failed.`); - return false; - } - else { - core.info(`Retrying checkCallerIsCustomer.\nRetry count: ${retryCount}`); - retryCount--; - return yield this.checkCallerIsCustomer(bearerToken, retryCount); - } - }); - } - _checkCallerIsCustomer(bearerToken) { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - let options = { - method: 'GET', - timeout: 2500, - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + bearerToken, - } - }; - core.debug(`${options['method'].toUpperCase()} ${GetScanContextURL}`); - const req = https.request(GetScanContextURL, options, (res) => { - res.on('end', () => { - resolve(res.statusCode); - }); - res.on('data', function (d) { - }); - }); - req.on('error', (error) => { - reject(new Error(`Error calling url: ${error}`)); - }); - req.end(); - }); - }); - } -} -exports.ContainerMapping = ContainerMapping; diff --git a/lib/v2/defender-cli.js b/lib/v2/defender-cli.js deleted file mode 100644 index 54a5b0c5..00000000 --- a/lib/v2/defender-cli.js +++ /dev/null @@ -1,166 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MicrosoftDefenderCLI = void 0; -const core = __importStar(require("@actions/core")); -const path = __importStar(require("path")); -const defender_helpers_1 = require("./defender-helpers"); -const defender_client_1 = require("./defender-client"); -const job_summary_1 = require("./job-summary"); -class MicrosoftDefenderCLI { - constructor() { - this.prSummaryEnabled = true; - this.succeedOnError = false; - } - runPreJob() { - return __awaiter(this, void 0, void 0, function* () { - }); - } - runPostJob() { - return __awaiter(this, void 0, void 0, function* () { - }); - } - runMain() { - return __awaiter(this, void 0, void 0, function* () { - yield this.runDefenderCLI(); - }); - } - runDefenderCLI() { - return __awaiter(this, void 0, void 0, function* () { - const debugInput = core.getInput(defender_helpers_1.Inputs.Debug); - const debug = debugInput ? debugInput.toLowerCase() === 'true' : false; - if (debug) { - (0, defender_helpers_1.setupDebugLogging)(true); - core.debug('Debug logging enabled'); - } - const command = core.getInput(defender_helpers_1.Inputs.Command) || 'fs'; - const scanType = (0, defender_helpers_1.validateScanType)(command); - const prSummaryInput = core.getInput(defender_helpers_1.Inputs.PrSummary); - this.prSummaryEnabled = prSummaryInput ? prSummaryInput.toLowerCase() !== 'false' : true; - core.debug(`PR Summary enabled: ${this.prSummaryEnabled}`); - const argsInput = core.getInput(defender_helpers_1.Inputs.Args) || ''; - let additionalArgs = (0, defender_helpers_1.parseAdditionalArgs)(argsInput); - let target; - switch (scanType) { - case defender_helpers_1.ScanType.FileSystem: - const fileSystemPath = core.getInput(defender_helpers_1.Inputs.FileSystemPath) || - process.env['GITHUB_WORKSPACE'] || - process.cwd(); - target = (0, defender_helpers_1.validateFileSystemPath)(fileSystemPath); - core.debug(`Filesystem scan using directory: ${target}`); - break; - case defender_helpers_1.ScanType.Image: - const imageName = core.getInput(defender_helpers_1.Inputs.ImageName); - if (!imageName) { - throw new Error('Image name is required for image scan'); - } - target = (0, defender_helpers_1.validateImageName)(imageName); - break; - case defender_helpers_1.ScanType.Model: - const modelPath = core.getInput(defender_helpers_1.Inputs.ModelPath); - if (!modelPath) { - throw new Error('Model path is required for model scan'); - } - target = (0, defender_helpers_1.validateModelPath)(modelPath); - break; - default: - throw new Error(`Unsupported scan type: ${scanType}`); - } - const breakInput = core.getInput(defender_helpers_1.Inputs.Break); - const breakOnCritical = breakInput ? breakInput.toLowerCase() === 'true' : false; - additionalArgs = additionalArgs.filter(arg => arg !== '--defender-break'); - if (breakOnCritical) { - additionalArgs.push('--defender-break'); - core.debug('Break on critical vulnerability enabled: adding --defender-break flag'); - } - additionalArgs = additionalArgs.filter(arg => arg !== '--defender-debug'); - if (debug) { - additionalArgs.push('--defender-debug'); - core.debug('Debug mode enabled: adding --defender-debug flag'); - } - let successfulExitCodes = [0]; - const outputPath = path.join(process.env['RUNNER_TEMP'] || process.cwd(), 'defender.sarif'); - const policyInput = core.getInput(defender_helpers_1.Inputs.Policy) || 'mdc'; - let policy; - if (policyInput === 'none') { - policy = ''; - } - else { - policy = policyInput; - } - core.debug(`Scan Type: ${scanType}`); - core.debug(`Target: ${target}`); - core.debug(`Policy: ${policy}`); - core.debug(`Output Path: ${outputPath}`); - if (additionalArgs.length > 0) { - core.debug(`Additional Arguments: ${additionalArgs.join(' ')}`); - } - process.env['Defender_Extension'] = 'true'; - core.debug('Environment variable set: Defender_Extension=true'); - core.setOutput('sarifFile', outputPath); - core.exportVariable('DEFENDER_SARIF_FILE', outputPath); - core.debug(`sarifFile output set to: ${outputPath}`); - try { - switch (scanType) { - case defender_helpers_1.ScanType.FileSystem: - yield (0, defender_client_1.scanDirectory)(target, policy, outputPath, successfulExitCodes, additionalArgs); - break; - case defender_helpers_1.ScanType.Image: - yield (0, defender_client_1.scanImage)(target, policy, outputPath, successfulExitCodes, additionalArgs); - break; - case defender_helpers_1.ScanType.Model: - yield (0, defender_client_1.scanModel)(target, policy, outputPath, successfulExitCodes, additionalArgs); - break; - } - if (this.prSummaryEnabled) { - core.debug('Posting job summary...'); - yield (0, job_summary_1.postJobSummary)(outputPath, scanType, target); - } - } - catch (error) { - if (this.prSummaryEnabled) { - try { - yield (0, job_summary_1.postJobSummary)(outputPath, scanType, target); - } - catch (summaryError) { - core.debug(`Failed to post summary after error: ${summaryError}`); - } - } - core.error(`Defender CLI execution failed: ${error}`); - throw error; - } - }); - } -} -exports.MicrosoftDefenderCLI = MicrosoftDefenderCLI; diff --git a/lib/v2/defender-client.js b/lib/v2/defender-client.js deleted file mode 100644 index bfaaf8ad..00000000 --- a/lib/v2/defender-client.js +++ /dev/null @@ -1,128 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.setupEnvironment = exports.scanModel = exports.scanImage = exports.scanDirectory = void 0; -const core = __importStar(require("@actions/core")); -const exec = __importStar(require("@actions/exec")); -const fs = __importStar(require("fs")); -const path = __importStar(require("path")); -const os = __importStar(require("os")); -const installer = __importStar(require("./defender-installer")); -function scanDirectory(directoryPath, policy, outputPath, successfulExitCodes, additionalArgs) { - return __awaiter(this, void 0, void 0, function* () { - yield scan('fs', directoryPath, policy, outputPath, successfulExitCodes, additionalArgs); - }); -} -exports.scanDirectory = scanDirectory; -function scanImage(imageName, policy, outputPath, successfulExitCodes, additionalArgs) { - return __awaiter(this, void 0, void 0, function* () { - yield scan('image', imageName, policy, outputPath, successfulExitCodes, additionalArgs); - }); -} -exports.scanImage = scanImage; -function scanModel(modelPath, policy, outputPath, successfulExitCodes, additionalArgs) { - return __awaiter(this, void 0, void 0, function* () { - yield scan('model', modelPath, policy, outputPath, successfulExitCodes, additionalArgs); - }); -} -exports.scanModel = scanModel; -function scan(scanType, target, policy, outputPath, successfulExitCodes, additionalArgs) { - return __awaiter(this, void 0, void 0, function* () { - const resolvedPolicy = policy || 'mdc'; - const resolvedOutputPath = outputPath || path.join(process.env['RUNNER_TEMP'] || process.cwd(), 'defender.sarif'); - const inputArgs = [ - 'scan', - scanType, - target, - '--defender-policy', - resolvedPolicy, - '--defender-output', - resolvedOutputPath - ]; - if (additionalArgs && additionalArgs.length > 0) { - inputArgs.push(...additionalArgs); - } - yield runDefenderCli(inputArgs, successfulExitCodes); - }); -} -function runDefenderCli(inputArgs, successfulExitCodes) { - return __awaiter(this, void 0, void 0, function* () { - yield setupEnvironment(); - const cliFilePath = getCliFilePath(); - if (!cliFilePath) { - throw new Error('DEFENDER_FILEPATH environment variable is not set. Defender CLI may not be installed.'); - } - core.debug(`Running Defender CLI: ${cliFilePath} ${inputArgs.join(' ')}`); - const isDebug = process.env['RUNNER_DEBUG'] === '1' || core.isDebug(); - if (isDebug && !inputArgs.includes('--defender-debug')) { - inputArgs.push('--defender-debug'); - } - const exitCode = yield exec.exec(cliFilePath, inputArgs, { - ignoreReturnCode: true - }); - const validExitCodes = successfulExitCodes || [0]; - if (!validExitCodes.includes(exitCode)) { - throw new Error(`Defender CLI exited with an error exit code: ${exitCode}`); - } - core.debug(`Defender CLI completed successfully with exit code: ${exitCode}`); - }); -} -function setupEnvironment() { - return __awaiter(this, void 0, void 0, function* () { - const toolCacheDir = process.env['RUNNER_TOOL_CACHE'] || path.join(os.homedir(), '.defender'); - const defenderDir = path.join(toolCacheDir, '_defender'); - if (!fs.existsSync(defenderDir)) { - fs.mkdirSync(defenderDir, { recursive: true }); - } - const packagesDirectory = process.env['DEFENDER_PACKAGES_DIRECTORY'] || path.join(defenderDir, 'packages'); - process.env['DEFENDER_PACKAGES_DIRECTORY'] = packagesDirectory; - if (!process.env['DEFENDER_FILEPATH']) { - const cliVersion = resolveCliVersion(); - core.debug(`Installing Defender CLI version: ${cliVersion}`); - yield installer.install(cliVersion); - } - }); -} -exports.setupEnvironment = setupEnvironment; -function resolveCliVersion() { - let version = process.env['DEFENDER_VERSION'] || 'latest'; - if (version.includes('*')) { - version = 'Latest'; - } - core.debug(`Resolved Defender CLI version: ${version}`); - return version; -} -function getCliFilePath() { - return process.env['DEFENDER_FILEPATH']; -} diff --git a/lib/v2/defender-helpers.js b/lib/v2/defender-helpers.js deleted file mode 100644 index e31b7c96..00000000 --- a/lib/v2/defender-helpers.js +++ /dev/null @@ -1,174 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.parseAdditionalArgs = exports.getEncodedContent = exports.encode = exports.writeToOutStream = exports.setupDebugLogging = exports.validateImageName = exports.validateModelPath = exports.validateModelUrl = exports.isUrl = exports.validateFileSystemPath = exports.validateScanType = exports.Constants = exports.ScanType = exports.Inputs = void 0; -const core = __importStar(require("@actions/core")); -const fs = __importStar(require("fs")); -const os = __importStar(require("os")); -var Inputs; -(function (Inputs) { - Inputs["Command"] = "command"; - Inputs["Args"] = "args"; - Inputs["FileSystemPath"] = "fileSystemPath"; - Inputs["ImageName"] = "imageName"; - Inputs["ModelPath"] = "modelPath"; - Inputs["Break"] = "break"; - Inputs["Debug"] = "debug"; - Inputs["PrSummary"] = "pr-summary"; - Inputs["Policy"] = "policy"; -})(Inputs || (exports.Inputs = Inputs = {})); -var ScanType; -(function (ScanType) { - ScanType["FileSystem"] = "fs"; - ScanType["Image"] = "image"; - ScanType["Model"] = "model"; -})(ScanType || (exports.ScanType = ScanType = {})); -var Constants; -(function (Constants) { - Constants["Unknown"] = "unknown"; - Constants["PreJobStartTime"] = "PREJOBSTARTTIME"; - Constants["DefenderExecutable"] = "Defender"; -})(Constants || (exports.Constants = Constants = {})); -function validateScanType(scanTypeInput) { - const scanType = scanTypeInput; - if (!Object.values(ScanType).includes(scanType)) { - throw new Error(`Invalid scan type: ${scanTypeInput}. Valid options are: ${Object.values(ScanType).join(', ')}`); - } - return scanType; -} -exports.validateScanType = validateScanType; -function validateFileSystemPath(fsPath) { - if (!fsPath || fsPath.trim() === '') { - throw new Error('Filesystem path cannot be empty for filesystem scan'); - } - const trimmedPath = fsPath.trim(); - if (!fs.existsSync(trimmedPath)) { - throw new Error(`Filesystem path does not exist: ${trimmedPath}`); - } - return trimmedPath; -} -exports.validateFileSystemPath = validateFileSystemPath; -function isUrl(input) { - if (!input) { - return false; - } - const lowercased = input.toLowerCase(); - return lowercased.startsWith('http://') || lowercased.startsWith('https://'); -} -exports.isUrl = isUrl; -function validateModelUrl(url) { - try { - const parsedUrl = new URL(url); - if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { - throw new Error(`Invalid URL protocol: ${parsedUrl.protocol}. Only http:// and https:// are supported.`); - } - if (!parsedUrl.hostname) { - throw new Error('URL must have a valid hostname.'); - } - return url; - } - catch (error) { - if (error instanceof TypeError) { - throw new Error(`Invalid URL format: ${url}`); - } - throw error; - } -} -exports.validateModelUrl = validateModelUrl; -function validateModelPath(modelPath) { - if (!modelPath || modelPath.trim() === '') { - throw new Error('Model path cannot be empty for model scan'); - } - const trimmedPath = modelPath.trim(); - if (isUrl(trimmedPath)) { - return validateModelUrl(trimmedPath); - } - if (!fs.existsSync(trimmedPath)) { - throw new Error(`Model path does not exist: ${trimmedPath}`); - } - const stats = fs.statSync(trimmedPath); - if (!stats.isFile() && !stats.isDirectory()) { - throw new Error(`Model path must be a file or directory: ${trimmedPath}`); - } - return trimmedPath; -} -exports.validateModelPath = validateModelPath; -function validateImageName(imageName) { - if (!imageName || imageName.trim() === '') { - throw new Error('Image name cannot be empty for image scan'); - } - const trimmedImageName = imageName.trim(); - const imageNameRegex = /^(?:(?:[a-zA-Z0-9._-]+(?:\.[a-zA-Z0-9._-]+)*(?::[0-9]+)?\/)?[a-zA-Z0-9._-]+(?:\/[a-zA-Z0-9._-]+)*)(?::[a-zA-Z0-9._-]+|@sha256:[a-fA-F0-9]{64})?$/; - if (!imageNameRegex.test(trimmedImageName)) { - throw new Error(`Invalid image name format: ${trimmedImageName}. Image name should follow container image naming conventions.`); - } - return trimmedImageName; -} -exports.validateImageName = validateImageName; -function setupDebugLogging(enabled) { - if (enabled) { - process.env['RUNNER_DEBUG'] = '1'; - core.debug('Debug logging enabled'); - } -} -exports.setupDebugLogging = setupDebugLogging; -function writeToOutStream(data, outStream = process.stdout) { - outStream.write(data.trim() + os.EOL); -} -exports.writeToOutStream = writeToOutStream; -const encode = (str) => Buffer.from(str, 'binary').toString('base64'); -exports.encode = encode; -function getEncodedContent(dockerVersion, dockerEvents, dockerImages) { - let data = []; - data.push('DockerVersion: ' + dockerVersion); - data.push('DockerEvents:'); - data.push(dockerEvents); - data.push('DockerImages:'); - data.push(dockerImages); - return (0, exports.encode)(data.join(os.EOL)); -} -exports.getEncodedContent = getEncodedContent; -function parseAdditionalArgs(additionalArgs) { - if (!additionalArgs || additionalArgs.trim() === '') { - return []; - } - const args = []; - const trimmedArgs = additionalArgs.trim(); - const regex = /(?:[^\s"']+|"[^"]*"|'[^']*')+/g; - const matches = trimmedArgs.match(regex); - if (matches) { - for (const match of matches) { - let arg = match; - if ((arg.startsWith('"') && arg.endsWith('"')) || - (arg.startsWith("'") && arg.endsWith("'"))) { - arg = arg.slice(1, -1); - } - args.push(arg); - } - } - core.debug(`Parsed additional arguments: ${JSON.stringify(args)}`); - return args; -} -exports.parseAdditionalArgs = parseAdditionalArgs; diff --git a/lib/v2/defender-installer.js b/lib/v2/defender-installer.js deleted file mode 100644 index f519de87..00000000 --- a/lib/v2/defender-installer.js +++ /dev/null @@ -1,239 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.setVariables = exports.resolveFileName = exports.install = void 0; -const core = __importStar(require("@actions/core")); -const crypto = __importStar(require("crypto")); -const fs = __importStar(require("fs")); -const https = __importStar(require("https")); -const path = __importStar(require("path")); -const os = __importStar(require("os")); -const downloadBaseUrl = 'https://cli.dfd.security.azure.com/public'; -const maxRetries = 3; -const downloadTimeoutMs = 30000; -function install(cliVersion = 'latest') { - return __awaiter(this, void 0, void 0, function* () { - const existingPath = process.env['DEFENDER_FILEPATH']; - if (existingPath && fs.existsSync(existingPath)) { - core.debug(`Defender CLI already installed at: ${existingPath}`); - return; - } - const existingDir = process.env['DEFENDER_DIRECTORY']; - if (existingDir && fs.existsSync(existingDir)) { - const fileName = resolveFileName(); - const filePath = path.join(existingDir, fileName); - if (fs.existsSync(filePath)) { - core.debug(`Found pre-installed Defender CLI at: ${filePath}`); - setVariables(existingDir, fileName, cliVersion); - return; - } - } - const toolCacheDir = process.env['RUNNER_TOOL_CACHE'] || path.join(os.homedir(), '.defender'); - const packagesDirectory = process.env['DEFENDER_PACKAGES_DIRECTORY'] || path.join(toolCacheDir, '_defender', 'packages'); - if (!fs.existsSync(packagesDirectory)) { - fs.mkdirSync(packagesDirectory, { recursive: true }); - } - const fileName = resolveFileName(); - let lastError; - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - core.info(`Downloading Defender CLI (attempt ${attempt}/${maxRetries})...`); - yield downloadDefenderCli(packagesDirectory, fileName, cliVersion); - setVariables(packagesDirectory, fileName, cliVersion, true); - core.info(`Defender CLI installed successfully.`); - return; - } - catch (error) { - lastError = error; - core.warning(`Download attempt ${attempt} failed: ${lastError.message}`); - if (attempt < maxRetries) { - core.info('Retrying...'); - } - } - } - throw new Error(`Failed to install Defender CLI after ${maxRetries} attempts: ${lastError === null || lastError === void 0 ? void 0 : lastError.message}`); - }); -} -exports.install = install; -function downloadDefenderCli(packagesDirectory, fileName, cliVersion) { - return __awaiter(this, void 0, void 0, function* () { - const versionDir = path.join(packagesDirectory, `defender-cli.${cliVersion}`); - if (!fs.existsSync(versionDir)) { - fs.mkdirSync(versionDir, { recursive: true }); - } - const filePath = path.join(versionDir, fileName); - const downloadUrl = `${downloadBaseUrl}/${cliVersion.toLowerCase()}/${fileName}`; - core.debug(`Downloading from: ${downloadUrl}`); - core.debug(`Saving to: ${filePath}`); - yield downloadFile(downloadUrl, filePath); - yield verifyIntegrity(filePath, downloadUrl); - if (process.platform !== 'win32') { - fs.chmodSync(filePath, 0o755); - } - }); -} -function downloadFile(url, filePath) { - return new Promise((resolve, reject) => { - const file = fs.createWriteStream(filePath); - const request = https.get(url, { timeout: downloadTimeoutMs }, (response) => { - if (response.statusCode === 301 || response.statusCode === 302) { - file.close(); - fs.unlinkSync(filePath); - const redirectUrl = response.headers.location; - if (!redirectUrl) { - return reject(new Error('Redirect without location header')); - } - const allowedHost = new URL(downloadBaseUrl).hostname; - const redirectHost = new URL(redirectUrl).hostname; - if (redirectHost !== allowedHost) { - return reject(new Error(`Redirect to untrusted host: ${redirectHost}. Expected: ${allowedHost}`)); - } - core.debug(`Following redirect to: ${redirectUrl}`); - downloadFile(redirectUrl, filePath).then(resolve).catch(reject); - return; - } - if (response.statusCode !== 200) { - file.close(); - fs.unlinkSync(filePath); - return reject(new Error(`Download failed with status code: ${response.statusCode}`)); - } - response.pipe(file); - file.on('finish', () => { - file.close(); - resolve(); - }); - }); - request.on('error', (error) => { - file.close(); - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - reject(new Error(`Download error: ${error.message}`)); - }); - request.on('timeout', () => { - request.destroy(); - file.close(); - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - reject(new Error('Download timed out')); - }); - }); -} -function verifyIntegrity(filePath, downloadUrl) { - return __awaiter(this, void 0, void 0, function* () { - const checksumUrl = `${downloadUrl}.sha256`; - const expectedHash = yield downloadString(checksumUrl); - const expected = expectedHash.trim().split(/\s+/)[0].toLowerCase(); - const fileBuffer = fs.readFileSync(filePath); - const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex'); - if (actualHash !== expected) { - fs.unlinkSync(filePath); - throw new Error(`Integrity check failed for ${path.basename(filePath)}: expected ${expected}, got ${actualHash}`); - } - core.debug(`Integrity verified: ${actualHash}`); - }); -} -function downloadString(url) { - return new Promise((resolve, reject) => { - const request = https.get(url, { timeout: downloadTimeoutMs }, (response) => { - if (response.statusCode === 301 || response.statusCode === 302) { - const redirectUrl = response.headers.location; - if (!redirectUrl) { - return reject(new Error('Redirect without location header')); - } - const allowedHost = new URL(downloadBaseUrl).hostname; - const redirectHost = new URL(redirectUrl).hostname; - if (redirectHost !== allowedHost) { - return reject(new Error(`Redirect to untrusted host: ${redirectHost}. Expected: ${allowedHost}`)); - } - core.debug(`Following redirect to: ${redirectUrl}`); - downloadString(redirectUrl).then(resolve).catch(reject); - return; - } - if (response.statusCode !== 200) { - return reject(new Error(`Download failed with status code: ${response.statusCode}`)); - } - const chunks = []; - response.on('data', (chunk) => chunks.push(chunk)); - response.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8'))); - response.on('error', (error) => reject(new Error(`Download error: ${error.message}`))); - }); - request.on('error', (error) => { - reject(new Error(`Download error: ${error.message}`)); - }); - request.on('timeout', () => { - request.destroy(); - reject(new Error('Download timed out')); - }); - }); -} -function resolveFileName() { - const platform = os.platform(); - const arch = os.arch(); - switch (platform) { - case 'win32': - if (arch === 'arm64') - return 'Defender_win-arm64.exe'; - if (arch === 'ia32') - return 'Defender_win-x86.exe'; - return 'Defender_win-x64.exe'; - case 'linux': - if (arch === 'arm64') - return 'Defender_linux-arm64'; - return 'Defender_linux-x64'; - case 'darwin': - if (arch === 'arm64') - return 'Defender_osx-arm64'; - return 'Defender_osx-x64'; - default: - core.warning(`Unknown platform: ${platform}. Defaulting to linux-x64.`); - return 'Defender_linux-x64'; - } -} -exports.resolveFileName = resolveFileName; -function setVariables(packagesDirectory, fileName, cliVersion, validate = false) { - const defenderDir = path.join(packagesDirectory, `defender-cli.${cliVersion}`); - const defenderFilePath = path.join(defenderDir, fileName); - if (validate && !fs.existsSync(defenderFilePath)) { - throw new Error(`Defender CLI not found after download: ${defenderFilePath}`); - } - process.env['DEFENDER_DIRECTORY'] = defenderDir; - process.env['DEFENDER_FILEPATH'] = defenderFilePath; - process.env['DEFENDER_INSTALLEDVERSION'] = cliVersion; - core.debug(`DEFENDER_DIRECTORY=${defenderDir}`); - core.debug(`DEFENDER_FILEPATH=${defenderFilePath}`); - core.debug(`DEFENDER_INSTALLEDVERSION=${cliVersion}`); -} -exports.setVariables = setVariables; diff --git a/lib/v2/defender-interface.js b/lib/v2/defender-interface.js deleted file mode 100644 index 6b0ba53d..00000000 --- a/lib/v2/defender-interface.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getDefenderExecutor = void 0; -function getDefenderExecutor(runner) { - return new runner(); -} -exports.getDefenderExecutor = getDefenderExecutor; diff --git a/lib/v2/defender-main.js b/lib/v2/defender-main.js deleted file mode 100644 index 4d03025f..00000000 --- a/lib/v2/defender-main.js +++ /dev/null @@ -1,59 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const core = __importStar(require("@actions/core")); -const defender_cli_1 = require("./defender-cli"); -const defender_interface_1 = require("./defender-interface"); -const defender_helpers_1 = require("./defender-helpers"); -let succeedOnError = false; -function _getDefenderRunner() { - return (0, defender_interface_1.getDefenderExecutor)(defender_cli_1.MicrosoftDefenderCLI); -} -function run() { - return __awaiter(this, void 0, void 0, function* () { - core.debug('Starting Microsoft Defender for DevOps scan'); - const defenderRunner = _getDefenderRunner(); - succeedOnError = defenderRunner.succeedOnError; - yield defenderRunner.runMain(); - }); -} -run().catch(error => { - if (succeedOnError) { - (0, defender_helpers_1.writeToOutStream)('Ran into error: ' + error); - core.info('Finished execution with error (succeedOnError=true)'); - } - else { - core.setFailed(error); - } -}); diff --git a/lib/v2/job-summary.js b/lib/v2/job-summary.js deleted file mode 100644 index 63d7f32b..00000000 --- a/lib/v2/job-summary.js +++ /dev/null @@ -1,277 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.postJobSummary = exports.generateNoFindingsSummary = exports.generateMarkdownSummary = exports.parseSarifContent = exports.formatLocation = exports.extractCveId = exports.mapLevelToSeverity = exports.Severity = exports.SarifLevel = void 0; -const core = __importStar(require("@actions/core")); -const fs = __importStar(require("fs")); -var SarifLevel; -(function (SarifLevel) { - SarifLevel["Error"] = "error"; - SarifLevel["Warning"] = "warning"; - SarifLevel["Note"] = "note"; - SarifLevel["None"] = "none"; -})(SarifLevel || (exports.SarifLevel = SarifLevel = {})); -var Severity; -(function (Severity) { - Severity["Critical"] = "critical"; - Severity["High"] = "high"; - Severity["Medium"] = "medium"; - Severity["Low"] = "low"; - Severity["Unknown"] = "unknown"; -})(Severity || (exports.Severity = Severity = {})); -function mapLevelToSeverity(level, properties) { - if (properties === null || properties === void 0 ? void 0 : properties.severity) { - const propSeverity = properties.severity.toLowerCase(); - if (propSeverity === 'critical') - return Severity.Critical; - if (propSeverity === 'high') - return Severity.High; - if (propSeverity === 'medium') - return Severity.Medium; - if (propSeverity === 'low') - return Severity.Low; - } - switch (level === null || level === void 0 ? void 0 : level.toLowerCase()) { - case SarifLevel.Error: - return Severity.High; - case SarifLevel.Warning: - return Severity.Medium; - case SarifLevel.Note: - return Severity.Low; - case SarifLevel.None: - return Severity.Low; - default: - return Severity.Unknown; - } -} -exports.mapLevelToSeverity = mapLevelToSeverity; -function extractCveId(ruleId, properties) { - if (properties === null || properties === void 0 ? void 0 : properties.cveId) { - return properties.cveId; - } - if (ruleId) { - const cveMatch = ruleId.match(/CVE-\d{4}-\d+/i); - if (cveMatch) { - return cveMatch[0].toUpperCase(); - } - } - return undefined; -} -exports.extractCveId = extractCveId; -function formatLocation(locations) { - var _a, _b, _c, _d; - if (!locations || locations.length === 0) { - return undefined; - } - const loc = locations[0]; - const uri = (_b = (_a = loc.physicalLocation) === null || _a === void 0 ? void 0 : _a.artifactLocation) === null || _b === void 0 ? void 0 : _b.uri; - const line = (_d = (_c = loc.physicalLocation) === null || _c === void 0 ? void 0 : _c.region) === null || _d === void 0 ? void 0 : _d.startLine; - if (uri) { - return line ? `${uri}:${line}` : uri; - } - return undefined; -} -exports.formatLocation = formatLocation; -function parseSarifContent(sarifContent) { - var _a, _b, _c, _d, _e; - const summary = { - total: 0, - critical: 0, - high: 0, - medium: 0, - low: 0, - unknown: 0, - vulnerabilities: [] - }; - let sarif; - try { - sarif = JSON.parse(sarifContent); - } - catch (error) { - core.warning(`Failed to parse SARIF content: ${error}`); - return summary; - } - if (!sarif.runs || sarif.runs.length === 0) { - core.debug('No runs found in SARIF document'); - return summary; - } - const rulesMap = new Map(); - for (const run of sarif.runs) { - if ((_b = (_a = run.tool) === null || _a === void 0 ? void 0 : _a.driver) === null || _b === void 0 ? void 0 : _b.rules) { - for (const rule of run.tool.driver.rules) { - rulesMap.set(rule.id, rule); - } - } - if (run.results) { - for (const result of run.results) { - const ruleId = result.ruleId || 'unknown'; - const rule = rulesMap.get(ruleId); - const severity = mapLevelToSeverity(result.level || ((_c = rule === null || rule === void 0 ? void 0 : rule.defaultConfiguration) === null || _c === void 0 ? void 0 : _c.level), result.properties || (rule === null || rule === void 0 ? void 0 : rule.properties)); - const vulnerability = { - ruleId, - message: ((_d = result.message) === null || _d === void 0 ? void 0 : _d.text) || ((_e = rule === null || rule === void 0 ? void 0 : rule.shortDescription) === null || _e === void 0 ? void 0 : _e.text) || 'No description available', - severity, - location: formatLocation(result.locations), - cveId: extractCveId(ruleId, result.properties) - }; - summary.vulnerabilities.push(vulnerability); - summary.total++; - switch (severity) { - case Severity.Critical: - summary.critical++; - break; - case Severity.High: - summary.high++; - break; - case Severity.Medium: - summary.medium++; - break; - case Severity.Low: - summary.low++; - break; - default: - summary.unknown++; - } - } - } - } - return summary; -} -exports.parseSarifContent = parseSarifContent; -function generateMarkdownSummary(summary, scanType, target, hasCriticalOrHigh) { - const lines = []; - lines.push('# Microsoft Defender for DevOps Scan Results'); - lines.push(''); - lines.push('## Summary'); - lines.push('| Severity | Count |'); - lines.push('|----------|-------|'); - lines.push(`| 🔴 Critical | ${summary.critical} |`); - lines.push(`| 🟠 High | ${summary.high} |`); - lines.push(`| 🟡 Medium | ${summary.medium} |`); - lines.push(`| 🟢 Low | ${summary.low} |`); - if (summary.unknown > 0) { - lines.push(`| ⚪ Unknown | ${summary.unknown} |`); - } - lines.push(''); - lines.push(`**Total Vulnerabilities**: ${summary.total}`); - lines.push(''); - if (summary.critical > 0 || summary.high > 0) { - lines.push('## Critical and High Findings'); - const criticalAndHigh = summary.vulnerabilities.filter(v => v.severity === Severity.Critical || v.severity === Severity.High); - let index = 1; - for (const vuln of criticalAndHigh.slice(0, 20)) { - const severityIcon = vuln.severity === Severity.Critical ? '🔴' : '🟠'; - const identifier = vuln.cveId || vuln.ruleId; - const location = vuln.location ? ` in \`${vuln.location}\`` : ''; - lines.push(`${index}. ${severityIcon} **${identifier}** - ${vuln.message}${location}`); - index++; - } - if (criticalAndHigh.length > 20) { - lines.push(`... and ${criticalAndHigh.length - 20} more`); - } - lines.push(''); - } - lines.push('## Scan Details'); - lines.push(`- **Scan Type**: ${formatScanType(scanType)}`); - lines.push(`- **Target**: \`${target}\``); - const statusIcon = hasCriticalOrHigh ? '❌' : '✅'; - const statusText = hasCriticalOrHigh - ? 'Failed (Critical/High vulnerabilities found)' - : 'Passed'; - lines.push(`- **Status**: ${statusIcon} ${statusText}`); - lines.push(''); - lines.push('---'); - lines.push('*Generated by Microsoft Defender for DevOps*'); - return lines.join('\n'); -} -exports.generateMarkdownSummary = generateMarkdownSummary; -function formatScanType(scanType) { - switch (scanType.toLowerCase()) { - case 'fs': - return 'Filesystem'; - case 'image': - return 'Container Image'; - case 'model': - return 'AI Model'; - default: - return scanType; - } -} -function generateNoFindingsSummary(scanType, target) { - const lines = []; - lines.push('# Microsoft Defender for DevOps Scan Results'); - lines.push(''); - lines.push('## Summary'); - lines.push('✅ **No vulnerabilities found!**'); - lines.push(''); - lines.push('## Scan Details'); - lines.push(`- **Scan Type**: ${formatScanType(scanType)}`); - lines.push(`- **Target**: \`${target}\``); - lines.push('- **Status**: ✅ Passed'); - lines.push(''); - lines.push('---'); - lines.push('*Generated by Microsoft Defender for DevOps*'); - return lines.join('\n'); -} -exports.generateNoFindingsSummary = generateNoFindingsSummary; -function postJobSummary(sarifPath, scanType, target) { - return __awaiter(this, void 0, void 0, function* () { - try { - core.debug(`Attempting to post job summary from SARIF: ${sarifPath}`); - if (!fs.existsSync(sarifPath)) { - core.warning(`SARIF file not found at ${sarifPath}. Skipping job summary.`); - return false; - } - const sarifContent = fs.readFileSync(sarifPath, 'utf8'); - const summary = parseSarifContent(sarifContent); - core.debug(`Parsed ${summary.total} vulnerabilities from SARIF`); - const hasCriticalOrHigh = summary.critical > 0 || summary.high > 0; - let markdown; - if (summary.total === 0) { - markdown = generateNoFindingsSummary(scanType, target); - } - else { - markdown = generateMarkdownSummary(summary, scanType, target, hasCriticalOrHigh); - } - yield core.summary.addRaw(markdown).write(); - core.debug('Posted summary to GitHub Job Summary'); - return true; - } - catch (error) { - core.warning(`Failed to post job summary: ${error}`); - return false; - } - }); -} -exports.postJobSummary = postJobSummary; diff --git a/lib/v2/post.js b/lib/v2/post.js deleted file mode 100644 index 114788ab..00000000 --- a/lib/v2/post.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const core = __importStar(require("@actions/core")); -const container_mapping_1 = require("./container-mapping"); -const defender_interface_1 = require("./defender-interface"); -function runPost() { - return __awaiter(this, void 0, void 0, function* () { - yield (0, defender_interface_1.getDefenderExecutor)(container_mapping_1.ContainerMapping).runPostJob(); - }); -} -runPost().catch((error) => { - core.debug(error); -}); diff --git a/lib/v2/pre.js b/lib/v2/pre.js deleted file mode 100644 index 9160a24c..00000000 --- a/lib/v2/pre.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const core = __importStar(require("@actions/core")); -const container_mapping_1 = require("./container-mapping"); -const defender_interface_1 = require("./defender-interface"); -function runPre() { - return __awaiter(this, void 0, void 0, function* () { - yield (0, defender_interface_1.getDefenderExecutor)(container_mapping_1.ContainerMapping).runPreJob(); - }); -} -runPre().catch((error) => { - core.debug(error); -}); diff --git a/package-lock.json b/package-lock.json index 37725ca0..013bf7d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,15 +15,15 @@ }, "devDependencies": { "@types/mocha": "^10.0.10", - "@types/node": "^25.3.0", + "@types/node": "^25.6.0", "@types/q": "^1.5.8", - "@types/sinon": "^21.0.0", + "@types/sinon": "^21.0.1", "del": "^8.0.1", "gulp": "^5.0.1", "gulp-cli": "^3.1.0", "gulp-typescript": "^6.0.0-alpha.1", "mocha": "^11.7.5", - "sinon": "^21.0.0", + "sinon": "^22.0.0", "typescript": "^5.9.3" } }, @@ -62,15 +62,6 @@ "integrity": "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==", "license": "MIT" }, - "node_modules/@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, "node_modules/@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -237,18 +228,6 @@ "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==", "license": "MIT" }, - "node_modules/@microsoft/security-devops-actions-toolkit/node_modules/undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -315,27 +294,24 @@ "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", - "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1" } }, "node_modules/@sinonjs/samsam": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-9.0.2.tgz", - "integrity": "sha512-H/JSxa4GNKZuuU41E3b8Y3tbSEx8y4uq4UH1C56ONQac16HblReJomIvv3Ud7ANQHQmkeSowY49Ij972e/pGxQ==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-10.0.2.tgz", + "integrity": "sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1", "type-detect": "^4.1.0" @@ -346,7 +322,6 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -359,13 +334,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, - "license": "MIT", "dependencies": { - "undici-types": "~7.18.0" + "undici-types": "~7.19.0" } }, "node_modules/@types/q": { @@ -376,11 +350,10 @@ "license": "MIT" }, "node_modules/@types/sinon": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.0.tgz", - "integrity": "sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.1.tgz", + "integrity": "sha512-5yoJSqLbjH8T9V2bksgRayuhpZy+723/z6wBOR+Soe4ZlXC0eW8Na71TeaZPUWDQvM7LYKa9UGFc6LRqxiR5fQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/sinonjs__fake-timers": "*" } @@ -588,9 +561,14 @@ } }, "node_modules/balanced-match": { - "version": "1.0.2", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, "node_modules/bare-events": { "version": "2.8.2", @@ -669,14 +647,16 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/braces": { @@ -846,11 +826,6 @@ "dev": true, "license": "MIT" }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "1.9.0", "dev": true, @@ -979,11 +954,10 @@ } }, "node_modules/diff": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", - "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", "dev": true, - "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } @@ -2278,14 +2252,19 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.5" }, "engines": { - "node": "*" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minipass": { @@ -2333,15 +2312,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2454,21 +2424,6 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "dev": true, @@ -2797,13 +2752,13 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -2915,16 +2870,6 @@ ], "license": "MIT" }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, "node_modules/readable-stream": { "version": "2.3.8", "dev": true, @@ -3148,13 +3093,13 @@ } }, "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", "dev": true, "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "engines": { + "node": ">=20.0.0" } }, "node_modules/shebang-command": { @@ -3191,17 +3136,15 @@ } }, "node_modules/sinon": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.2.tgz", - "integrity": "sha512-VHV4UaoxIe5jrMd89Y9duI76T5g3Lp+ET+ctLhLDaZtSznDPah1KKpRElbdBV4RwqWSw2vadFiVs9Del7MbVeQ==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.0.0.tgz", + "integrity": "sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==", "dev": true, - "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^15.1.1", - "@sinonjs/samsam": "^9.0.2", - "diff": "^8.0.3", - "supports-color": "^7.2.0" + "@sinonjs/fake-timers": "^15.4.0", + "@sinonjs/samsam": "^10.0.2", + "diff": "^9.0.0" }, "funding": { "type": "opencollective", @@ -3488,7 +3431,6 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, - "license": "MIT", "engines": { "node": ">=4" } @@ -3542,20 +3484,19 @@ } }, "node_modules/undici": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", - "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.0.2.tgz", + "integrity": "sha512-B9MeU5wuFhkFAuNeA19K2GDFcQXZxq33fL0nRy2Aq30wdufZbyyvxW3/ChaeipXVfy/wUweZyzovQGk39+9k2w==", "license": "MIT", "engines": { - "node": ">=18.17" + "node": ">=22.19.0" } }, "node_modules/undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", - "dev": true, - "license": "MIT" + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "dev": true }, "node_modules/unicorn-magic": { "version": "0.3.0", @@ -3570,13 +3511,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, - "license": "MIT" - }, "node_modules/unique-stream": { "version": "2.3.1", "dev": true, @@ -3917,7 +3851,7 @@ "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==", "requires": { "tunnel": "^0.0.6", - "undici": "^6.23.0" + "undici": ">=6.24.1" } }, "@actions/io": { @@ -3925,11 +3859,6 @@ "resolved": "https://registry.npmjs.org/@actions/io/-/io-2.0.0.tgz", "integrity": "sha512-Jv33IN09XLO+0HS79aaODsvIRyduiF7NY/F6LYeK5oeUmrsz7aFdRphQjFoESF4jS7lMauDOttKALcpapVDIAg==" }, - "@fastify/busboy": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==" - }, "@gulpjs/messages": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", @@ -4044,21 +3973,13 @@ "integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==", "requires": { "tunnel": "^0.0.6", - "undici": "^5.25.4" + "undici": ">=6.24.1" } }, "@actions/io": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz", "integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==" - }, - "undici": { - "version": "5.29.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", - "requires": { - "@fastify/busboy": "^2.0.0" - } } } }, @@ -4111,18 +4032,18 @@ } }, "@sinonjs/fake-timers": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.1.1.tgz", - "integrity": "sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==", + "version": "15.4.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-15.4.0.tgz", + "integrity": "sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.1" } }, "@sinonjs/samsam": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-9.0.2.tgz", - "integrity": "sha512-H/JSxa4GNKZuuU41E3b8Y3tbSEx8y4uq4UH1C56ONQac16HblReJomIvv3Ud7ANQHQmkeSowY49Ij972e/pGxQ==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-10.0.2.tgz", + "integrity": "sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.1", @@ -4144,12 +4065,12 @@ "dev": true }, "@types/node": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", - "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "version": "25.6.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", + "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", "dev": true, "requires": { - "undici-types": "~7.18.0" + "undici-types": "~7.19.0" } }, "@types/q": { @@ -4159,9 +4080,9 @@ "dev": true }, "@types/sinon": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.0.tgz", - "integrity": "sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==", + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.1.tgz", + "integrity": "sha512-5yoJSqLbjH8T9V2bksgRayuhpZy+723/z6wBOR+Soe4ZlXC0eW8Na71TeaZPUWDQvM7LYKa9UGFc6LRqxiR5fQ==", "dev": true, "requires": { "@types/sinonjs__fake-timers": "*" @@ -4207,7 +4128,7 @@ "dev": true, "requires": { "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "picomatch": ">=2.3.2" } }, "append-buffer": { @@ -4295,7 +4216,9 @@ } }, "balanced-match": { - "version": "1.0.2", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true }, "bare-events": { @@ -4342,13 +4265,12 @@ } }, "brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" } }, "braces": { @@ -4455,10 +4377,6 @@ "version": "1.1.4", "dev": true }, - "concat-map": { - "version": "0.0.1", - "dev": true - }, "convert-source-map": { "version": "1.9.0", "dev": true @@ -4545,9 +4463,9 @@ "dev": true }, "diff": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", - "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-9.0.0.tgz", + "integrity": "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==", "dev": true }, "duplexify": { @@ -4795,7 +4713,7 @@ "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.1.1", + "minimatch": ">=3.1.3", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } @@ -5413,17 +5331,19 @@ "dev": true, "requires": { "braces": ">=3.0.3", - "picomatch": "^2.3.1" + "picomatch": ">=2.3.2" } }, "mimic-response": { "version": "4.0.0" }, "minimatch": { - "version": "3.1.2", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "requires": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^5.0.5" } }, "minipass": { @@ -5449,10 +5369,10 @@ "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^9.0.5", + "minimatch": ">=3.1.3", "ms": "^2.1.3", "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", + "serialize-javascript": ">=7.0.5", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", "workerpool": "^9.2.0", @@ -5461,15 +5381,6 @@ "yargs-unparser": "^2.0.0" }, "dependencies": { - "brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, "chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -5525,7 +5436,7 @@ "requires": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "minimatch": ">=3.1.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" @@ -5537,15 +5448,6 @@ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true }, - "minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - }, "ms": { "version": "2.1.3", "dev": true @@ -5759,9 +5661,9 @@ "dev": true }, "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true }, "plugin-error": { @@ -5831,15 +5733,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "dev": true }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, "readable-stream": { "version": "2.3.8", "dev": true, @@ -5859,7 +5752,7 @@ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "picomatch": "^2.2.1" + "picomatch": ">=2.3.2" } }, "rechoir": { @@ -5986,13 +5879,10 @@ } }, "serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "requires": { - "randombytes": "^2.1.0" - } + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", + "dev": true }, "shebang-command": { "version": "2.0.0", @@ -6016,16 +5906,15 @@ "dev": true }, "sinon": { - "version": "21.0.2", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.2.tgz", - "integrity": "sha512-VHV4UaoxIe5jrMd89Y9duI76T5g3Lp+ET+ctLhLDaZtSznDPah1KKpRElbdBV4RwqWSw2vadFiVs9Del7MbVeQ==", + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-22.0.0.tgz", + "integrity": "sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==", "dev": true, "requires": { "@sinonjs/commons": "^3.0.1", - "@sinonjs/fake-timers": "^15.1.1", - "@sinonjs/samsam": "^9.0.2", - "diff": "^8.0.3", - "supports-color": "^7.2.0" + "@sinonjs/fake-timers": "^15.4.0", + "@sinonjs/samsam": "^10.0.2", + "diff": "^9.0.0" } }, "slash": { @@ -6265,14 +6154,14 @@ "dev": true }, "undici": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", - "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==" + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.0.2.tgz", + "integrity": "sha512-B9MeU5wuFhkFAuNeA19K2GDFcQXZxq33fL0nRy2Aq30wdufZbyyvxW3/ChaeipXVfy/wUweZyzovQGk39+9k2w==" }, "undici-types": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", - "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "version": "7.19.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", + "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "dev": true }, "unicorn-magic": { @@ -6281,12 +6170,6 @@ "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", "dev": true }, - "undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true - }, "unique-stream": { "version": "2.3.1", "dev": true, diff --git a/package.json b/package.json index 6759e801..c77e3973 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,11 @@ "description": "Node dependencies for the microsoft/security-devops-action.", "overrides": { "braces": ">=3.0.3", - "micromatch": ">=4.0.8" + "micromatch": ">=4.0.8", + "picomatch": ">=2.3.2", + "minimatch": ">=3.1.3", + "serialize-javascript": ">=7.0.5", + "undici": ">=6.24.1" }, "scripts": { "build": "npx gulp", @@ -21,15 +25,15 @@ }, "devDependencies": { "@types/mocha": "^10.0.10", - "@types/node": "^25.3.0", + "@types/node": "^25.6.0", "@types/q": "^1.5.8", - "@types/sinon": "^21.0.0", + "@types/sinon": "^21.0.1", "del": "^8.0.1", "gulp": "^5.0.1", "gulp-cli": "^3.1.0", "gulp-typescript": "^6.0.0-alpha.1", "mocha": "^11.7.5", - "sinon": "^21.0.0", + "sinon": "^22.0.0", "typescript": "^5.9.3" } } diff --git a/sda.sarif b/sda.sarif deleted file mode 100644 index 46a3e920..00000000 --- a/sda.sarif +++ /dev/null @@ -1,9259 +0,0 @@ -{ - "$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json", - "version": "2.1.0", - "runs": [ - { - "tool": { - "driver": { - "name": "antimalware", - "rules": [ - { - "id": "NoThreatsFound", - "name": "No threats were found by AntiMalware." - } - ], - "properties": { - "RawName": "antimalware" - } - } - }, - "invocations": [ - { - "commandLine": "\"C:\\ProgramData\\Microsoft\\Windows Defender\\Platform\\4.18.24090.11-0\\MpCmdRun.exe\" -Scan -ScanType 3 -DisableRemediation -File D:\\source\\security-devops-action", - "executionSuccessful": true - } - ], - "versionControlProvenance": [ - { - "repositoryUri": "https://github.com/reynoldsa/security-devops-action", - "revisionId": "c5bc432f9640469fd713f651b4d18af73867f27a", - "branch": "main", - "properties": { - "RepositoryRoot": "D:\\source\\security-devops-action" - } - } - ], - "results": [], - "columnKind": "utf16CodeUnits", - "policies": [ - { - "name": "Microsoft", - "version": "2.0.3" - } - ], - "properties": { - "toolInfoId": "antimalware>>0>>202411062057" - } - }, - { - "tool": { - "driver": { - "name": "bandit", - "properties": { - "RawName": "bandit" - } - } - }, - "invocations": [ - { - "endTimeUtc": "2024-11-07T04:56:49.000Z", - "executionSuccessful": true - } - ], - "versionControlProvenance": [ - { - "repositoryUri": "https://github.com/reynoldsa/security-devops-action", - "revisionId": "c5bc432f9640469fd713f651b4d18af73867f27a", - "branch": "main", - "properties": { - "RepositoryRoot": "D:\\source\\security-devops-action" - } - } - ], - "results": [], - "columnKind": "utf16CodeUnits", - "policies": [ - { - "name": "Microsoft", - "version": "2.0.3" - } - ], - "properties": { - "metrics": {"_totals":{"loc":0,"nosec":0,"SEVERITY.UNDEFINED":0.0,"CONFIDENCE.UNDEFINED":0.0,"SEVERITY.LOW":0.0,"CONFIDENCE.LOW":0.0,"SEVERITY.MEDIUM":0.0,"CONFIDENCE.MEDIUM":0.0,"SEVERITY.HIGH":0.0,"CONFIDENCE.HIGH":0.0},"D:\\source\\security-devops-action\\samples\\insecure.py":{"loc":0,"nosec":0,"SEVERITY.UNDEFINED":0.0,"SEVERITY.LOW":0.0,"SEVERITY.MEDIUM":0.0,"SEVERITY.HIGH":0.0,"CONFIDENCE.UNDEFINED":0.0,"CONFIDENCE.LOW":0.0,"CONFIDENCE.MEDIUM":0.0,"CONFIDENCE.HIGH":0.0}}, - "toolInfoId": "bandit>>1>>202411062057" - } - }, - { - "tool": { - "driver": { - "name": "credscan", - "organization": "Microsoft Corporation", - "product": "Microsoft Security Credential Scanner Client", - "fullName": "CredentialScanner 2.5.1.13", - "version": "2.5.1.13", - "semanticVersion": "2.5.1", - "rules": [ - { - "id": "CSCAN-GENERAL0020", - "name": "X.509 Certificate Private Key", - "fullDescription": { - "text": "used as a private component in SSL certificates." - }, - "shortDescription": { - "text": "X.509 Certificate Private Key." - }, - "messageStrings": { - "Default": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - } - }, - "helpUri": "https://aka.ms/CredScanDocs" - } - ], - "properties": { - "Comments": "CredentialScanner is an Azure security tool to scan for credentials & other sensitive data in source code and/or system files.", - "RawName": "credscan" - } - }, - "properties": { - "IsPreview": true - } - }, - "invocations": [ - { - "startTimeUtc": "2024-11-07T04:57:07.500Z", - "endTimeUtc": "2024-11-07T04:57:15.725Z", - "executionSuccessful": true - } - ], - "versionControlProvenance": [ - { - "repositoryUri": "https://github.com/reynoldsa/security-devops-action", - "revisionId": "c5bc432f9640469fd713f651b4d18af73867f27a", - "branch": "main", - "properties": { - "RepositoryRoot": "D:\\source\\security-devops-action" - } - } - ], - "originalUriBaseIds": { - "file:///D:/source/security-devops-action/": { - "uri": "file:///D:/source/security-devops-action/" - } - }, - "results": [ - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/allsans.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/allsans.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "ad80df55e021c410c64bbdc3c768739c9b7fd32cfe9d37e5049efe305a7cabbe", - "gdnAlternativeSignature0": "471e593e20b5c75c62e499b8249c85f1835dc7f99dc9553a8a66b1be2550515d" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/badcert.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/badcert.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 19, - "startColumn": 1, - "endLine": 19, - "endColumn": 31 - } - } - } - ], - "fingerprints": { - "HashCode": "fdZwTjfxyQHIYf+BmyPXyLEOqdG4U2NLBuFNLckqc/s=", - "gdnPrimarySignature": "8c4f1c7c24033f5c2d1af110b7167b907f6c213c8da0388cc94da267b3b26053", - "gdnAlternativeSignature0": "370b8fdda16cd6662fa9f668df5eb3d0a34e6a7df7f9a47aa8e76ca6db6d7ceb" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/idnsans.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/idnsans.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "e0143173968f10743c164db98a97f2f2ad51665ef207fc2e5ed568dadf16daa4", - "gdnAlternativeSignature0": "6a0059872bc6e5a9f1910e1c20b82a8c9770991596470214159c8d720884cf3c" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert.passwd.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert.passwd.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 37 - } - } - } - ], - "fingerprints": { - "HashCode": "vIPMvs25zTEA4CvYd/yXI5Q3s9TvruLN5sjPEqmD9Qo=", - "gdnPrimarySignature": "81cfc42c1d0b6a44b58032508492c13a1da8709259d9b955b7818b54375d7454", - "gdnAlternativeSignature0": "a3834b8e54bd96dedf30634b2195d9c1b45ffc9ac2d0cf9e7d72fb01ab2be4b6" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "a5673d23e7575ac45ddbdc1d2e29a20164ef7e82f569408bffe292ceb779806a", - "gdnAlternativeSignature0": "13b90a64372a219e131bd44c942fb99d2e0499c28a050af144f432498b71b0b7" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert2.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert2.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "0e05cea19167aed8b8ae01c841a7334ccb9c7fd7b993406580ff2832d15f7ce5", - "gdnAlternativeSignature0": "17f42847401af81a1d829e8aeac516090ae6c41935d0265f19c20dc5208ce44b" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert3.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert3.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "cc2c869c6af3917c188f3405a5cab29825b895ad248b5e8d5657be11a3575e97", - "gdnAlternativeSignature0": "77b87003353fbbbdd842e915e87ceb94a00295aaa14b394e046cfc2e3d4c70a3" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert4.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycert4.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "c778616f1b5c561f1c66d5843f0e4759cbccf82ba1868f8af267ba96077086df", - "gdnAlternativeSignature0": "c387065dadee4e1320aee04842ec83e45758e802066fcc8deba5055695bfe565" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycertecc.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/keycertecc.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "07fc5532f6969723a59a30bbf4679124b3408c52ad141644aefd5a5ee5ce3187", - "gdnAlternativeSignature0": "c3edc90bf722fd1545c98c99e988dc5405b162ce917767cb0aa7f53ac4954506" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/pycakey.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/pycakey.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "4d5d643001bdc9ca750ddc12572d03f20c6ea6b00ccec260daad81b2cbaad937", - "gdnAlternativeSignature0": "5940924f309382ed130dc9019b7ddc750982599de02152e9f2badb1a4def1c77" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/ssl_key.passwd.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/ssl_key.passwd.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 37 - } - } - } - ], - "fingerprints": { - "HashCode": "vIPMvs25zTEA4CvYd/yXI5Q3s9TvruLN5sjPEqmD9Qo=", - "gdnPrimarySignature": "78f7c576f77b65667382d3cd1f98088d2a5e607d39cc22af6729d55e1f62f28c", - "gdnAlternativeSignature0": "45c71b1ac1f0538e9be6aba276dcb1484749f62e1ed6c955b8d06b3a034f4295" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - }, - { - "ruleId": "CSCAN-GENERAL0020", - "ruleIndex": 0, - "rule": { - "id": "CSCAN-GENERAL0020" - }, - "level": "error", - "message": { - "text": "A potential secret was detected. Validate file contains secrets, remove, rotate credential, and use approved store. For additional information on secret remediation see the remediation section at https://aka.ms/CredScanDocs " - }, - "analysisTarget": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/ssl_key.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": ".gdn/i/nuget/Microsoft.Guardian.BanditRedist_windows_amd64.1.6.3.1/tools/lib/test/ssl_key.pem", - "uriBaseId": "file:///D:/source/security-devops-action/" - }, - "region": { - "startLine": 1, - "startColumn": 1, - "endLine": 1, - "endColumn": 27 - } - } - } - ], - "fingerprints": { - "HashCode": "K4LFfz40Tf2WjHYSwHcxzmrGBXdPbp+75ngl6MIfimE=", - "gdnPrimarySignature": "1f4b7943f9d0c70caa2b1022c17ac1978128ebd3cf36afb1375dbd8dade2cd89", - "gdnAlternativeSignature0": "47585da2145382121fadbbbe66ef8b7f20a193b4b38372d031552b996edb0fea" - }, - "suppressions": [], - "rank": 94.0, - "properties": { - "DefectCode": "SecretInFile", - "MatchingScore": 94.41, - "EnrichmentScore": 112.5, - "Severity": 94.0, - "Validation": "NoValidationRequested", - "Risk": "100" - } - } - ], - "columnKind": "utf16CodeUnits", - "policies": [ - { - "name": "Microsoft", - "version": "2.0.3" - } - ], - "properties": { - "toolInfoId": "credscan>>2>>202411062057" - } - }, - { - "tool": { - "driver": { - "name": "eslint", - "version": "8.56.0", - "informationUri": "https://eslint.org", - "properties": { - "RawName": "eslint" - } - } - }, - "invocations": [ - { - "toolConfigurationNotifications": [ - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/gulpfile.js", - "index": 0 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'const' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/lib/container-mapping.js", - "index": 1 - }, - "region": { - "startLine": 36, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'const' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/lib/main.js", - "index": 2 - }, - "region": { - "startLine": 35, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'const' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/lib/msdo-helpers.js", - "index": 3 - }, - "region": { - "startLine": 7, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'const' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/lib/msdo.js", - "index": 5 - }, - "region": { - "startLine": 36, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'const' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/lib/post.js", - "index": 6 - }, - "region": { - "startLine": 35, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'const' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/lib/pre.js", - "index": 7 - }, - "region": { - "startLine": 35, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'const' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/samples/insecure.js", - "index": 8 - }, - "region": { - "startLine": 1, - "startColumn": 5 - } - } - } - ], - "message": { - "text": "Parsing error: Unexpected token injection" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/src/container-mapping.ts", - "index": 9 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/src/main.ts", - "index": 10 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/src/msdo-helpers.ts", - "index": 11 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/src/msdo-interface.ts", - "index": 12 - }, - "region": { - "startLine": 4, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'export' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/src/msdo.ts", - "index": 13 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/src/post.ts", - "index": 14 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/src/pre.ts", - "index": 15 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/test/post.tests.ts", - "index": 16 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/test/pre.tests.ts", - "index": 17 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - }, - { - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "file:///D:/source/security-devops-action/test/testCommon.ts", - "index": 18 - }, - "region": { - "startLine": 1, - "startColumn": 1 - } - } - } - ], - "message": { - "text": "Parsing error: The keyword 'import' is reserved" - }, - "level": "error", - "descriptor": { - "id": "ESL0999" - } - } - ], - "executionSuccessful": false - } - ], - "versionControlProvenance": [ - { - "repositoryUri": "https://github.com/reynoldsa/security-devops-action", - "revisionId": "c5bc432f9640469fd713f651b4d18af73867f27a", - "branch": "main", - "properties": { - "RepositoryRoot": "D:\\source\\security-devops-action" - } - } - ], - "artifacts": [ - { - "location": { - "uri": "file:///D:/source/security-devops-action/gulpfile.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/lib/container-mapping.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/lib/main.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/lib/msdo-helpers.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/lib/msdo-interface.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/lib/msdo.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/lib/post.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/lib/pre.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/samples/insecure.js" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/src/container-mapping.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/src/main.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/src/msdo-helpers.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/src/msdo-interface.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/src/msdo.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/src/post.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/src/pre.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/test/post.tests.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/test/pre.tests.ts" - } - }, - { - "location": { - "uri": "file:///D:/source/security-devops-action/test/testCommon.ts" - } - } - ], - "results": [], - "columnKind": "utf16CodeUnits", - "policies": [ - { - "name": "Microsoft", - "version": "2.0.3" - } - ], - "properties": { - "toolInfoId": "eslint>>3>>202411062057" - } - }, - { - "tool": { - "driver": { - "name": "iacfilescanner", - "organization": "Microsoft", - "fullName": "IaC File Scanner", - "version": "0.1.3", - "rules": [ - { - "id": "IFS-1", - "name": "TagForResource", - "help": { - "text": "An IaC tag(s) was found on this resource. If there is a supported mapping tag, it will be used for code-to-cloud mapping." - }, - "shortDescription": { - "text": "An IaC tag(s) was found on this resource." - }, - "messageStrings": { - "default": { - "text": "An IaC tag(s) was found on this resource." - } - } - } - ], - "properties": { - "RawName": "iacfilescanner" - } - } - }, - "invocations": [ - { - "startTimeUtc": "2024-11-07T04:57:38.817Z", - "endTimeUtc": "2024-11-07T04:57:38.863Z", - "executionSuccessful": true - } - ], - "versionControlProvenance": [ - { - "repositoryUri": "https://github.com/reynoldsa/security-devops-action", - "revisionId": "c5bc432f9640469fd713f651b4d18af73867f27a", - "branch": "main", - "properties": { - "RepositoryRoot": "D:\\source\\security-devops-action" - } - } - ], - "originalUriBaseIds": { - "ROOTPATH": { - "uri": "file:///D:/source/security-devops-action" - } - }, - "artifacts": [ - { - "location": { - "uri": "samples/IaCMapping/main.tf", - "uriBaseId": "ROOTPATH" - } - } - ], - "results": [ - { - "ruleId": "IFS-1", - "ruleIndex": 0, - "level": "note", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 1 - } - }, - "logicalLocations": [ - { - "fullyQualifiedName": "iacmapping1212", - "kind": "azurerm_storage_account" - } - ], - "properties": { - "mappingTagDictionary": {"mapping_tag":"6189b638-15a5-42ec-b934-0d2b8e035ce1"} - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6a2b2a71245a88c5e349c7097ea77bb21272924e2d7d7fb032670e63664912bd", - "gdnAlternativeSignature0": "21dbf0708629d98ff73f008fe2a43b6e0848c2e9ab665049176189097f037ace" - } - } - ], - "columnKind": "utf16CodeUnits", - "policies": [ - { - "name": "Microsoft", - "version": "2.0.3" - } - ], - "properties": { - "toolInfoId": "iacfilescanner>>4>>202411062057" - } - }, - { - "tool": { - "driver": { - "name": "templateanalyzer", - "organization": "Microsoft", - "fullName": "Template Analyzer", - "version": "0.8.0+1ba73133c28786a16b2c19e5d5eef09eb2324538", - "informationUri": "https://github.com/Azure/template-analyzer", - "rules": [ - { - "id": "TA-000001", - "name": "AppService.EnableDiagnosticLogs", - "fullDescription": { - "text": "Enable auditing of diagnostic logs on the app. This enables you to recreate activity trails for investigation purposes if a security incident occurs or your network is compromised." - }, - "help": { - "text": "Enable diagnostic logs in App Service." - }, - "shortDescription": { - "text": "Diagnostic logs in App Service should be enabled." - }, - "messageStrings": { - "default": { - "text": "Enable auditing of diagnostic logs on the app. This enables you to recreate activity trails for investigation purposes if a security incident occurs or your network is compromised." - } - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000001-diagnostic-logs-in-app-service-should-be-enabled" - }, - { - "id": "TA-000003", - "name": "AppServiceAPIApp.OnlyFTPS", - "fullDescription": { - "text": "Enable FTPS enforcement for enhanced security." - }, - "help": { - "text": "Enable FTPS enforcement for enhanced security." - }, - "shortDescription": { - "text": "FTPS only should be required in your API app." - }, - "messageStrings": { - "default": { - "text": "Enable FTPS enforcement for enhanced security." - } - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000003-ftps-only-should-be-required-in-your-api-app" - }, - { - "id": "TA-000004", - "name": "AppServiceAPIApp.OnlyHTTPS", - "fullDescription": { - "text": "API apps should require HTTPS to ensure connections are made to the expected server and data in transit is protected from network layer eavesdropping attacks." - }, - "help": { - "text": "Use HTTPS to ensure server/service authentication and protect data in transit from network layer eavesdropping attacks." - }, - "shortDescription": { - "text": "API app should only be accessible over HTTPS." - }, - "messageStrings": { - "default": { - "text": "API apps should require HTTPS to ensure connections are made to the expected server and data in transit is protected from network layer eavesdropping attacks." - } - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md#ta-000004-api-app-should-only-be-accessible-over-https" - }, - { - "id": "TA-000005", - "name": "AppServiceAPIApp.UseLatestTLS", - "fullDescription": { - "text": "API apps should require the latest TLS version." - }, - "help": { - "text": "Upgrade to the latest TLS version." - }, - "shortDescription": { - "text": "Latest TLS version should be used in your API app." - }, - "messageStrings": { - "default": { - "text": "API apps should require the latest TLS version." - } - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000005-latest-tls-version-should-be-used-in-your-api-app" - }, - { - "id": "TA-000006", - "name": "AppServiceAPIApp.RestrictCORSAccess", - "fullDescription": { - "text": "Cross-Origin Resource Sharing (CORS) should not allow all domains to access your API app. Allow only required domains to interact with your API app." - }, - "help": { - "text": "Allow only required domains to interact with your API app." - }, - "shortDescription": { - "text": "CORS should not allow every resource to access your API app." - }, - "messageStrings": { - "default": { - "text": "Cross-Origin Resource Sharing (CORS) should not allow all domains to access your API app. Allow only required domains to interact with your API app." - } - }, - "defaultConfiguration": { - "level": "note" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000006-cors-should-not-allow-every-resource-to-access-your-api-app" - }, - { - "id": "TA-000007", - "name": "AppServiceAPIApp.UseManagedIdentity", - "fullDescription": { - "text": "For enhanced authentication security, use a managed identity. On Azure, managed identities eliminate the need for developers to have to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens." - }, - "help": { - "text": "Use a managed identity for enhanced authentication security." - }, - "shortDescription": { - "text": "Managed identity should be used in your API app." - }, - "messageStrings": { - "default": { - "text": "For enhanced authentication security, use a managed identity. On Azure, managed identities eliminate the need for developers to have to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens." - } - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000007-managed-identity-should-be-used-in-your-api-app" - }, - { - "id": "TA-000009", - "name": "AppServiceFunctionApp.OnlyFTPS", - "fullDescription": { - "text": "Enable FTPS enforcement for enhanced security." - }, - "help": { - "text": "Enable FTPS enforcement for enhanced security." - }, - "shortDescription": { - "text": "FTPS only should be required in your function app." - }, - "messageStrings": { - "default": { - "text": "Enable FTPS enforcement for enhanced security." - } - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000009-ftps-only-should-be-required-in-your-function-app" - }, - { - "id": "TA-000010", - "name": "AppServiceFunctionApp.OnlyHTTPS", - "fullDescription": { - "text": "Function apps should require HTTPS to ensure connections are made to the expected server and data in transit is protected from network layer eavesdropping attacks." - }, - "help": { - "text": "Use HTTPS to ensure server/service authentication and protect data in transit from network layer eavesdropping attacks." - }, - "shortDescription": { - "text": "Function app should only be accessible over HTTPS." - }, - "messageStrings": { - "default": { - "text": "Function apps should require HTTPS to ensure connections are made to the expected server and data in transit is protected from network layer eavesdropping attacks." - } - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000010-function-app-should-only-be-accessible-over-https" - }, - { - "id": "TA-000011", - "name": "AppServiceFunctionApp.UseLatestTLS", - "fullDescription": { - "text": "Function apps should require the latest TLS version." - }, - "help": { - "text": "Upgrade to the latest TLS version." - }, - "shortDescription": { - "text": "Latest TLS version should be used in your function app." - }, - "messageStrings": { - "default": { - "text": "Function apps should require the latest TLS version." - } - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000011-latest-tls-version-should-be-used-in-your-function-app" - }, - { - "id": "TA-000012", - "name": "AppServiceFunctionApp.RestrictCORSAccess", - "fullDescription": { - "text": "Cross-Origin Resource Sharing (CORS) should not allow all domains to access your function app. Allow only required domains to interact with your function app." - }, - "help": { - "text": "Allow only required domains to interact with your function app." - }, - "shortDescription": { - "text": "CORS should not allow every resource to access your function app." - }, - "messageStrings": { - "default": { - "text": "Cross-Origin Resource Sharing (CORS) should not allow all domains to access your function app. Allow only required domains to interact with your function app." - } - }, - "defaultConfiguration": { - "level": "note" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000012-cors-should-not-allow-every-resource-to-access-your-function-app" - }, - { - "id": "TA-000013", - "name": "AppServiceFunctionApp.UseManagedIdentity", - "fullDescription": { - "text": "For enhanced authentication security, use a managed identity. On Azure, managed identities eliminate the need for developers to have to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens." - }, - "help": { - "text": "Use a managed identity for enhanced authentication security." - }, - "shortDescription": { - "text": "Managed identity should be used in your function app." - }, - "messageStrings": { - "default": { - "text": "For enhanced authentication security, use a managed identity. On Azure, managed identities eliminate the need for developers to have to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens." - } - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000013-managed-identity-should-be-used-in-your-function-app" - }, - { - "id": "TA-000015", - "name": "AppServiceWebApp.OnlyFTPS", - "fullDescription": { - "text": "Enable FTPS enforcement for enhanced security." - }, - "help": { - "text": "Enable FTPS enforcement for enhanced security." - }, - "shortDescription": { - "text": "FTPS only should be required in your web app." - }, - "messageStrings": { - "default": { - "text": "Enable FTPS enforcement for enhanced security." - } - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000015-ftps-only-should-be-required-in-your-web-app" - }, - { - "id": "TA-000016", - "name": "AppServiceWebApp.OnlyHTTPS", - "fullDescription": { - "text": "Web apps should require HTTPS to ensure connections are made to the expected server and data in transit is protected from network layer eavesdropping attacks." - }, - "help": { - "text": "Use HTTPS to ensure server/service authentication and protect data in transit from network layer eavesdropping attacks." - }, - "shortDescription": { - "text": "Web apps should only be accessible over HTTPS." - }, - "messageStrings": { - "default": { - "text": "Web apps should require HTTPS to ensure connections are made to the expected server and data in transit is protected from network layer eavesdropping attacks." - } - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000016-web-apps-should-only-be-accessible-over-https" - }, - { - "id": "TA-000017", - "name": "AppServiceWebApp.UseLatestTLS", - "fullDescription": { - "text": "Web apps should require the latest TLS version." - }, - "help": { - "text": "Upgrade to the latest TLS version." - }, - "shortDescription": { - "text": "Latest TLS version should be used in your web app." - }, - "messageStrings": { - "default": { - "text": "Web apps should require the latest TLS version." - } - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000017-latest-tls-version-should-be-used-in-your-web-app" - }, - { - "id": "TA-000018", - "name": "AppServiceWebApp.RestrictCORSAccess", - "fullDescription": { - "text": "Cross-Origin Resource Sharing (CORS) should not allow all domains to access your web application. Allow only required domains to interact with your web app." - }, - "help": { - "text": "Allow only required domains to interact with your web app." - }, - "shortDescription": { - "text": "CORS should not allow every resource to access your web apps." - }, - "messageStrings": { - "default": { - "text": "Cross-Origin Resource Sharing (CORS) should not allow all domains to access your web application. Allow only required domains to interact with your web app." - } - }, - "defaultConfiguration": { - "level": "note" - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000018-cors-should-not-allow-every-resource-to-access-your-web-apps" - }, - { - "id": "TA-000019", - "name": "AppServiceWebApp.UseManagedIdentity", - "fullDescription": { - "text": "For enhanced authentication security, use a managed identity. On Azure, managed identities eliminate the need for developers to have to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens." - }, - "help": { - "text": "Use a managed identity for enhanced authentication security." - }, - "shortDescription": { - "text": "Managed identity should be used in your web app." - }, - "messageStrings": { - "default": { - "text": "For enhanced authentication security, use a managed identity. On Azure, managed identities eliminate the need for developers to have to manage credentials by providing an identity for the Azure resource in Azure AD and using it to obtain Azure Active Directory (Azure AD) tokens." - } - }, - "helpUri": "https://github.com/Azure/template-analyzer/blob/main/docs/built-in-rules.md/#ta-000019-managed-identity-should-be-used-in-your-web-app" - } - ], - "properties": { - "RawName": "templateanalyzer" - } - } - }, - "invocations": [ - { - "startTimeUtc": "2024-11-07T04:57:57.369Z", - "endTimeUtc": "2024-11-07T04:58:02.943Z", - "toolExecutionNotifications": [ - { - "message": { - "text": "Discovered 1 template-parameter pairs to analyze" - }, - "level": "note" - } - ], - "executionSuccessful": true - } - ], - "versionControlProvenance": [ - { - "repositoryUri": "https://github.com/reynoldsa/security-devops-action", - "revisionId": "c5bc432f9640469fd713f651b4d18af73867f27a", - "branch": "main", - "properties": { - "RepositoryRoot": "D:\\source\\security-devops-action" - } - } - ], - "originalUriBaseIds": { - "ROOTPATH": { - "uri": "file:///D:/source/security-devops-action" - } - }, - "artifacts": [ - { - "location": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - } - } - ], - "results": [ - { - "ruleId": "TA-000001", - "ruleIndex": 0, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 264 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "be38ef4a5beacf017f220b3d98472e58b2a22f36fd9be444d705a6da0156fd74", - "gdnAlternativeSignature0": "ed2c5f6d187878540408f5bbb17875166e824df9cd545c2071e66b80f6c4bb01" - } - }, - { - "ruleId": "TA-000001", - "ruleIndex": 0, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 179 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 215 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 280 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d37d8282e31133b27146eb024c2736f1d7b65bdd6a42c08607bdb2bead9b5423", - "gdnAlternativeSignature0": "4c40d2cc63ce679ba6157fdc72d12b40481a893ab13296c36239c7ed8622cb86" - } - }, - { - "ruleId": "TA-000003", - "ruleIndex": 1, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 165 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 179 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 215 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3549d0b318441c8b49bd89ca36ea85f70bf45f022ab8c0609706ff5f5a88d2d8", - "gdnAlternativeSignature0": "0edb0e1b59eb8e8d3fc182d647d80d92147876a0454f610aa836842fb52181e5" - } - }, - { - "ruleId": "TA-000003", - "ruleIndex": 1, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 195 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8dbbe99de40dee0eb3c9eb6568f8e48c5effeec820c694f77be3999efaad513b", - "gdnAlternativeSignature0": "f244a0d31df3eeaba0ca511703721d84fe79f8beb849b3cc4453a7f7e9ffc9c1" - } - }, - { - "ruleId": "TA-000004", - "ruleIndex": 2, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 29 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d1a766811373af8220668a1819fa53325b88ba36dffbf2706701e95dfa1e0aed", - "gdnAlternativeSignature0": "f0153f08d6d8174a8d76708b6935ba0ef2b38e06ff62758758e948d72ef1be52" - } - }, - { - "ruleId": "TA-000004", - "ruleIndex": 2, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 44 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6e9b1515330559d9ab46e4c5b6afdd2ee9a07fc5122b6c3748c3688cfeca2789", - "gdnAlternativeSignature0": "c0dd2c03abc529e58717d3191f4fd5e673d3316a39093ba7bd9b964920239290" - } - }, - { - "ruleId": "TA-000005", - "ruleIndex": 3, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 165 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 179 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 215 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b7e1a7ec5ef845a37f73c0407622bd567cdd07b2dfedc75a099d29862f374762", - "gdnAlternativeSignature0": "1cdb3b2f11971c7558e88df98da50f7e9a361b97dd3668a26543df13716d7597" - } - }, - { - "ruleId": "TA-000005", - "ruleIndex": 3, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 195 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4643d07151b417ee81f093352a1a712e3536febd4f9c68cb9774bc20578541ec", - "gdnAlternativeSignature0": "c90df3b94d648eb7354b444cbe823e580310b5eda0a5391f3f4c80b4c05317f9" - } - }, - { - "ruleId": "TA-000006", - "ruleIndex": 4, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 218 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "65526fef72a3bd5686e08978f894f58ee0c94d6b14333616137c1197168771c2", - "gdnAlternativeSignature0": "6738218eca8210aa235b6a000d75d9cd8ec306669ef6783344d819eea5ad16a3" - } - }, - { - "ruleId": "TA-000006", - "ruleIndex": 4, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 199 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "74875f7d2bcd35300850f78ef8d77d9ccba6f1f3e01f8aa8126148d78a157a60", - "gdnAlternativeSignature0": "02e4138fc76c7af81aa05894a92ac4d606d31021aa410b0aa62abbdd6eba5eed" - } - }, - { - "ruleId": "TA-000007", - "ruleIndex": 5, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 187 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "be7e0b3fdbf227e31c46e9fd7b3a36bd67a5577fe93fabe7de457fb26f58dc34", - "gdnAlternativeSignature0": "b76e97cfdd95980416531f989be2fa221fd4f3689c9ca167b515e573bc729d04" - } - }, - { - "ruleId": "TA-000009", - "ruleIndex": 6, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 309 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "265486bd8e28eea8810483d45dcc81f731cd9776eed60a7893943e33d9b07b6f", - "gdnAlternativeSignature0": "ed44340ff877ba9d68523648797c4f0f7b56c7c028d93312e55d5c4dc2cdf0a8" - } - }, - { - "ruleId": "TA-000009", - "ruleIndex": 6, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 179 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 215 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 325 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d70937da25c2f4c4bf54334ccc495634c22918258a3b46e45fcd389fd482855f", - "gdnAlternativeSignature0": "34c1f242e862c9a4b1ccceb99a33b1aac0df47aadcc5e6b4cc6a9957bfa60ddb" - } - }, - { - "ruleId": "TA-000010", - "ruleIndex": 7, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 70 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "f09e415fda3660cc86a250ca8b35c87db6d6f5d15c69693c1cee3d5a1bd841c4", - "gdnAlternativeSignature0": "c11ab3206f07476269883239e183c0a38d77630a64dc0a0fac178d45f7f0cb8b" - } - }, - { - "ruleId": "TA-000010", - "ruleIndex": 7, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 85 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b3eb7244e83ec91b3b2fdcadbdd498b8f8095140b6c23b74883b5ca61878a994", - "gdnAlternativeSignature0": "3a37f05c16ebd9b493c603024eeb1c5f9c35a5a44d2378a11b6c33003fba815f" - } - }, - { - "ruleId": "TA-000011", - "ruleIndex": 8, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 309 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "03a4354b8e006461fb34be109fe4601b633f817d7b237a02dbf2f1346d91e0b4", - "gdnAlternativeSignature0": "c490246cd2a3ee33ab981814e002c687938d33fba3eefb891fd8db2c7a92d64c" - } - }, - { - "ruleId": "TA-000011", - "ruleIndex": 8, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 179 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 215 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 325 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "24126068b178d605ced2a12989a4fa2ebd4d15eb30c7c2c37b9d3c6cab349686", - "gdnAlternativeSignature0": "c5923a6a520bd145ef3957d75a6c9d8cbf1025fcd4915449e60bbb02bd10a9e8" - } - }, - { - "ruleId": "TA-000012", - "ruleIndex": 9, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 313 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "36e23dcaac28961df21b197c601391bc76f0c90d72380a56f1fa29327bd0017c", - "gdnAlternativeSignature0": "3fa84bc1cc49b60bb7204fb88c7fada8faa63e5f8c9984e10e1b83d3b95bb9e7" - } - }, - { - "ruleId": "TA-000012", - "ruleIndex": 9, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 218 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b20e23b6ed47125967e4d087e615c9d4a9aba6d7dd9e2176ed151efcebb82d02", - "gdnAlternativeSignature0": "2ee68544e8b6b6a54363c95df7bd3d1422cf1c193ed005dddba038439e9650d7" - } - }, - { - "ruleId": "TA-000013", - "ruleIndex": 10, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 319 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ed0363b6f87f1d4e7c0807f68e051e1b3787bafbd2455d4528a10bf4d94b9edf", - "gdnAlternativeSignature0": "72d1e7c2a57d35fa08e178c56264746e48ed563a4da2d7f5dc8570438ed5d90c" - } - }, - { - "ruleId": "TA-000015", - "ruleIndex": 11, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 264 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ec6f5ed98134d472df3077d9c883d07c25923b8e2f9f0ee4ad62438d0a72b4f4", - "gdnAlternativeSignature0": "5e86cd98e6560ffb14e4eb72c99c8a11042feb183a1713a37c682f8417eca214" - } - }, - { - "ruleId": "TA-000015", - "ruleIndex": 11, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 179 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 215 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 280 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "38abf206aa092efe2a8fa6d3546f1a342e08c86f4b25be558b8afd5bcb532651", - "gdnAlternativeSignature0": "f53e1db90f911dfc29b77e08c01fc16a609b9720473da74edae88428a4e2bc1c" - } - }, - { - "ruleId": "TA-000016", - "ruleIndex": 12, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 111 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "408e1546a1bb4ad4b304b67d08fe6d99943c94a173d95edb8ddaa25a03798989", - "gdnAlternativeSignature0": "84d16425f679ebd232bade7a449c0cdbfef3776b9b928279599f00b71fbcfb22" - } - }, - { - "ruleId": "TA-000016", - "ruleIndex": 12, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 125 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "bce4367d8d17dfed8e0504a56bdbde591cfc253f3632a2f21142dffcac9e33a5", - "gdnAlternativeSignature0": "e76df0666939f84d4c4ef2f13e863a131ecb5f7e36060126730de3bd7aee8734" - } - }, - { - "ruleId": "TA-000017", - "ruleIndex": 13, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 264 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "84b7be1aa526d0d05c2de90d3e202c9ac537835e7b3140c871e452ee53505cf7", - "gdnAlternativeSignature0": "059a393d058cffaa8cdbe2fad159ee89d4f04ea63fcbee3981408888a39432e8" - } - }, - { - "ruleId": "TA-000017", - "ruleIndex": 13, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 179 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 215 - } - } - }, - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 280 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c8faed1d99ea5b1ced01439da3370660d482769c59a7665dda694f69f336bc76", - "gdnAlternativeSignature0": "216e7a3be42bfa5aae23ed3abf7bccf0d48aa5ad6ae0f617088f0de77723da89" - } - }, - { - "ruleId": "TA-000018", - "ruleIndex": 14, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 268 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b17d79fb40da84e43645bcdc62e3f720f8039e9be3454fd847cbef27623eea6f", - "gdnAlternativeSignature0": "4050ebca29a424898a464da3b851e5ae90c32384f653b4478a38a331c2639fa8" - } - }, - { - "ruleId": "TA-000018", - "ruleIndex": 14, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 218 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "478c565e6f29f52f64c42e08aca4fe0c94a9ed37a629ab4529d58c764580df69", - "gdnAlternativeSignature0": "0a4753c9c18e6f31fb4d238774fa74f694731b7aff4566b256d7a4f2c93536cd" - } - }, - { - "ruleId": "TA-000019", - "ruleIndex": 15, - "level": "error", - "message": { - "id": "default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json", - "uriBaseId": "ROOTPATH" - }, - "region": { - "startLine": 274 - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b7f8fadd8a0cbc8600952d828f4f4f59c163cd99350430230e976a126d8c9a89", - "gdnAlternativeSignature0": "1dad07739d9b4f2d7845f7a709da12b240d41c1a3536217dc80b23b104ea5362" - } - } - ], - "columnKind": "utf16CodeUnits", - "policies": [ - { - "name": "Microsoft", - "version": "2.0.3" - } - ], - "properties": { - "toolInfoId": "templateanalyzer>>5>>202411062057" - } - }, - { - "tool": { - "driver": { - "name": "checkov", - "organization": "bridgecrew", - "version": "3.2.199", - "informationUri": "https://checkov.io", - "rules": [ - { - "id": "CKV_AZURE_59", - "name": "Ensure that Storage accounts disallow public access", - "fullDescription": { - "text": "Ensure that Storage accounts disallow public access" - }, - "help": { - "text": "Ensure that Storage accounts disallow public access\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/ensure-that-storage-accounts-disallow-public-access" - }, - { - "id": "CKV_AZURE_33", - "name": "Ensure Storage logging is enabled for Queue service for read, write and delete requests", - "fullDescription": { - "text": "Ensure Storage logging is enabled for Queue service for read, write and delete requests" - }, - "help": { - "text": "Ensure Storage logging is enabled for Queue service for read, write and delete requests\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-logging-policies/enable-requests-on-storage-logging-for-queue-service" - }, - { - "id": "CKV_AZURE_44", - "name": "Ensure Storage Account is using the latest version of TLS encryption", - "fullDescription": { - "text": "Ensure Storage Account is using the latest version of TLS encryption" - }, - "help": { - "text": "Ensure Storage Account is using the latest version of TLS encryption\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-storage-policies/bc-azr-storage-2" - }, - { - "id": "CKV_AZURE_190", - "name": "Ensure that Storage blobs restrict public access", - "fullDescription": { - "text": "Ensure that Storage blobs restrict public access" - }, - "help": { - "text": "Ensure that Storage blobs restrict public access\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/azr-networking-190" - }, - { - "id": "CKV2_AZURE_40", - "name": "Ensure storage account is not configured with Shared Key authorization", - "fullDescription": { - "text": "Ensure storage account is not configured with Shared Key authorization" - }, - "help": { - "text": "Ensure storage account is not configured with Shared Key authorization\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-iam-policies/bc-azure-2-40" - }, - { - "id": "CKV2_AZURE_47", - "name": "Ensure storage account is configured without blob anonymous access", - "fullDescription": { - "text": "Ensure storage account is configured without blob anonymous access" - }, - "help": { - "text": "Ensure storage account is configured without blob anonymous access\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-iam-policies/bc-azure-2-47" - }, - { - "id": "CKV2_AZURE_33", - "name": "Ensure storage account is configured with private endpoint", - "fullDescription": { - "text": "Ensure storage account is configured with private endpoint" - }, - "help": { - "text": "Ensure storage account is configured with private endpoint\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/bc-azure-2-33" - }, - { - "id": "CKV2_AZURE_41", - "name": "Ensure storage account is configured with SAS expiration policy", - "fullDescription": { - "text": "Ensure storage account is configured with SAS expiration policy" - }, - "help": { - "text": "Ensure storage account is configured with SAS expiration policy\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-iam-policies/bc-azure-2-41" - }, - { - "id": "CKV2_AZURE_38", - "name": "Ensure soft-delete is enabled on Azure storage account", - "fullDescription": { - "text": "Ensure soft-delete is enabled on Azure storage account" - }, - "help": { - "text": "Ensure soft-delete is enabled on Azure storage account\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/bc-azure-2-38" - }, - { - "id": "CKV2_AZURE_1", - "name": "Ensure storage for critical data are encrypted with Customer Managed Key", - "fullDescription": { - "text": "Ensure storage for critical data are encrypted with Customer Managed Key" - }, - "help": { - "text": "Ensure storage for critical data are encrypted with Customer Managed Key\nResource: azurerm_storage_account.terraformaccount1" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/ensure-storage-for-critical-data-are-encrypted-with-customer-managed-key" - }, - { - "id": "CKV_K8S_25", - "name": "Minimize the admission of containers with added capability", - "fullDescription": { - "text": "Minimize the admission of containers with added capability" - }, - "help": { - "text": "Minimize the admission of containers with added capability\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-24" - }, - { - "id": "CKV_K8S_20", - "name": "Containers should not run with allowPrivilegeEscalation", - "fullDescription": { - "text": "Containers should not run with allowPrivilegeEscalation" - }, - "help": { - "text": "Containers should not run with allowPrivilegeEscalation\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-19" - }, - { - "id": "CKV_K8S_21", - "name": "The default namespace should not be used", - "fullDescription": { - "text": "The default namespace should not be used" - }, - "help": { - "text": "The default namespace should not be used\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-20" - }, - { - "id": "CKV_K8S_28", - "name": "Minimize the admission of containers with the NET_RAW capability", - "fullDescription": { - "text": "Minimize the admission of containers with the NET_RAW capability" - }, - "help": { - "text": "Minimize the admission of containers with the NET_RAW capability\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-27" - }, - { - "id": "CKV_K8S_43", - "name": "Image should use digest", - "fullDescription": { - "text": "Image should use digest" - }, - "help": { - "text": "Image should use digest\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-39" - }, - { - "id": "CKV_K8S_8", - "name": "Liveness Probe Should be Configured", - "fullDescription": { - "text": "Liveness Probe Should be Configured" - }, - "help": { - "text": "Liveness Probe Should be Configured\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-7" - }, - { - "id": "CKV_K8S_37", - "name": "Minimize the admission of containers with capabilities assigned", - "fullDescription": { - "text": "Minimize the admission of containers with capabilities assigned" - }, - "help": { - "text": "Minimize the admission of containers with capabilities assigned\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-34" - }, - { - "id": "CKV_K8S_29", - "name": "Apply security context to your pods and containers", - "fullDescription": { - "text": "Apply security context to your pods and containers" - }, - "help": { - "text": "Apply security context to your pods and containers\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/ensure-securitycontext-is-applied-to-pods-and-containers" - }, - { - "id": "CKV_K8S_22", - "name": "Use read-only filesystem for containers where possible", - "fullDescription": { - "text": "Use read-only filesystem for containers where possible" - }, - "help": { - "text": "Use read-only filesystem for containers where possible\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-21" - }, - { - "id": "CKV_K8S_23", - "name": "Minimize the admission of root containers", - "fullDescription": { - "text": "Minimize the admission of root containers" - }, - "help": { - "text": "Minimize the admission of root containers\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-22" - }, - { - "id": "CKV_K8S_40", - "name": "Containers should run as a high UID to avoid host conflict", - "fullDescription": { - "text": "Containers should run as a high UID to avoid host conflict" - }, - "help": { - "text": "Containers should run as a high UID to avoid host conflict\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-37" - }, - { - "id": "CKV_K8S_31", - "name": "Ensure that the seccomp profile is set to docker/default or runtime/default", - "fullDescription": { - "text": "Ensure that the seccomp profile is set to docker/default or runtime/default" - }, - "help": { - "text": "Ensure that the seccomp profile is set to docker/default or runtime/default\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-29" - }, - { - "id": "CKV_K8S_38", - "name": "Ensure that Service Account Tokens are only mounted where necessary", - "fullDescription": { - "text": "Ensure that Service Account Tokens are only mounted where necessary" - }, - "help": { - "text": "Ensure that Service Account Tokens are only mounted where necessary\nResource: StatefulSet.default.cassandra" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/kubernetes-policies/kubernetes-policy-index/bc-k8s-35" - }, - { - "id": "CKV2_K8S_6", - "name": "Minimize the admission of pods which lack an associated NetworkPolicy", - "fullDescription": { - "text": "Minimize the admission of pods which lack an associated NetworkPolicy" - }, - "help": { - "text": "Minimize the admission of pods which lack an associated NetworkPolicy\nResource: Pod.default.cassandra.app-cassandra" - }, - "defaultConfiguration": { - "level": "error" - } - }, - { - "id": "CKV_AZURE_225", - "name": "Ensure the App Service Plan is zone redundant", - "fullDescription": { - "text": "Ensure the App Service Plan is zone redundant" - }, - "help": { - "text": "Ensure the App Service Plan is zone redundant\nResource: Microsoft.Web/serverfarms.serverFarm" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-storage-policies/bc-azure-225" - }, - { - "id": "CKV_AZURE_17", - "name": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set", - "fullDescription": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "help": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/bc-azr-networking-7" - }, - { - "id": "CKV_AZURE_78", - "name": "Ensure FTP deployments are disabled", - "fullDescription": { - "text": "Ensure FTP deployments are disabled" - }, - "help": { - "text": "Ensure FTP deployments are disabled\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/ensure-ftp-deployments-are-disabled" - }, - { - "id": "CKV_AZURE_18", - "name": "Ensure that 'HTTP Version' is the latest if used to run the web app", - "fullDescription": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "help": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/bc-azr-networking-8" - }, - { - "id": "CKV_AZURE_14", - "name": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service", - "fullDescription": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service" - }, - "help": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/bc-azr-networking-5" - }, - { - "id": "CKV_AZURE_16", - "name": "Ensure that Register with Azure Active Directory is enabled on App Service", - "fullDescription": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "help": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-iam-policies/bc-azr-iam-1" - }, - { - "id": "CKV_AZURE_71", - "name": "Ensure that Managed identity provider is enabled for web apps", - "fullDescription": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "help": { - "text": "Ensure that Managed identity provider is enabled for web apps\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/ensure-that-managed-identity-provider-is-enabled-for-app-services" - }, - { - "id": "CKV_AZURE_15", - "name": "Ensure web app is using the latest version of TLS encryption", - "fullDescription": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "help": { - "text": "Ensure web app is using the latest version of TLS encryption\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/bc-azr-networking-6" - }, - { - "id": "CKV_AZURE_222", - "name": "Ensure that Azure Web App public network access is disabled", - "fullDescription": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "help": { - "text": "Ensure that Azure Web App public network access is disabled\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/azr-networking-63" - }, - { - "id": "CKV_AZURE_153", - "name": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot", - "fullDescription": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot" - }, - "help": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/ensure-azure-web-app-redirects-all-http-traffic-to-https-in-azure-app-service-slot" - }, - { - "id": "CKV_AZURE_67", - "name": "Ensure that 'HTTP Version' is the latest, if used to run the Function app", - "fullDescription": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "help": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/ensure-that-http-version-is-the-latest-if-used-to-run-the-function-app" - }, - { - "id": "CKV_AZURE_70", - "name": "Ensure that Function apps is only accessible over HTTPS", - "fullDescription": { - "text": "Ensure that Function apps is only accessible over HTTPS" - }, - "help": { - "text": "Ensure that Function apps is only accessible over HTTPS\nResource: Microsoft.Web/sites.ApiAppNoHttps" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-networking-policies/ensure-that-function-apps-is-only-accessible-over-https" - }, - { - "id": "CKV_AZURE_13", - "name": "Ensure App Service Authentication is set on Azure App Service", - "fullDescription": { - "text": "Ensure App Service Authentication is set on Azure App Service" - }, - "help": { - "text": "Ensure App Service Authentication is set on Azure App Service\nResource: Microsoft.Web/sites/config.SitesConfig/RestrictedCORSAccess_web" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/bc-azr-general-2" - }, - { - "id": "CKV_AZURE_65", - "name": "Ensure that App service enables detailed error messages", - "fullDescription": { - "text": "Ensure that App service enables detailed error messages" - }, - "help": { - "text": "Ensure that App service enables detailed error messages\nResource: Microsoft.Web/sites/config.SitesConfig/RestrictedCORSAccess_web" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-logging-policies/tbdensure-that-app-service-enables-detailed-error-messages" - }, - { - "id": "CKV_AZURE_80", - "name": "Ensure that 'Net Framework' version is the latest, if used as a part of the web app", - "fullDescription": { - "text": "Ensure that 'Net Framework' version is the latest, if used as a part of the web app" - }, - "help": { - "text": "Ensure that 'Net Framework' version is the latest, if used as a part of the web app\nResource: Microsoft.Web/sites/config.SitesConfig/RestrictedCORSAccess_web" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/ensure-that-net-framework-version-is-the-latest-if-used-as-a-part-of-the-web-app" - }, - { - "id": "CKV_AZURE_66", - "name": "Ensure that App service enables failed request tracing", - "fullDescription": { - "text": "Ensure that App service enables failed request tracing" - }, - "help": { - "text": "Ensure that App service enables failed request tracing\nResource: Microsoft.Web/sites/config.SitesConfig/RestrictedCORSAccess_web" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-logging-policies/ensure-that-app-service-enables-failed-request-tracing" - }, - { - "id": "CKV_AZURE_63", - "name": "Ensure that App service enables HTTP logging", - "fullDescription": { - "text": "Ensure that App service enables HTTP logging" - }, - "help": { - "text": "Ensure that App service enables HTTP logging\nResource: Microsoft.Web/sites/config.SitesConfig/RestrictedCORSAccess_web" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-logging-policies/ensure-that-app-service-enables-http-logging" - }, - { - "id": "CKV_AZURE_88", - "name": "Ensure that app services use Azure Files", - "fullDescription": { - "text": "Ensure that app services use Azure Files" - }, - "help": { - "text": "Ensure that app services use Azure Files\nResource: Microsoft.Web/sites/config.SitesConfig/RestrictedCORSAccess_web" - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/azure-policies/azure-general-policies/ensure-that-app-services-use-azure-files" - }, - { - "id": "CKV_DOCKER_2", - "name": "Ensure that HEALTHCHECK instructions have been added to container images", - "fullDescription": { - "text": "Ensure that HEALTHCHECK instructions have been added to container images" - }, - "help": { - "text": "Ensure that HEALTHCHECK instructions have been added to container images\nResource: /samples\\Dockerfile." - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/docker-policies/docker-policy-index/ensure-that-healthcheck-instructions-have-been-added-to-container-images" - }, - { - "id": "CKV_DOCKER_3", - "name": "Ensure that a user for the container has been created", - "fullDescription": { - "text": "Ensure that a user for the container has been created" - }, - "help": { - "text": "Ensure that a user for the container has been created\nResource: /samples\\Dockerfile." - }, - "defaultConfiguration": { - "level": "error" - }, - "helpUri": "https://docs.prismacloud.io/en/enterprise-edition/policy-reference/docker-policies/docker-policy-index/ensure-that-a-user-for-the-container-has-been-created" - } - ], - "properties": { - "RawName": "checkov" - } - } - }, - "versionControlProvenance": [ - { - "repositoryUri": "https://github.com/reynoldsa/security-devops-action", - "revisionId": "c5bc432f9640469fd713f651b4d18af73867f27a", - "branch": "main", - "properties": { - "RepositoryRoot": "D:\\source\\security-devops-action" - } - } - ], - "results": [ - { - "ruleId": "CKV_DOCKER_2", - "ruleIndex": 42, - "level": "note", - "message": { - "text": "Ensure that HEALTHCHECK instructions have been added to container images" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/Dockerfile" - }, - "region": { - "startLine": 1, - "endLine": 2, - "snippet": { - "text": "FROM alpine:3.14.0\nRUN echo \"testuser:x:10999:10999:,,,:/home/testuser:/bin/bash\" >> /etc/passwd && echo \"testuser::18761:0:99999:7:::\" >> /etc/shadow\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d575ad4cda6d50d0a5b22693f2455c3705f7c36bb4b15adc9192690133bf9374", - "gdnAlternativeSignature0": "b638b75acb82e58442ebe3ecff85569f7009ac136ba3db701680ba599b613c84" - }, - "attachments": [] - }, - { - "ruleId": "CKV_DOCKER_3", - "ruleIndex": 43, - "level": "note", - "message": { - "text": "Ensure that a user for the container has been created" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/Dockerfile" - }, - "region": { - "startLine": 1, - "endLine": 2, - "snippet": { - "text": "FROM alpine:3.14.0\nRUN echo \"testuser:x:10999:10999:,,,:/home/testuser:/bin/bash\" >> /etc/passwd && echo \"testuser::18761:0:99999:7:::\" >> /etc/shadow\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "1242a262a0e0dc6e681ad67a81a121ae66f0b2562d1d669a066233daaa7a615a", - "gdnAlternativeSignature0": "81662655dc607bf53d1554c1d9b11df48d1d564eac1529173b8d0a2e61969e63" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_225", - "ruleIndex": 24, - "message": { - "text": "Ensure the App Service Plan is zone redundant" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 14, - "endLine": 19, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/serverfarms\",\n \"name\": \"serverFarm\",\n \"location\": \"[parameters('location')]\"\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5221a67299814f524df6a6522077373a0fd22c2d3f1748e6025a8552333f7141", - "gdnAlternativeSignature0": "e6aab96518196be0b417589447f9a67fea4ac7f6f0a89667d1394f61195916f6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2052f152b6d5766a7b59a9c7b4f1d29a0f7bb9060b894b7a65b9e58207a28318", - "gdnAlternativeSignature0": "e18fdea947ebaa5eeda8e3c09e3f041d81794a440563fc2ff6526653c22c9300" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5f73cccef9e3e5de304868ce267f1b16ddbbad0c9c52f4c39866f9e347b7d5d2", - "gdnAlternativeSignature0": "7806091df62a7f1ac9e2fd4c6a8f834a76f61fa22e8e6b577272499b3e942104" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b9354cf1c73339ba30adcd102787a08181f25aaff9f6782332e185ca65fb9a6b", - "gdnAlternativeSignature0": "c95f4a7cc4fb4eab5c51d2885069bf453fc79e65aac23ff2d04b015fe36717d0" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_14", - "ruleIndex": 28, - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "be5f727a2ce11c634799422c02b8a4786d9b57ab5402571c6025b43e6b077b3d", - "gdnAlternativeSignature0": "30eef084050fc6c74f476b1ce8aa4c766974299647bbfbf548e37ddd1fc1ceb1" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8ec0cf28cfe77f1886c419e78389f8a6102878cb7612535c76516fd29c664889", - "gdnAlternativeSignature0": "b35de61bb549b7a7050bd10b59810d2d6c10f19155d87b4bd9b1f6a92ac87ae8" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ee7a8721be19f071f11af14aaa7aca9576415f4d4d1e62346a853f58100c82e4", - "gdnAlternativeSignature0": "ec622e49b27f8e22a9e6f643cf8cf907eb559102eae22be4e991b94720701355" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ef9b889940362ecd85475a4717800814cf44656011817a1d3370a3bcab0f9006", - "gdnAlternativeSignature0": "e8b887b9e4630d128d846a53822b5d4d36f7078245f4e138644f951d5da1c04b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e1dc47b6e2369dd39565addd153d327dd0d76de16d44d15949739b4ece89857b", - "gdnAlternativeSignature0": "d43a14c244c9f2b15c1ba3cec391a15f1baf94ac985c7db47cad796ef60a075e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_153", - "ruleIndex": 33, - "level": "note", - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "97d7ca490bf520d069c13ebdb0d71a09b89fcd7769dde3ce13a56b628553002e", - "gdnAlternativeSignature0": "4900509f0378a44b7fbeba8ec8bf1dbc277cdf17e6f3726e7111c71d23884b5a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c56f175caf69b08d44542457468251bc934009d1db105d9b7bbebcbcb3beb452", - "gdnAlternativeSignature0": "913aa909429d17a84df0c1e8748ff6aea024d4ef99d8904de01b8ebfebb6ef2c" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_70", - "ruleIndex": 35, - "message": { - "text": "Ensure that Function apps is only accessible over HTTPS" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 20, - "endLine": 32, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2f45d4e06de44a01421e37f361501aad833ff822c1204548527c417f387a4bfd", - "gdnAlternativeSignature0": "c1176515ad6f738679a0735507229cb7137334a387d241a6f6f16e99fe2c2751" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "77217b91fc3a35c94077b14d569129379554bc6c475fef0ed13e21ee3f64e356", - "gdnAlternativeSignature0": "a92fd4c7fd1c82fb5c7c4e2b973b97bd202817cb9be0631ec7ec94540eca12a0" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "96272a03ccdc56a5a8b96c5109daa200cb64585bf6cb129647e817d4f08a4bfd", - "gdnAlternativeSignature0": "3e23e70479065b8d7df9e45448b64661427e6438fbe37d04a36266ce32bee78a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "67cba71b0950ef66e3a3a355b9a91d2d202c0813fe7f8b95e741f7812d7033fc", - "gdnAlternativeSignature0": "fbff0c06779ae5a02c627c32a5286e16208ac4855c442d93ca1238f9c34e1d11" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_14", - "ruleIndex": 28, - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2f0e38a86c95c2a52024f64d67bcd131909536c1a0e5b97ad5e093d03574efea", - "gdnAlternativeSignature0": "5ecca9431ae6ccf7833666998b89928bef027106342ff5293dbcea4fbfd8985f" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "047e97aec3e3db452cd6494fac807837f79de6dbf49057a6af9fa720f29866cd", - "gdnAlternativeSignature0": "4d3bb1bfb723ab4e801290b50a7f708428b9978d60d75e9d4b421ce3261e987e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "60a28f98c4f08919941b75af5cb9983b3d6d0d4fdbdfd72f0090398ec41d78b8", - "gdnAlternativeSignature0": "81e9acc15fdef5820327f3e7ba5bb7d63c7bc0ff05365b47c155fc8b5bf089f7" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "661df7e8e64bd5bdd34a1d439102b29529c13c746e5feb730fac1b89383a4443", - "gdnAlternativeSignature0": "20930ab9036a0597b410e4bbb9a3ae46121270bb3384764cabfe9af8664ee4c2" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "eef8e08eefe99efe6383c19a6c2f6896c9ef94af166789c535a44e439faff96b", - "gdnAlternativeSignature0": "d9cfc31c8fd621cb84b4ac6e4aebe8fbdc58ac745ec38f3cad71e0d6516cac75" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_153", - "ruleIndex": 33, - "level": "note", - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b6eaac00912ec69927074d73bba19050e90b93d7c2863034c5f57ac14b6c3f56", - "gdnAlternativeSignature0": "63b91b45615d3139f0c8994573f3c9045d8602acb32fa9a2b56ff7b1e8c030d3" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "1a28305cc04bfcb6e7d6cd127478774b1bee41fc46643d7798536ede530ea751", - "gdnAlternativeSignature0": "0f393c4096130800e7805e4a66f1950cf2d771868201e4632b04ce44be71df65" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_70", - "ruleIndex": 35, - "message": { - "text": "Ensure that Function apps is only accessible over HTTPS" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 33, - "endLine": 46, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b7e0569871e364eb4f1ad13804f6bbac0225d2b791ccc53683c54d880a8d56ad", - "gdnAlternativeSignature0": "558d0b90f8a67d09495fad85b33c5877cb89b0319b670c21c57f4959cca1eaf1" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "aaddab01eb68ff517e9444b2e9d93377d31950dea3e82ddb8ab944c5dfee7c1f", - "gdnAlternativeSignature0": "57cebe241ad3cf45ef799f306169e2b2264d43b4510f8e0752726be7131ab490" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "590ff9987116abb8a3400496eddeb58c1f024d384c76051a44dc2c39e4e49283", - "gdnAlternativeSignature0": "61d1b562f5d44f6ddbc3c5c2814e820572971c9d99d41830d74ee6dab9e8b1b8" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "db280dfff3e2790244c8b505a80423b5448fc8e75d59a62250b7a79536cf7ff1", - "gdnAlternativeSignature0": "ac8dbe3cffb76f8661fc62b1f0f4da58be581b4d0ef2caa7f3cd204caca2463e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "aea92e244aca978f643e2e1ac665f53ebba318185bd336a66cd47a0b665060ec", - "gdnAlternativeSignature0": "b48782f8c3aa23c586d734684f31424229a47093f158458dc6b2d60685e3d769" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "798c50dcd9acde7329ed5009c3d07a2217acf11fec1f31004c527083961c540d", - "gdnAlternativeSignature0": "165dd9555e9c9161445bf7503e05d3cbed8dcbb6302f42637d6b45ccaca0ff58" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4c5dfc4574f7685bcd89ddc5a65c7d0c511987abf75c9de4430457d1727af8af", - "gdnAlternativeSignature0": "d2ce914431dc4d3c206c55b66c4b6dca1bb2031972528496dbf84c655535f145" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "44c94e00b2108329c9f76c78147b2604cc20cabbf31e7f8e4299bf4d2fd94f14", - "gdnAlternativeSignature0": "61c01b83b4d60836ee555f3f704f36d40cb7a30c25a0d5dcd09b5f172423448a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 47, - "endLine": 60, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "235d3319b06d373c3b2babd6236b29e3ee46066a92409d48434ecf54474c4f3d", - "gdnAlternativeSignature0": "781e42feb7fc10ca262f5cce84e3a566206e0d9abfe2465ba0a1fb45a98eef9e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3645ea9051ae5673c813896b86a7ba9dd2b3b5e32c23cafc80908b2f2524f944", - "gdnAlternativeSignature0": "3f5299a9cafa29f11de4de69e3fd6545bbed0d8ef6dfdc23e989ccbd39b25cdd" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ca7a9cc04d61e4c2746e08d814a13bfce97322ab3330c23ac6a3a046e1da28cb", - "gdnAlternativeSignature0": "7223771f7f45cc448238ef3189a578bb9838dc9dcd6954b1769a77b8828299a3" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "71dc14fcd10f9c9553a4da8ac502b6435b2ed0db62277cd987cca6d551c6d181", - "gdnAlternativeSignature0": "d43fc88acb8d338e40410db7c591108b80e3d46c28060e62437919f14f722dfe" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_14", - "ruleIndex": 28, - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3c0093298905723cba273ae4a7665878ab158eed129e338a933ea628f12cea27", - "gdnAlternativeSignature0": "3e6753aa3addf5ba0305dc624bf944236f65feb3fb342a87052ac03331c29086" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d1a5aac95999bad7e7b5c9e0e255d9d2ca11a7cea6fa5dc385222d4feaf488c3", - "gdnAlternativeSignature0": "33369208b079f75cbe0cab69fc1bf20ac7c43ea8f2b7536e1b4f13a771190cb6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d73af49617110ba351d090184b05165dc8855332c51a8857ed25280cf9afad44", - "gdnAlternativeSignature0": "09a999b4a9287158b3cca86c7e300c895a1350f7ef5816c5b55efc9f3ea7ce55" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "03753151129aae06d95a6bf05714f3962691cc58c210d7d9e1834d8fd0ccfbf9", - "gdnAlternativeSignature0": "ee019a6f911abaec8264daa5ce7e95edb613bb130a37c20e98d07b6ce408aa71" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b8b0b37b159d4ac33da5cc0f6c8e629f574c8e2989630facb7ed998b4fbd3e2b", - "gdnAlternativeSignature0": "c33ac75db46382ebd17cb142c0ac1e4ad6899e74b3a7aba6452e31cff4a58b91" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_153", - "ruleIndex": 33, - "level": "note", - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e964440f1574351b358e9ad24c3716799d68e1fff901003018bb171fe1731425", - "gdnAlternativeSignature0": "d665f8f5eb905b17e8a1114333ea0f968495576f17e39f7aa042a5e10d58f6dd" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "46bcafa5ef7682f49218b2cdae86203af9bd2d6676b1305517ff82ed2534c8a4", - "gdnAlternativeSignature0": "3aa93e2f42f94795cf57ca2abb96bbaf98d85c457a25083f5dc76fd25f6c828d" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_70", - "ruleIndex": 35, - "message": { - "text": "Ensure that Function apps is only accessible over HTTPS" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 61, - "endLine": 73, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8897f68b7d0cfd331d551cfe62263a1a37c12f1b2fa6cc72ced0fb70f1d07bea", - "gdnAlternativeSignature0": "1e32293b35ee682f0207bfc6b97b03f98947a3c42b123d602986180ab02349dd" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e19ab0fdf6b41c60416ec1789e435d14b225c9059e0cb0e72b85621eaa9a6a86", - "gdnAlternativeSignature0": "9c76d0ac3067ddae324af883867178b01d2394b6293865422cfd1c2a0d8f2322" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "9ccd2eec805423da588e0e4fb39a20af8824e7b43e64c3e4b69ef404665c32cc", - "gdnAlternativeSignature0": "f564ab2518131f06da69f86eb0b8f61753018f7a7779cfb057eddd15551977ee" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "9359461703908a57a96cb32c9d317be3fe28b28f0bf8392611a279a831e3c47d", - "gdnAlternativeSignature0": "4e87df39b2999630b9a10e9ecb92008f17baf6e351e0b208887f85e9931397af" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_14", - "ruleIndex": 28, - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "52979741c27ad8788e0a869cb9c45f1dd101ad482c8c5157b8812f78991baf31", - "gdnAlternativeSignature0": "5058e736e60a56f62767c611d9a6aa94a5845c6ee0c4aa10fb6626a2aa2a70bf" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "31f0c7f30ad4509ee75d1a179af6e021694beb27d3a38ff0b5bcf0fd5d9bfec0", - "gdnAlternativeSignature0": "1129123e54ead9a5143b8feca94087caf7d35b5ae8251eafbe5b7cc169c2321e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0768d45c77ded4c1385d2feae675b811f3c643696bda9156a8bfccd599aff704", - "gdnAlternativeSignature0": "a60f942f7a1141f3ee5bceb13b7991b3a42f1f9d9b467e82a0d3f2b9ba339d7c" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6401e6c3c7b71999c46c38b67aac9488aa986bc7b294124d85576bda8a4c0a9a", - "gdnAlternativeSignature0": "4dfd2fb00d7468e4b5ec7f9be6f463d7ce9654014d94abd3d367315c073cb7bb" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e32c0ff998fcef5e2c8dc7044fad00fee70e804ea115377e31bb12a7f59a2c10", - "gdnAlternativeSignature0": "439c05a50fad4d75ec8d278a58719987c9009948631a2f11782251d73c8f4200" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_153", - "ruleIndex": 33, - "level": "note", - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a5c04b13d6677bc41f4b3715f1e01ce95036675d6de4df8dc0439de074a03d73", - "gdnAlternativeSignature0": "49b98e30d0dd7ce696a9b0b481e3281fd671f0fd70de26dd6fb7510b705e0f87" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e6301a23f58d4b667b0f9399522c3093c2289a6533a91c5cdcf22d245d526fa9", - "gdnAlternativeSignature0": "6091c16fd0d417e61913fb8a205a0381ceb3beab6ee091ecdef880751180c796" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_70", - "ruleIndex": 35, - "message": { - "text": "Ensure that Function apps is only accessible over HTTPS" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 74, - "endLine": 87, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp,linux\",\n \"name\": \"FunctionApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0f2797770c6de1cf13dc915a59ed4540e23f915d86778fdb7e6b17a631c027fa", - "gdnAlternativeSignature0": "7f942a785576efce2530b9d5e5138b37b95f67caaaf4f068e03450ac35bc9b27" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8a917df4e8c678506a78db5559479538775f940b8c14aa146822e79c821a287e", - "gdnAlternativeSignature0": "536e095efb39b0a59bf24cda2950cc9d7a939e228a53bf159bf8cd0fb1b8da6e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "012a7cc9d1337a51d3b90f52d7866f9183fa75c796a6b444a5792554db38986e", - "gdnAlternativeSignature0": "a0b181e8b990ea5f29718c2f802c61d0e3d160d0fea9394c335ce70582b89c12" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "07162ca3946165f6599af87465895d93e3a4672e03b47519894ce51b2c5cc9bd", - "gdnAlternativeSignature0": "74128bc18a6b19a6678aa9cbd0384858bae0f0270c9c36bed84570fd26bfbaca" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a69519b3c5b094db4ab476ddeb127f3a19304d1a0dceaaa0990a3438551fa084", - "gdnAlternativeSignature0": "be3bd3018a1fbfa842ccab409f12e02b083c1afa3058a47b84c9fff1d4da82b3" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "736147b83648552df9d3f8b2b392ab48a9bf9ab58620fb7a3818f40f07bbfc61", - "gdnAlternativeSignature0": "f27f79c0de60ef21728d0f1fc1410e6a2cea4efb033b119007e4e6ab0626384a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a4b11836c10056ef0cdbbdb6afefc25c31906aaa79ca3bdbc1bdb18ed2d011b7", - "gdnAlternativeSignature0": "07b6c44a303c8c16e35003d2412596b49a13368ed484ff649ffa144d2b5771f8" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ac27e5bae66e1875d76fc175bccaf11c9f0a10e45ac8385025f5dde90c69e4fb", - "gdnAlternativeSignature0": "9a5fdc81a5d1e596583d75f0a2da7b28dd82d0c9155a0baf2b57fd32cf33e21d" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 88, - "endLine": 101, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d06586685b31f55d3be4de5084e13b421c700e4c87a7437bffe41291bf65d35b", - "gdnAlternativeSignature0": "dcb4c97d8e1a7a14524f2c6534312187b0272594e87ed98fe98e172b43b7dff9" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0207769333b739f934c6c2472c0f269001ca3b1e973fe1cce2d2472d8bc56399", - "gdnAlternativeSignature0": "c00d75f61dc4dfb1d452c9fa66da4eea57d1fd4fada176b36c66d53d5b9b1cb0" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5d7890ffeb37cacd07b107299144f625fa773f8e8fb28a62a7c0dea16025a0ee", - "gdnAlternativeSignature0": "089e7957dda409a966a18b7e294c4573d4ec68ad4e24b23938e321a8e82b030d" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c29ee62ee70b4965bc0d67ef69030f6b70e617d9fcd3ac8200b68198192b7dc1", - "gdnAlternativeSignature0": "b4ba90b3e3e84e928c796f1c7ebb2e6797dea862b0e44cef5199dbe9bfe5e6aa" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_14", - "ruleIndex": 28, - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ee8d746b0689b235f3208999ce7df1653fa45d2711d41a5d6e7ba90fd50c388c", - "gdnAlternativeSignature0": "ea3506129a69e11e59a2393a8e401e15c35473f82a143746cf124f0aceb0f013" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "7b591f479f9723771411f3c953ee4ab3beda88856ae3ab53a88a64d61214e01d", - "gdnAlternativeSignature0": "0ac09190fd50243610f0dc1cefc1f8f5bf5340c6554465ca85333d39814c3094" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "f3eef46d00d6fe15f0b4da40bda2f0ab16e3c91fcec06df3545e1c7fe7d8ff6d", - "gdnAlternativeSignature0": "6d2fd3507d1f39e715802fc90b4f9779fa4a250936446362dfb3a3c8675b600b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "52578dfe3657d9104db6fe82cafe200490942428064ed7a2ba755d780c8365a3", - "gdnAlternativeSignature0": "c54bc8f04da699379969061f974819212592deb962782dc7070937bd22ff33c6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a5c0013a8fd9f0097d45e444d88054036ed8f442fe330587d0eae9d3714ec87c", - "gdnAlternativeSignature0": "10e9ab48939200696e4f91afa907175cea23e4c9aea63613a7ca13160da48477" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_153", - "ruleIndex": 33, - "level": "note", - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c8b8eb4f5fd69ed893f62f660e2df19dc69d3bb4a56c750d27505476f1cfd972", - "gdnAlternativeSignature0": "3bc11347887444d838588cd7d6dfcda7b36d4137a8406b9064dff980db330958" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "7a9df9fe206d31f6b88c7461cbbedd1791b9500136c048ba6c5a1a70390f97ac", - "gdnAlternativeSignature0": "69eb37c4e01e77eaee89dd5c5159883cb79b66d176282e18fcb6e2926e00a887" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_70", - "ruleIndex": 35, - "message": { - "text": "Ensure that Function apps is only accessible over HTTPS" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 102, - "endLine": 114, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app,linux\",\n \"name\": \"WebAppNoHttps\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "67339bfd8472664e8e86c3dc86cc304bf7b9e1d32dfd00de0e6fbaeb2beea398", - "gdnAlternativeSignature0": "3e179c62893671bd328f85697d3c649cb45309fb6525b65b76a9aa5a649adc58" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4f72f1e4d99a8378d05b185cebfa8825d333a71cd5ef5b0bf186468bdbefea17", - "gdnAlternativeSignature0": "c78d4a844035a2ce61548f8d60784616fb2403fb930f340915bc4bfcc468716f" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "cd8cdaf3f142d7a4565c43f60507169bdc01b2286039cf1255d61c596ba3101f", - "gdnAlternativeSignature0": "bd5c17ecc6b46d6513f8eca0a60db649dd30c9a1eb735d04fbc624a1b4b79f51" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "f7e0232cacef22c54066a9584d66a2197c9c74c0e03f83d8eb7c4e0134444dd0", - "gdnAlternativeSignature0": "7d8d600175945563c3b4bfe9e2c144473bb0a6ad19b8148501a70cdc8061004d" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_14", - "ruleIndex": 28, - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ae3081631a2091bd9163df6f1ee42da0cb7854cc3bd2d6b398ceb8157d47c295", - "gdnAlternativeSignature0": "89901158631e4d71235d05997a134e895b4ccc2e4ef71ec40ae5b8fe13439adb" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "255f7613ce31bf6755bb2ecceee14742ed8cff561d5a310b501ef17b7a9297d3", - "gdnAlternativeSignature0": "b96b4d533c88a6a70fd777af318af980187826608f3a569474d34858e2dfe22c" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0b1fb05ccebfce0be5690acbadf661f9c2c441ad9d90eadd8db28efb2a5c574a", - "gdnAlternativeSignature0": "8835bcf442c7e87a2f87b12a5afeea783d9963935c9e670d58841eeee02b0fe0" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2b2824300dc3a47d41fe35895bcc7cad5518ad6adbeecd51da01c157e6c984c0", - "gdnAlternativeSignature0": "7649a4df3f008f0807c7e8a88a1a2dc1343615f1b82341ecdf1c0191b0c59f89" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "91cfeca6c81755244c63c7c01d9c91da11cbcf416444876ee05b92c981358129", - "gdnAlternativeSignature0": "c4fc2fbf31a7a52d60660e3c94d040c2abb912507c60f5b084cb0d8eae09c334" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_153", - "ruleIndex": 33, - "level": "note", - "message": { - "text": "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service Slot" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "12fdacf2d5aa3a1ee71cd5c41c350cfba3e252cd7cbe4d579fed9cb23ab77388", - "gdnAlternativeSignature0": "26f89be242e3ae985b8f53b8ee3c67bead4ae71123378bf3dee7dc2d8263e6d5" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "54ced1e9e7299882c21663f45e6f27de7eb21223e77344e814efcd89b8eed40a", - "gdnAlternativeSignature0": "33f9e2f7e0ddc2d74628ef9e4d55b535c2ddb446f42351f4ba30926022834e1a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_70", - "ruleIndex": 35, - "message": { - "text": "Ensure that Function apps is only accessible over HTTPS" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 115, - "endLine": 127, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_HttpsFalse\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": false\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "288291d4755ccee48c5333b7d63f37c8e0706cf06714117660c84507a289ebc0", - "gdnAlternativeSignature0": "f5a45e007c2154338b28247a0731d64cd6c700e52b86171ebfab23be42290c88" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "08008df0f45ad8f64f4f9486ca9d1d20053d77608610e41a01834c68b1cc3a09", - "gdnAlternativeSignature0": "7f7317b8b44e4c8b52c5da46edf6e775d451d85bc442fbf7d98ac9b0ecdf9b1a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a2fcaa633cbc5e8689c4c980a1b76805afde139435d0d92003f29dbc1adb25d2", - "gdnAlternativeSignature0": "02fc484de3908421bf498b943a3501de05c15edb775e37cb7257ad89b4e1ea9b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "bf112ad4ff0c1f3dbda36d12b6d980c83e9448f3cea1fab2124874b363e2ac33", - "gdnAlternativeSignature0": "ddde80d77251f9ab96e8180febb6470c41bad8598a1751ecf3205201e04032d6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "adf211c29e22998dc5e478d0f175052925f2426ac7b3c077d75a3d438bf01a71", - "gdnAlternativeSignature0": "4b079ac338cf1fa4d757290e73fb9b247e37983f28925e314c9d61debdca8a29" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "12cdce98f0d5aa4daf69793f9157ef1dd1dfd49ee3ef333f6d57ae4bad06477b", - "gdnAlternativeSignature0": "9ddb0eff72c95cda99852373789fe69e358faf69c9dd5a9fb90d415e957f67f3" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a01b7d7f752ca024104d998b10919091141c26bbd9d73412d05e9b7df9363f16", - "gdnAlternativeSignature0": "7df932c190b3ca666346a7f0465861d381530f106e254076b900a704cda36707" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "92ce11dc062621efb46ffb9f89dfd1b9c12dded0b94b04f3b0595f9420c0511a", - "gdnAlternativeSignature0": "7305d34c7a68da6a3a1c357d001602242c92f53fa425b0b2b7cac8681b8720c0" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 128, - "endLine": 141, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_HttpsTrue\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\"\n ],\n \"properties\": {\n \"serverFarmId\": \"[resourceId('Microsoft.Web/serverfarms', 'serverFarm')]\",\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "66c95de9831d167d19805f23da1ed5e5e7187f6b898fd2c379923be413ebaed2", - "gdnAlternativeSignature0": "493078216959af69095559e71e4e85917a9850e8ea90fd13cae9e6e6378b602b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "dae96b9aa7c9155ac642e29b28b9fd890e6e1c9fa6412927d964aa22c026cce4", - "gdnAlternativeSignature0": "9b8c72895461d25ee31a8c5b1b5bdda37dc3c8b032c14663c43538cf838a7484" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0e15991fa47fbdff46caf4d88b4a2be08fb7309de35a5105df283f2cd3ef226f", - "gdnAlternativeSignature0": "b0664718fd5e7c63444d10f083239952773ba1744dbab54ba4179b90213c162b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "f413c46ab964537bc7ae7dfcb7b2ffcef2f4859e417da785d2d693af373bbc72", - "gdnAlternativeSignature0": "90284155b27b35d9aa40959078e6e324d16c9ad43994fb20812c5b87ab3da8fe" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "7720ed6c8b5f7baf81abe1e027fc660ee7213935e400dae0edcfd3446026ac34", - "gdnAlternativeSignature0": "19a91cb4fc78c2570287be6094778b7b3129af76dc046331d5a63c1adc857126" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "31eeaa41cb930b4da2b791b254b14fe3f8abf8fb09a3f4592f23195d8b3d32ac", - "gdnAlternativeSignature0": "852e81a1be9daed64c584b0a57f48572b61750d22f9558ca1b3cc6dabca55ddf" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d41a5fe9e3ea723beaa68eaf7d4285ce450d5356436fcf3239c57109fab2e3f3", - "gdnAlternativeSignature0": "2a0982291cb4f11f796e6292520001696fa061f3549a3b4b6874c119b0dc8939" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0ae6105850f99ccaf95cf627c1995f3d7919bd31dc28f2945bcbf79f503fe118", - "gdnAlternativeSignature0": "72596372d73524682d0e517eb7a46d563f86d56830f27c9461e4d32ba4a781f2" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 142, - "endLine": 158, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "7681cb3edcfb2659c2d32f2e7974f467446481d6b8f2b69ebae4df96958593db", - "gdnAlternativeSignature0": "a0af6fe2a04fe4a682850851dd4abb0ce07f4da5c8164df732c5042397d0f297" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4df01be86e5e4147c31aff5bef0d87d3e3a0fea9898be19a88df8f6060c6e582", - "gdnAlternativeSignature0": "a3bda844d97fdd08c544d09a457ab85e6f4f33118537eec7ee0be628841a0253" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "7622258391e9aeb5136bfc53200e4631c118a10864775710c6e20c3a1c237687", - "gdnAlternativeSignature0": "481a7ad24af6150821e083f9d4f23bb233fc2533beddacef31c29768569b84d6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ed1a58b3b54cc84e53c7bf600f8fab74e9c11af3031a3f091ec16ba9718b6009", - "gdnAlternativeSignature0": "15c3e8d74638416369e18b9d5385946a799c5d6e07556598bad7ffab2362586e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3182a4a340f4f50c65cd416eee5fbc9ded7a6ef32624b4c7474bc837466e224b", - "gdnAlternativeSignature0": "d8a84c842901bc6695e4d2872e84deb0a7baa06a702117331a93c6feadf5a546" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2c00ee00a620c96918dc4b9545112008c52cc88c30ac68e0b8478f877a16c74f", - "gdnAlternativeSignature0": "0fe0595ae36ffe8d3f8292c1f39136832913fff70668f658520a5a08513d411e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6cdcbc4da68c2815a6d3f0bd78504303b0825e880d14097332cb170407c80128", - "gdnAlternativeSignature0": "1fe507dd25e0339cdc76945f0b74a799aa63dcc0b23cad0285faf8848457d916" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "f3ac1d65124943989cd407b1862791529fab3c4e0eedc65511c90e43edf3404b", - "gdnAlternativeSignature0": "298513397a838c0f2a237b8c715b3e500bcd2a54a0f1f6163bd77768da9dbcca" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 159, - "endLine": 168, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "bd6450c773eed311a8e88ceb126978f5c3988a64c332b8ec936cd28ea7aa295b", - "gdnAlternativeSignature0": "f234d72c8e49b23ce2aa32bda1a3f909baa6ad9b4fbbf65d4211d56cbf976848" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_13", - "ruleIndex": 36, - "message": { - "text": "Ensure App Service Authentication is set on Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 169, - "endLine": 186, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/RestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4764b062aa2d198673df7c5f6d2c0e9c01286e83909a609e2372747b782c1ab7", - "gdnAlternativeSignature0": "528e2e7673c1c9c45f2e03b442261956045574d01f03c9004d1d2bef6c09bea0" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_65", - "ruleIndex": 37, - "level": "note", - "message": { - "text": "Ensure that App service enables detailed error messages" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 169, - "endLine": 186, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/RestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b7a5a598012ddeb8be2fd66b67a61ffcdc2b743ee4bdec4d88ff3ec3b35747b5", - "gdnAlternativeSignature0": "1cef927286d0a4b4ea6cf8c1b9c129beffa0889f29a77ce4d2b443c9d83d3669" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_80", - "ruleIndex": 38, - "level": "note", - "message": { - "text": "Ensure that 'Net Framework' version is the latest, if used as a part of the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 169, - "endLine": 186, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/RestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3f0e499426cfa4fe5bdb96a9894cb77d781ce58b0d391af6741cbbb23829d7c9", - "gdnAlternativeSignature0": "e237636bdd6a09fe032da4732bf99d542b96c319747ff963d56185e4298297e4" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_66", - "ruleIndex": 39, - "level": "note", - "message": { - "text": "Ensure that App service enables failed request tracing" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 169, - "endLine": 186, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/RestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ecda7f10c676f4fb72429cae500da0aa6d3a253664f489dd99c37140cc4adbdd", - "gdnAlternativeSignature0": "456c9b0bbba231ac9f76aeb80c65a8f2e4f2395fb0cbc94b7aa38df4ba418c4f" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_63", - "ruleIndex": 40, - "level": "note", - "message": { - "text": "Ensure that App service enables HTTP logging" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 169, - "endLine": 186, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/RestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0debc8b4bed8e61fddb6166094e0f30c9823a2a4bc6e21990d839ce42bbe7faf", - "gdnAlternativeSignature0": "119c560594404bbb1f85c2a4a64be3ba7f355cd183dcc2efa31fd0bc552f5ad5" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 169, - "endLine": 186, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/RestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5c6cabb001d3b83d407085e00b359528524e83c168685191c6a9258098a9d366", - "gdnAlternativeSignature0": "3664e4a542b0f7100774ae71190236958f7e845d9a1d5079ba8b85ebf4ecfd1d" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_88", - "ruleIndex": 41, - "level": "note", - "message": { - "text": "Ensure that app services use Azure Files" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 169, - "endLine": 186, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/RestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6f0de3fde830219d08c3c4083fce6ccd9966859a6cd4143ec03fe04798472dee", - "gdnAlternativeSignature0": "6d432a9079e10e7a68453f21043b67398f44a7095b3b4337ad2a97b879c6adc4" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "dcd550d66ea4f3ffc89adf365fe7cd5bf8d63462acecd1cccb341a7335e9fbc6", - "gdnAlternativeSignature0": "5831a40aa6b645473d2ba22c02586e6993565e63f22aec548d3af0fa06b9fd22" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6b0c6b35c56eaa234e454e964d8305e90681f1bb57225e8b47b7d84d4bd9c52f", - "gdnAlternativeSignature0": "25b4881300990c178e3af646a347540425e737d05e462e73ac054f3c73691340" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "9fb5162913194706fc251e38d156beebe0887f9bb6ee8c03671cd6755a952833", - "gdnAlternativeSignature0": "99d41b0689e4d871f9d62281e26dfbfd83c98541d8a02fcc05cae59e1c6e1e61" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e11b584dd233a10ac72c56c29b0abb0a46b40eec0fd60d073902939bb2fa1a7c", - "gdnAlternativeSignature0": "0094ff86f766a290f1f59fd99f98729982d98a5c48687076a1fef7cf652dc8c1" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4750d98ad7c586d98825c46bea9511b196f12dc4264fb696186efd16ab3d94a2", - "gdnAlternativeSignature0": "b8e824ce01520cea292f1a7f62c7bcb8a5f77c5151b5bc056b1412295ff3497a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6618f89bf0f657561f12e6f711f090337aa8f3864239d9b428ecc00ca4e734d8", - "gdnAlternativeSignature0": "020034ec5f28ee65e82f5e48c62ebc00c918318b37fb5530d365b705686fd720" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "257663068ac706eb48e30972d5ef65ae59a81b05935937b81969fbce76b54284", - "gdnAlternativeSignature0": "7162b78e9cff93e85080d9d077dbd2f6d9c5a765d0da6485890b044204b68786" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 187, - "endLine": 204, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"api\",\n \"name\": \"ApiApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2dae51e3df9996d76d22a1ae6f75f332e4bf5bad3f4272e79c26b2b7c999ac94", - "gdnAlternativeSignature0": "8259e76b9c016c2688d043c19e0143fe202feb2d893e108efc9e81c9d35ce4f5" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_13", - "ruleIndex": 36, - "message": { - "text": "Ensure App Service Authentication is set on Azure App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 205, - "endLine": 222, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/UnrestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"*\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0e8af6cde44520ef4bcab2e8711c2dda5a7fd48878091674e03dc681118a266f", - "gdnAlternativeSignature0": "146cabdfaf6092c02eb82036132680f7b6808bdd5432312d46330c9bd3585e72" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_65", - "ruleIndex": 37, - "level": "note", - "message": { - "text": "Ensure that App service enables detailed error messages" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 205, - "endLine": 222, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/UnrestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"*\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ef81362c707d65d3863cae808daf52e6f7ef001be334504a099e1b9ff50e232b", - "gdnAlternativeSignature0": "6b075093c48fdb089dd2354278746e6a51fe721cabb128f140a431cf005127d3" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_80", - "ruleIndex": 38, - "level": "note", - "message": { - "text": "Ensure that 'Net Framework' version is the latest, if used as a part of the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 205, - "endLine": 222, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/UnrestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"*\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0b92428b5f8d657f24f7b113a8e844c8b9f0eb0582cb7ceea6933aa04dafd8ff", - "gdnAlternativeSignature0": "817496a6ee1a0d6d64bb24701e78f9cba8b3fc06268f0bade0689ce46999e311" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_66", - "ruleIndex": 39, - "level": "note", - "message": { - "text": "Ensure that App service enables failed request tracing" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 205, - "endLine": 222, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/UnrestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"*\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3e9ad501881045e5d7309b74094c547164a1dab831701c7dfea6dac72f78ad39", - "gdnAlternativeSignature0": "8d2d4e5f2e7fb6ffe5a3d690ad5075c7a4194060994f0f063c517791e6bd4150" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_63", - "ruleIndex": 40, - "level": "note", - "message": { - "text": "Ensure that App service enables HTTP logging" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 205, - "endLine": 222, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/UnrestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"*\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4f45838aff932f565433ff3fd599bc9de4d7bd434bf4511cee319935dfb28950", - "gdnAlternativeSignature0": "d7afaaf97a739bde64c18fb27889fb4d931b2ba29561244209ab5f03659f4b71" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 205, - "endLine": 222, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/UnrestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"*\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b7d1e0a2597990637571695421255bca16f0c42cb2ec39b943ec7ec98dbf64e8", - "gdnAlternativeSignature0": "1d2b4a62840a1bb054f7389ba3c9215d640240d28ecac6c4dd75531648052b7c" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_88", - "ruleIndex": 41, - "level": "note", - "message": { - "text": "Ensure that app services use Azure Files" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 205, - "endLine": 222, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites/config\",\n \"name\": \"SitesConfig/UnrestrictedCORSAccess_web\",\n \"location\": \"[parameters('location')]\",\n \"dependsOn\": [\n \"ApiApp_NoSitesConfig\",\n \"WebApp_NoSitesConfig\",\n \"FunctionApp_NoSitesConfig\"\n ],\n \"properties\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"*\"\n ]\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "7ac28937c766ad8aca6f80f4c1b203c09f14c9b030a09850ddf94906052d3cff", - "gdnAlternativeSignature0": "86dfaece04781f7765ed78900613f50af845e94a4498b5bfe133a0567832350b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b0458996e102d04b1269a39e6ad2f6d6fa741a778b7821a91a590d13708de75c", - "gdnAlternativeSignature0": "ba1b6ae6482744c512163ee84d83124346fdff55efcc553c93a117827b277dae" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8e375b5a9dc4728fd4ba4a21882993369de923f2eac2ed78cf8491552d8facaa", - "gdnAlternativeSignature0": "853de3499062ba5a2cb953cc5283596ec4b8e90b4b1b51d5d7ed7fd9ef487dd5" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a127ae6cec02825e2f917c367e3db26bf8017f68b1998857a4ebb175353f6a51", - "gdnAlternativeSignature0": "e1bb268111081a04f49ac60c72a63ed0738145f62892adb59d43e0cdb4181ffc" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d351baa7f806ab65257e43138c406148e9c81bb988521fe221f56f7e72e21818", - "gdnAlternativeSignature0": "5c32a35a8c57bffbbce0105bb252897bba594b8d564d6be228aa0d086db40eb6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "34cf1285b6c9b01e2163c4235369bba2bd1740e57a71fa21d4945fee87438740", - "gdnAlternativeSignature0": "e681471f7211744f1008218a8212ef79deecebeb44aaf693cb5c6a2ff0c30fbc" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e25dee6e2c2afa0f90032dee4f66872f8022716e6e1870e697e282669a0e6bea", - "gdnAlternativeSignature0": "44100aca6dd76613f2e92578a73695780785c3777bbba30651b6e632ad118318" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "da1a3fa73f790c28a05f8d7c42c7961059009ddc9e050d309066a52dc28f2964", - "gdnAlternativeSignature0": "b97d0c99d154acece432cd4b25ed97cebdc3307aff68dff7034a8814c9407ae1" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 223, - "endLine": 239, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c469692cea7b7ca010669e9d28957de550b80aca420639dab41c3b50acb673fd", - "gdnAlternativeSignature0": "0280a30c60f721964ef8f0e1f057cc3823c08e2c877a2f35ecc0bf0cba67aa73" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4fd9a062f86a13872b9dad7a38cfe0d953f929b117ea32a74b12949f94482d1e", - "gdnAlternativeSignature0": "4313f93aeba30524fe4b597801c38b1eff909f6cbc2cb6ac96c9fc11b4acd52a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6ee562e5d732261eee58f4880f619fded377b21bb88bcc90f9b5abad30ed6398", - "gdnAlternativeSignature0": "e931b536ca4eb49838ef185e4d0af6477b51badbaa93242a6c4ff1b2e1298bf8" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "143778d4f31ab73a5bb75054896253c16373f69b3e0441b850b0369d1bddf1c4", - "gdnAlternativeSignature0": "03129ed6590e6f4bf2f43e9281e29c697a95962f5878ba90589b6fe88fe04380" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8e4a04fbf2e32b4f92897087315fb87884e9f2888146d04906779b927323f6c4", - "gdnAlternativeSignature0": "f321e049084aef0145e1b33f9c49fbfd5cdeb2ab03c824e93f910fbd3adbbd44" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3c4d99dfa4a6499c4718cdbe5c7c7b5fb1667ca31c16bddbc1f0d50f201b0eec", - "gdnAlternativeSignature0": "fe55bb9000959302368aa07dd560c68c98caed221602cf2a2f997e85d4bdd6f6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "eb4bed8fbe96c4a4bda2d5714d30a37fe5af249e7983c2e529ed2555b2302577", - "gdnAlternativeSignature0": "e5ada8bedea49618df2290b727da8e0b3564bbc4cad4ad22aee82b8a6afd2581" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c7c28aa1f6eb6c981b8dab94041a68bab23a9b72de245b2058c9280c612cac81", - "gdnAlternativeSignature0": "53b134cf1cec93348d12102916d6e3bd4072019df2f026de26b3ef6db3543505" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 240, - "endLine": 255, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"name\": \"WebApp_NoKind_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "23dc3709b4a9455bce85771532889a8fb832f85ad458d4f285fe9fb2eb7807ae", - "gdnAlternativeSignature0": "7fa5336cda8b5f2d8c41c95ffc7b95cd3ace43f023bd72dfbf479b585045984b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4e8cc7e2a4b582670ab605572ebf449e0b91a874db1ab9ef2e8e1202651fb74b", - "gdnAlternativeSignature0": "7fca34e471d5ec9837849486a2e299f93b5ee49824bfbaf0059ddade380c33c5" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "32f70b90c9398c6ab47faf4896ca724cc667414fbdd61f9d37ab64180039afa9", - "gdnAlternativeSignature0": "74ac563e5ceaa5a0e09e489b9fa579c8be0ec02947aed0617c6faddf0243f60c" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "f565d488d0ee136a77ca481e53820893ff8ba50cacc9abe76f757c1a84865639", - "gdnAlternativeSignature0": "3fe95bf85179982d04d6323aeb899d481688025a708d5e171859bd82cc1cdd76" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "88848a38797205708edddcc47234828b56efc3e522b2f791a287bd2e1362ca36", - "gdnAlternativeSignature0": "0c58696e023f4bb523561bf26c5c02916c2411086a669f73ace694130370fd8b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "df545930bb8458e5f4d491540b41367072287dd41d4cc413670e820d053b3c59", - "gdnAlternativeSignature0": "b156e3e128bdada88119590b89491ba5637d828eb52a1fd7cb66709f886f4015" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a4a0f2a5361ad3d1b9eae1eee98796fd4b9cb48e588e5ff7e991e03b1fd406f6", - "gdnAlternativeSignature0": "273d89c35b3928f35b2b753a0285d00ac263f3183a4a1488c2f7a79177415611" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "9719571b68e5b3a885fa6fd8f779399b128f621f8c56f6fab08675f5bab6bad4", - "gdnAlternativeSignature0": "e0db6d935221b8f1a1c87a50acb333285eeab2b10c099cfc8c0e1842c3a55397" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 256, - "endLine": 273, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c99607a3189b9bc5ab42ae5c2d7df75995a965340270a602883d37aeed0923d9", - "gdnAlternativeSignature0": "95c9c99f71bb81429ee8ccb9608f889801fd91e170be93bd519d0c4aa56ab001" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ab35f47a9966e5318d1be863b2ec0f17d58d977c71e868ebff0db5b49b68af09", - "gdnAlternativeSignature0": "9c833adad252c499f44a089a84712b55a0a006a47a9aba6c530d01a1bc94f558" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "602fdea611aaa3d22235f4e83203311f5ffe8e9e22833d6184d440e68f024bf5", - "gdnAlternativeSignature0": "5c17ba6738b8da7c912c45aebf73499fbce4dc65cf6407b66f5a4c274679619c" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "20d3cf16494d80ca62033400936f0feb57be0a8ab23b9cc9b162b2247897388e", - "gdnAlternativeSignature0": "cd1e03ef8f9614165ea4d7a0973270f4bbab817cb3cc52ad0cf36cb647c3b6d9" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "78cc949070f476ebc74b2dda7f5c41ae43b805ea3b7daf49d3078c058f79e5a1", - "gdnAlternativeSignature0": "a55b806fb31cb3bf0e6b5c3d01f6ffd490c3b886bb80a722866cc000586f0f8a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "15dc91ac7c730ca245aabad00f156494d8a29ee9b357748967d76f05344eda7f", - "gdnAlternativeSignature0": "5847938307c50605c0610b36925ab50eefcc6634ed7e565f480bda53984af466" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ea83d615bfff6cce7ff0679906857f978db31bcc20aaf8362758e87312e2d941", - "gdnAlternativeSignature0": "c039abcf9cf88918b4dd9167119722b1a5108ce5e38d75043c0b750fd2981ec3" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b051219a1626ff89706e92b04e25fb0932c1d985ea27f8aecbf7f743c04dc659", - "gdnAlternativeSignature0": "cabf626aade2ee1d730330c88d2aa7d8895dd4c93938eeb77c63f03a052c7679" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 274, - "endLine": 283, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"app\",\n \"name\": \"WebApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "6b115656ed41348ad449cf25c954970b70b571ade5d19e1105620b4e7717e2fe", - "gdnAlternativeSignature0": "aa6295e81aa6a4ae09a54ac0c497bf6291b50dd88a72b6e830bd210a1a68aceb" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "14fcb885307e08a2c5ada1206158c9a84bbe32823e0d5c615704e0bafa8aee79", - "gdnAlternativeSignature0": "fb7374d63bc8d8ab31652d206739a312b4868d345ca64543f8d31991d5b46cfa" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5ed1b342d8a0f06c5b0bb3d15388f5670cf2b409bf65da4b74e438912627892a", - "gdnAlternativeSignature0": "9f5a5eef4cfdb3f4462b7862dcba4b5bef7ef10e97449da469e0495432b1aa9c" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "f769bd7202fd3a2674861afca95fa1cfc0664bd0b76619913a2f39f87fd2fe8a", - "gdnAlternativeSignature0": "5992da2c97b93015cd5ff7d85e7dca064eea8c61012c16fd094729db911f95a8" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5bcdda995b38f39e6e644a8b926bad1dda898d677c7095948d0787f601427d9b", - "gdnAlternativeSignature0": "13ea3d62cdfd23a6f501449536936b1c6db8485e0dff7c99196f039ded5323e3" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "01e261f9a2c80b85eac147e32d3398613aacdcc9aba1ad95bed4948b25697177", - "gdnAlternativeSignature0": "06726daca90067656a51c927384b29c2fa63923f996cd8820a67beaac37a348a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "72e5f5c502813f23c86f6954f698b653e4357d2982e8bb56333ee89c838fcfad", - "gdnAlternativeSignature0": "faae5ac2227e01f41f73eda05f5b00d5de49e9d7c812dc34cbea14b50d6e9f77" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "64bf46cc4a2b1a39786b447ec202c2052f0686c787af51cc562ff3cb2baeadac", - "gdnAlternativeSignature0": "370f91c60e0d1be39570b5bc47e69e0826dd5835293145c9f61e8fcecac252b6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 284, - "endLine": 300, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_RestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4040613d3dfb88a05ae97881bd42b488d51f9ab73eb119fc0554a783ca548f34", - "gdnAlternativeSignature0": "aa1ce087240db3e7202e98fb6d5d42a12644148791a444e2923686fe70b74fc5" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "7aa4dc87ad0a059efeb183e063efb6d08c9643371ef92a08431c2d59dbe6d7d8", - "gdnAlternativeSignature0": "efa930281f9711759aae2cbd30e4f5f6dfa8935213ef9d897b14266716dc3a50" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e921d31bfff9b531e071fb26ffdf65b6ad8bbe22352067af06b35cb28e82fb8c", - "gdnAlternativeSignature0": "17ce881dab034d29e0a5df168f9e1c52bdd2c5c975e4fdda06fa613e032d79bf" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a1c9a388790b96bca8c54d7e1af1d480c30c7478de1cdf397833aefa8611697a", - "gdnAlternativeSignature0": "6550ec87cae9d511c54e0c344a4cdd2c0de2f8cd89c915cdadf98545ba96361f" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5f201d5437de55fd80955e36fa892fc93cfb30c65f554f53a86a4df19d91f7a6", - "gdnAlternativeSignature0": "24eb4b238d28c37f127ee733bc68e96c101b7c274ec58a964bb4ef8f105a07ad" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "eb2f9b348b74a7a30a2ccf85154a95c66cbbd999d7f0db1a5515dc50033cd858", - "gdnAlternativeSignature0": "ee26900668b5c483ff485bc262a9dda64ddedd4b96a4e5f956797fc4e31c4d1e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "752975926aaa31a34fc4bb75edb0955f8b1b2b7cdd24081443961bd594ef684e", - "gdnAlternativeSignature0": "55cf1c6618e5e7a3be0f2feae175940eb5ec736d17afe66f0895df6997ab216e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "26f0a1a83d914e1d5687c693993f2e09d71b52f756d23a5682778f4a2085ff5e", - "gdnAlternativeSignature0": "5a8642e916010fccd880c8c0e73e83f149b9d44c76710d50d541dbd4d3c7d72a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 301, - "endLine": 318, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_UnrestrictedCORSAccess_EmbeddedSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true,\n \"siteConfig\": {\n \"cors\": {\n \"allowedOrigins\": [\n \"someIP\",\n \"*\"\n ]\n }\n }\n }\n },\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "5add9273a9903c496a94184e7c5ae16aa8c53067ea2f4eecb7f6cc9876a3145b", - "gdnAlternativeSignature0": "a1cb801109625b1081f5c08bf8d85057df4fa338901c035a3c779845e8d973ca" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_17", - "ruleIndex": 25, - "message": { - "text": "Ensure the web app has 'Client Certificates (Incoming client certificates)' set" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "3f3a80ee3024489c35085570b02f970bafaaf551396274635a00a4b79ac0eae2", - "gdnAlternativeSignature0": "917035e9a96adddabea41ba383c24edf46e1ec39e5e02fc8be4ddbeb01bc5b4e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_78", - "ruleIndex": 26, - "message": { - "text": "Ensure FTP deployments are disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8493bbcbf96e7e1068c6296f576527e894ef7245800bcd2116bc07e72079b43d", - "gdnAlternativeSignature0": "755512e4b82449a08f44e7eaebdb946e29314098160f8e1f39b9870f19dc43f7" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_18", - "ruleIndex": 27, - "message": { - "text": "Ensure that 'HTTP Version' is the latest if used to run the web app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d15577475858e078e31e0a5a4560d3e46f04f5c0af458457d9c46525545d0add", - "gdnAlternativeSignature0": "34f10bce44f8b65d8306df6efd6b8a73666704ebe97281cf5bb20c27415a99c5" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_16", - "ruleIndex": 29, - "message": { - "text": "Ensure that Register with Azure Active Directory is enabled on App Service" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "21939dfe4c3d1e60adaaab7f75ef7a0b94b4377b2a2950f7670c9afd424de80f", - "gdnAlternativeSignature0": "0054487d755a01b77de504fc5157faecd817a0ecae44d1e4aa91fd1a46e0be8b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_71", - "ruleIndex": 30, - "level": "note", - "message": { - "text": "Ensure that Managed identity provider is enabled for web apps" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "47829e0ae136b8b26a4176f73de53c99044bba251a284cf106812e2600802361", - "gdnAlternativeSignature0": "0a441e95438cb23ebbfb5ba5d34958a96417b8b4a440e329223843a090eab255" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_15", - "ruleIndex": 31, - "message": { - "text": "Ensure web app is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0563668dc60ab562ba7553e4059ccb2600d2aee6505b99813a4d2edad7d7cab8", - "gdnAlternativeSignature0": "71db655b881a2f569a03dc4a80cf4f01371a09611c4f9780e1ac3eba24b4e2f8" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_222", - "ruleIndex": 32, - "message": { - "text": "Ensure that Azure Web App public network access is disabled" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "4f0638c743b2d1843ef77d55d462748d273aee152bbb618ceaa5a51f793330fc", - "gdnAlternativeSignature0": "e4025758f0b783be95138fa9820d245cb4c1db229e4353bd0a20f699ee9e616a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_67", - "ruleIndex": 34, - "message": { - "text": "Ensure that 'HTTP Version' is the latest, if used to run the Function app" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/insecure_arm.json" - }, - "region": { - "startLine": 319, - "endLine": 328, - "snippet": { - "text": " {\n \"apiVersion\": \"2019-08-01\",\n \"type\": \"Microsoft.Web/sites\",\n \"kind\": \"functionapp\",\n \"name\": \"FunctionApp_NoSitesConfig\",\n \"location\": \"[parameters('location')]\",\n \"properties\": {\n \"httpsOnly\": true\n }\n }\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "ca846feb1e4469b819a99002b3cec670caeab114d5f1c85c81ed5830745e8c77", - "gdnAlternativeSignature0": "962bde0a801111d2d1cefcb5d5bb60e8c031e3c31f4e4527dc4ab9fb05b23991" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_25", - "ruleIndex": 10, - "level": "note", - "message": { - "text": "Minimize the admission of containers with added capability" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a73a10ebaed1bcca5045392cd0e24ff11412eeae435f8a66bd90d64a40f09958", - "gdnAlternativeSignature0": "681910e9bced9366623d2dd9e93781a0f1a15f148fa6523a13e9a0058492d662" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_20", - "ruleIndex": 11, - "message": { - "text": "Containers should not run with allowPrivilegeEscalation" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "692c38842f145cb9c1ee25b48643b65a41b423830a016a149dc12712814602af", - "gdnAlternativeSignature0": "970e4c551a08367b382ed411f7ccca59e201e8e34e32717929767a568630af1e" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_21", - "ruleIndex": 12, - "level": "note", - "message": { - "text": "The default namespace should not be used" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "0378eb8b121cbc93bfe8a78bc75aeb8987f5e31d530ec0adf577e7cf03ee717d", - "gdnAlternativeSignature0": "fffebb6a7b3891cdd01cc402002cc3f81150a080fef1ea60dfe1e2f6f69eb601" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_28", - "ruleIndex": 13, - "level": "note", - "message": { - "text": "Minimize the admission of containers with the NET_RAW capability" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "70f66e4afeb0264ac95ebee5085b90a23e7a9a54107832fb271bd1f1aa298522", - "gdnAlternativeSignature0": "0f11dc2ccb44a7216c78769edb10df097f8a1fc681f56dfdfa93d7c2b802f9b7" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_43", - "ruleIndex": 14, - "level": "note", - "message": { - "text": "Image should use digest" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d54de20ef46f5565573d6411cce2a3baaf5b309cdc9e73bd2e0948dd98363d46", - "gdnAlternativeSignature0": "4e3e18739d7eee6105acea4f65c463484b66649a68e864755fc68c7030743359" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_8", - "ruleIndex": 15, - "level": "note", - "message": { - "text": "Liveness Probe Should be Configured" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2e737794eca54186f3ad53c5fe3a9a7e34d4e9617064e5f844c785e4082877e3", - "gdnAlternativeSignature0": "1efa25c9281d5272c63c1bd7aa7a55673eeb2696c3df5d258c0291cb3d22eff6" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_37", - "ruleIndex": 16, - "level": "note", - "message": { - "text": "Minimize the admission of containers with capabilities assigned" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "09e2a7056dc2cd2bb024c4a44a0111d6f1e72d32b6b4a76c8f02e14817384044", - "gdnAlternativeSignature0": "d3bc404f8a377bc479f16b07ba7be8098c6d7a10735ee0bc15be7f48c5f089f4" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_29", - "ruleIndex": 17, - "level": "note", - "message": { - "text": "Apply security context to your pods and containers" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2fb25b212960e5a8760b6523d4a4c2b47c5a0482186059c1d23c41446faf72b7", - "gdnAlternativeSignature0": "85a6f0ad55f847141b45ccf6ef907b5a180d07d0d29cac32bcc1829e8eb8b2ea" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_22", - "ruleIndex": 18, - "level": "note", - "message": { - "text": "Use read-only filesystem for containers where possible" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "da115818e9e2ffadf20a6b9d1cb1c02e963eb192dae03af8c39e6fce8bcc097e", - "gdnAlternativeSignature0": "d8c06cf85b8fbddd0ad8ba2f86285da2ad52245291c6cab6fd69f3fce31127d7" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_23", - "ruleIndex": 19, - "message": { - "text": "Minimize the admission of root containers" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "c1be36e7556627562c7357f53bacdfea88d2fd6839ffad11f41bff594ddd0f83", - "gdnAlternativeSignature0": "e149f6402cc34c6663973655fb12e532f6c670a7e0374081fae82fd6575dab65" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_40", - "ruleIndex": 20, - "level": "note", - "message": { - "text": "Containers should run as a high UID to avoid host conflict" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "25c05ca1359bd4c2edaee66ac2d7fdc6c213eebfa5d139a0555ed06ea2c573c7", - "gdnAlternativeSignature0": "a73c63b0f812757918d4198fe20e0100bcf3d60c9a9a37f67cd321a74a14f72b" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_31", - "ruleIndex": 21, - "level": "note", - "message": { - "text": "Ensure that the seccomp profile is set to docker/default or runtime/default" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "70378cd13d8568d09ced49362abbf35160b842273ca5ec57bf69c71c378a6321", - "gdnAlternativeSignature0": "3bfe46e3cda430aa1ce3f452e781cdc43697d087821cc36fd104604f940ae43a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_K8S_38", - "ruleIndex": 22, - "level": "note", - "message": { - "text": "Ensure that Service Account Tokens are only mounted where necessary" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "97d3e19a31f43aac84ccd148a5c258209c1727f5900e0b2b1435e8ab08b647b3", - "gdnAlternativeSignature0": "218768ca543d9e02181e0769e8f9a3c7e5e2318fb0742bed2736ea116b7b8f4e" - }, - "attachments": [] - }, - { - "ruleId": "CKV2_K8S_6", - "ruleIndex": 23, - "message": { - "text": "Minimize the admission of pods which lack an associated NetworkPolicy" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/K8s-cassandra-statefulset.yaml" - }, - "region": { - "startLine": 1, - "endLine": 96, - "snippet": { - "text": "apiVersion: \"apps/v1\" # for k8s versions before 1.9.0 use apps/v1beta2 and before 1.8.0 use extensions/v1beta1\nkind: StatefulSet\nmetadata:\n name: cassandra\n labels:\n app: cassandra\nspec:\n serviceName: cassandra\n replicas: 3\n selector:\n matchLabels:\n app: cassandra\n template:\n metadata:\n labels:\n app: cassandra\n spec:\n terminationGracePeriodSeconds: 1800\n containers:\n - name: cassandra\n image: gcr.io/google-samples/cassandra:v14\n imagePullPolicy: Always\n ports:\n - containerPort: 7000\n name: intra-node\n - containerPort: 7001\n name: tls-intra-node\n - containerPort: 7199\n name: jmx\n - containerPort: 9042\n name: cql\n resources:\n limits:\n cpu: \"500m\"\n memory: 1Gi\n requests:\n cpu: \"500m\"\n memory: 1Gi\n securityContext:\n capabilities:\n add:\n - IPC_LOCK\n lifecycle:\n preStop:\n exec:\n command:\n - /bin/sh\n - -c\n - nodetool drain\n env:\n - name: MAX_HEAP_SIZE\n value: 512M\n - name: HEAP_NEWSIZE\n value: 100M\n - name: CASSANDRA_SEEDS\n value: \"cassandra-0.cassandra.default.svc.cluster.local\"\n - name: CASSANDRA_CLUSTER_NAME\n value: \"K8Demo\"\n - name: CASSANDRA_DC\n value: \"DC1-K8Demo\"\n - name: CASSANDRA_RACK\n value: \"Rack1-K8Demo\"\n - name: CASSANDRA_SEED_PROVIDER\n value: io.k8s.cassandra.KubernetesSeedProvider\n - name: POD_IP\n valueFrom:\n fieldRef:\n fieldPath: status.podIP\n readinessProbe:\n exec:\n command:\n - /bin/bash\n - -c\n - /ready-probe.sh\n initialDelaySeconds: 15\n timeoutSeconds: 5\n # These volume mounts are persistent. They are like inline claims,\n # but not exactly because the names need to match exactly one of\n # the stateful pod volumes.\n volumeMounts:\n - name: cassandra-data\n mountPath: /var/lib/cassandra\n # These are converted to volume claims by the controller\n # and mounted at the paths mentioned above.\n # do not use these in production until ssd GCEPersistentDisk or other ssd pd\n volumeClaimTemplates:\n - metadata:\n name: cassandra-data\n annotations:\n volume.beta.kubernetes.io/storage-class: fast\n spec:\n accessModes: [ \"ReadWriteOnce\" ]\n resources:\n requests:\n storage: 1Gi\n---\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "2c09fe5d58637920ffb2f45d2e9e47b6d24f94f9a5a6318e86d5b119d90dc136", - "gdnAlternativeSignature0": "52664a33e0000747d7d55032ea8ff784c99f191108fd7b0a4405b4b4b1787c90" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_59", - "ruleIndex": 0, - "level": "note", - "message": { - "text": "Ensure that Storage accounts disallow public access" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "d7f32900926833945cac2ead4c1ed6c351aabf9fee418b413e3bfe46c8fc54f3", - "gdnAlternativeSignature0": "a3fc19e10564a2494f31f34241b921013aeb4aef2a9ef7ef9731f2fe7fd95ce9" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_33", - "ruleIndex": 1, - "message": { - "text": "Ensure Storage logging is enabled for Queue service for read, write and delete requests" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "641b61a3a4b3c0d759acd57a321d6db4044b47347cd764c152f071e8341faea0", - "gdnAlternativeSignature0": "83a8dfca2a610d8e67e683b30391980c336f3c1722ce80fb2afacb2a01b0a799" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_44", - "ruleIndex": 2, - "message": { - "text": "Ensure Storage Account is using the latest version of TLS encryption" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "9805631179c91f5535eda5e1f2a9d29d2295ca1bec013d3174a1285a9be83d60", - "gdnAlternativeSignature0": "05b8f7f1708c13235397e5a11b0fd243e02f41d8c5085efc269c1d4e73d2b39a" - }, - "attachments": [] - }, - { - "ruleId": "CKV_AZURE_190", - "ruleIndex": 3, - "level": "error", - "message": { - "text": "Ensure that Storage blobs restrict public access" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "a34ff2ab3988f90969a68250eef2b1c1e687c5b58396f198c2615a955b8db206", - "gdnAlternativeSignature0": "9c23a512d4110d27fefdb36bb130958e046b17463df5a7ef245d584c9f943363" - }, - "attachments": [] - }, - { - "ruleId": "CKV2_AZURE_40", - "ruleIndex": 4, - "message": { - "text": "Ensure storage account is not configured with Shared Key authorization" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "e9deaf9a38127ba6222b800492a1b840017f9907c25f9fba842d8c85ae861f33", - "gdnAlternativeSignature0": "bf8101fb23c886bf671ac5c24d8b62f3028e2d9b5e8fc2e106d6789aa8070b76" - }, - "attachments": [] - }, - { - "ruleId": "CKV2_AZURE_47", - "ruleIndex": 5, - "message": { - "text": "Ensure storage account is configured without blob anonymous access" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "1d3bb7599e6f934ddc2badeee51d506051093f4e718c3199d4efd83c6e74ec12", - "gdnAlternativeSignature0": "edfaa2046c397a63e4856e499fc9a7166c96e5bb51978d37f2426cefa1af8457" - }, - "attachments": [] - }, - { - "ruleId": "CKV2_AZURE_33", - "ruleIndex": 6, - "message": { - "text": "Ensure storage account is configured with private endpoint" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "43638c4e55b51ed9f346ab462c059ca30a19b570565c7de7ea7b5daee1d6d9ef", - "gdnAlternativeSignature0": "4b4ba219a803a0d61f213eab7cfdf2792eda5bd536ccf4992d1b5d9342e26ac3" - }, - "attachments": [] - }, - { - "ruleId": "CKV2_AZURE_41", - "ruleIndex": 7, - "message": { - "text": "Ensure storage account is configured with SAS expiration policy" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "761254b9af4e347b1a2a05bc08bd77da3c0a640e44671e27f0fa7cbbc88d2b93", - "gdnAlternativeSignature0": "3358ef85a8762cda2cea0b4f31eebe014dc97571753cefc4394067b9f23cf0f2" - }, - "attachments": [] - }, - { - "ruleId": "CKV2_AZURE_38", - "ruleIndex": 8, - "message": { - "text": "Ensure soft-delete is enabled on Azure storage account" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "8fdd2b7bd19cdd5ce21a89b95330d3a80ecd9efbe30f89c35f0b146a42a65d0f", - "gdnAlternativeSignature0": "e05ba2b227cc43fd36ea7fe66359bf553cdf88de6ff3aa184fc24ed97fa3c3c4" - }, - "attachments": [] - }, - { - "ruleId": "CKV2_AZURE_1", - "ruleIndex": 9, - "level": "error", - "message": { - "text": "Ensure storage for critical data are encrypted with Customer Managed Key" - }, - "locations": [ - { - "physicalLocation": { - "artifactLocation": { - "uri": "samples/IaCMapping/main.tf" - }, - "region": { - "startLine": 19, - "endLine": 29, - "snippet": { - "text": "resource \"azurerm_storage_account\" \"terraformaccount1\" {\n name = \"iacmapping1212\"\n resource_group_name = azurerm_resource_group.resourcegroup.name\n location = \"Central US\"\n account_tier = \"Standard\"\n account_replication_type = \"GRS\"\n\n tags = {\n \"mapping_tag\" = \"6189b638-15a5-42ec-b934-0d2b8e035ce1\"\n }\n}\n" - } - } - } - } - ], - "fingerprints": { - "gdnPrimarySignature": "b6600702be50525edb979f621ecf387776351f02b51dc4d37b822d660e575787", - "gdnAlternativeSignature0": "d1feda229fb89c3ecbaaaf3edec8f23599c5bccd3a725020863ea9b593ae4375" - }, - "attachments": [] - } - ], - "columnKind": "utf16CodeUnits", - "policies": [ - { - "name": "Microsoft", - "version": "2.0.3" - } - ], - "properties": { - "toolInfoId": "checkov>>6>>202411062057" - } - } - ], - "properties": { - "producer": "MicrosoftSecurityDevOps", - "pipelineRunUrl": "Unknown", - "sourcePipelineId": "Unknown" - } -} \ No newline at end of file diff --git a/src/v2/container-mapping.ts b/src/v2/container-mapping.ts deleted file mode 100644 index 11510cd9..00000000 --- a/src/v2/container-mapping.ts +++ /dev/null @@ -1,292 +0,0 @@ -import { IMicrosoftDefenderCLI } from "./defender-interface"; -import * as https from "https"; -import * as core from '@actions/core'; -import * as exec from '@actions/exec'; -import * as os from 'os'; - -const sendReportRetryCount: number = 1; -const GetScanContextURL: string = "https://dfdinfra-afdendpoint-prod-d5fqbucbg7fue0cf.z01.azurefd.net/github/v1/auth-push/GetScanContext?context=authOnly"; -const ContainerMappingURL: string = "https://dfdinfra-afdendpoint-prod-d5fqbucbg7fue0cf.z01.azurefd.net/github/v1/container-mappings"; - -/** - * Represents the tasks for container mapping that are used to fetch Docker images pushed in a job run. - */ -export class ContainerMapping implements IMicrosoftDefenderCLI { - readonly succeedOnError: boolean; - - constructor() { - this.succeedOnError = true; - } - - /** - * Container mapping pre-job commands wrapped in exception handling. - */ - public runPreJob() { - try { - core.info("::group::Microsoft Defender for DevOps container mapping pre-job - https://go.microsoft.com/fwlink/?linkid=2231419"); - this._runPreJob(); - } - catch (error) { - // Log the error - core.info("Error in Container Mapping pre-job: " + error); - } - finally { - // End the collapsible section - core.info("::endgroup::"); - } - } - - - /* - * Set the start time of the job run. - */ - private _runPreJob() { - const startTime = new Date().toISOString(); - core.saveState('PreJobStartTime', startTime); - core.info(`PreJobStartTime: ${startTime}`); - } - - /** - * Placeholder / interface satisfier for main operations - */ - public async runMain() { - // No commands - } - - /** - * Container mapping post-job commands wrapped in exception handling. - */ - public async runPostJob() { - try { - core.info("::group::Microsoft Defender for DevOps container mapping post-job - https://go.microsoft.com/fwlink/?linkid=2231419"); - await this._runPostJob(); - } catch (error) { - // Log the error - core.info("Error in Container Mapping post-job: " + error); - } finally { - // End the collapsible section - core.info("::endgroup::"); - } - } - - /* - * Using the start time, fetch the docker events and docker images in this job run and log the encoded output - * Send the report to Defender for DevOps - */ - private async _runPostJob() { - let startTime = core.getState('PreJobStartTime'); - if (startTime.length <= 0) { - startTime = new Date(new Date().getTime() - 10000).toISOString(); - core.debug(`PreJobStartTime not defined, using now-10secs`); - } - core.info(`PreJobStartTime: ${startTime}`); - - let reportData = { - dockerVersion: "", - dockerEvents: [], - dockerImages: [] - }; - - let bearerToken: string | void = await core.getIDToken() - .then((token) => { return token; }) - .catch((error) => { - throw new Error("Unable to get token: " + error); - }); - - if (!bearerToken) { - throw new Error("Empty OIDC token received"); - } - - // Don't run the container mapping workload if this caller isn't an active customer. - var callerIsOnboarded: boolean = await this.checkCallerIsCustomer(bearerToken, sendReportRetryCount); - if (!callerIsOnboarded) { - core.info("Client is not onboarded to Defender for DevOps. Skipping container mapping workload.") - return; - } - core.info("Client is onboarded for container mapping."); - - // Initialize the commands - let dockerVersionOutput = await exec.getExecOutput('docker --version'); - if (dockerVersionOutput.exitCode != 0) { - core.info(`Unable to get docker version: ${dockerVersionOutput}`); - core.info(`Skipping container mapping since docker not found/available.`); - return; - } - reportData.dockerVersion = dockerVersionOutput.stdout.trim(); - - await this.execCommand(`docker events --since ${startTime} --until ${new Date().toISOString()} --filter event=push --filter type=image --format ID={{.ID}}`, reportData.dockerEvents) - .catch((error) => { - throw new Error("Unable to get docker events: " + error); - }); - - await this.execCommand(`docker images --format CreatedAt={{.CreatedAt}}::Repo={{.Repository}}::Tag={{.Tag}}::Digest={{.Digest}}`, reportData.dockerImages) - .catch((error) => { - throw new Error("Unable to get docker images: " + error); - }); - - core.debug("Finished data collection, starting API calls."); - - var reportSent: boolean = await this.sendReport(JSON.stringify(reportData), bearerToken, sendReportRetryCount); - if (!reportSent) { - throw new Error("Unable to send report to backend service"); - }; - core.info("Container mapping data sent successfully."); - } - - /** - * Execute command and setup the listener to capture the output - * @param command Command to execute - * @param listener Listener to capture the output - * @returns a Promise - */ - private async execCommand(command: string, listener: string[]): Promise { - return exec.getExecOutput(command) - .then((result) => { - if(result.exitCode != 0) { - return Promise.reject(`Command execution failed: ${result}`); - } - result.stdout.trim().split(os.EOL).forEach(element => { - if(element.length > 0) { - listener.push(element); - } - }); - }); - } - - /** - * Sends a report to Defender for DevOps and retries on the specified count - * @param data the data to send - * @param retryCount the number of time to retry - * @param bearerToken the GitHub-generated OIDC token - * @returns a boolean Promise to indicate if the report was sent successfully or not - */ - private async sendReport(data: string, bearerToken: string, retryCount: number = 0): Promise { - core.debug(`attempting to send report: ${data}`); - return await this._sendReport(data, bearerToken) - .then(() => { - return true; - }) - .catch(async (error) => { - if (retryCount == 0) { - return false; - } else { - core.info(`Retrying API call due to error: ${error}.\nRetry count: ${retryCount}`); - retryCount--; - return await this.sendReport(data, bearerToken, retryCount); - } - }); - } - - /** - * Sends a report to Defender for DevOps - * @param data the data to send - * @returns a Promise - */ - private async _sendReport(data: string, bearerToken: string): Promise { - return new Promise((resolve, reject) => { - let apiTime = Date.now(); - let options = { - method: 'POST', - timeout: 2500, - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + bearerToken, - 'Content-Length': Buffer.byteLength(data, 'utf8') - } - }; - core.debug(`${options['method'].toUpperCase()} ${ContainerMappingURL}`); - - const req = https.request(ContainerMappingURL, options, (res) => { - let resData = ''; - res.on('data', (chunk) => { - resData += chunk.toString(); - }); - - res.on('end', () => { - core.debug('API calls finished. Time taken: ' + (Date.now() - apiTime) + "ms"); - core.debug(`Status code: ${res.statusCode} ${res.statusMessage}`); - core.debug('Response headers: ' + JSON.stringify(res.headers)); - if (resData.length > 0) { - core.debug('Response: ' + resData); - } - if (res.statusCode < 200 || res.statusCode >= 300) { - return reject(`Received Failed Status code when calling url: ${res.statusCode} ${resData}`); - } - resolve(); - }); - }); - - req.on('error', (error) => { - reject(new Error(`Error calling url: ${error}`)); - }); - - req.write(data); - req.end(); - }); - } - - /** - * Queries Defender for DevOps to determine if the caller is onboarded for container mapping. - * @param retryCount the number of time to retry - * @param bearerToken the GitHub-generated OIDC token - * @returns a boolean Promise to indicate if the report was sent successfully or not - */ - private async checkCallerIsCustomer(bearerToken: string, retryCount: number = 0): Promise { - return await this._checkCallerIsCustomer(bearerToken) - .then(async (statusCode) => { - if (statusCode == 200) { // Status 'OK' means the caller is an onboarded customer. - return true; - } else if (statusCode == 403) { // Status 'Forbidden' means caller is not a customer. - return false; - } else { - core.debug(`Unexpected status code: ${statusCode}`); - return await this.retryCall(bearerToken, retryCount); - } - }) - .catch(async (error) => { - core.info(`Unexpected error: ${error}.`); - return await this.retryCall(bearerToken, retryCount); - }); - } - - private async retryCall(bearerToken: string, retryCount: number): Promise { - if (retryCount == 0) { - core.info(`All retries failed.`); - return false; - } else { - core.info(`Retrying checkCallerIsCustomer.\nRetry count: ${retryCount}`); - retryCount--; - return await this.checkCallerIsCustomer(bearerToken, retryCount); - } - } - - private async _checkCallerIsCustomer(bearerToken: string): Promise { - return new Promise((resolve, reject) => { - let options = { - method: 'GET', - timeout: 2500, - headers: { - 'Content-Type': 'application/json', - 'Authorization': 'Bearer ' + bearerToken, - } - }; - core.debug(`${options['method'].toUpperCase()} ${GetScanContextURL}`); - - const req = https.request(GetScanContextURL, options, (res) => { - - res.on('end', () => { - resolve(res.statusCode); - }); - res.on('data', function(d) { - }); - }); - - req.on('error', (error) => { - reject(new Error(`Error calling url: ${error}`)); - }); - - req.end(); - }); - } - -} diff --git a/src/v2/defender-cli.ts b/src/v2/defender-cli.ts deleted file mode 100644 index dfd22a8d..00000000 --- a/src/v2/defender-cli.ts +++ /dev/null @@ -1,176 +0,0 @@ -import * as core from '@actions/core'; -import * as path from 'path'; -import { ScanType, Inputs, validateScanType, validateImageName, validateModelPath, validateFileSystemPath, parseAdditionalArgs, setupDebugLogging } from './defender-helpers'; -import { IMicrosoftDefenderCLI } from './defender-interface'; -import { scanDirectory, scanImage, scanModel } from './defender-client'; -import { postJobSummary } from './job-summary'; - -/* - * Class for Microsoft Defender CLI functionality. - * Mirrors AzDevOps v2's defender-cli.ts, adapted for GitHub Actions. - */ -export class MicrosoftDefenderCLI implements IMicrosoftDefenderCLI { - readonly succeedOnError: boolean; - private prSummaryEnabled: boolean = true; - - constructor() { - this.succeedOnError = false; - } - - public async runPreJob() { - // No pre-job commands for Defender CLI scanning - } - - public async runPostJob() { - // No post-job commands for Defender CLI scanning - } - - public async runMain() { - await this.runDefenderCLI(); - } - - private async runDefenderCLI() { - // Get debug setting early to enable verbose logging - const debugInput = core.getInput(Inputs.Debug); - const debug = debugInput ? debugInput.toLowerCase() === 'true' : false; - if (debug) { - setupDebugLogging(true); - core.debug('Debug logging enabled'); - } - - // Get and validate scan type using 'command' input with 'fs' as default - const command: string = core.getInput(Inputs.Command) || 'fs'; - const scanType = validateScanType(command); - - // Get pr-summary flag (defaults to true) - const prSummaryInput = core.getInput(Inputs.PrSummary); - this.prSummaryEnabled = prSummaryInput ? prSummaryInput.toLowerCase() !== 'false' : true; - core.debug(`PR Summary enabled: ${this.prSummaryEnabled}`); - - // Get and parse additional arguments - const argsInput = core.getInput(Inputs.Args) || ''; - let additionalArgs = parseAdditionalArgs(argsInput); - - let target: string; - - // Get target based on scan type and validate - switch (scanType) { - case ScanType.FileSystem: - const fileSystemPath = core.getInput(Inputs.FileSystemPath) || - process.env['GITHUB_WORKSPACE'] || - process.cwd(); - target = validateFileSystemPath(fileSystemPath); - core.debug(`Filesystem scan using directory: ${target}`); - break; - - case ScanType.Image: - const imageName = core.getInput(Inputs.ImageName); - if (!imageName) { - throw new Error('Image name is required for image scan'); - } - target = validateImageName(imageName); - break; - - case ScanType.Model: - const modelPath = core.getInput(Inputs.ModelPath); - if (!modelPath) { - throw new Error('Model path is required for model scan'); - } - target = validateModelPath(modelPath); - break; - - default: - throw new Error(`Unsupported scan type: ${scanType}`); - } - - // Handle break on critical vulnerability - const breakInput = core.getInput(Inputs.Break); - const breakOnCritical = breakInput ? breakInput.toLowerCase() === 'true' : false; - - // Remove --defender-break from additional args if manually added - additionalArgs = additionalArgs.filter(arg => arg !== '--defender-break'); - - if (breakOnCritical) { - additionalArgs.push('--defender-break'); - core.debug('Break on critical vulnerability enabled: adding --defender-break flag'); - } - - // Remove --defender-debug from additional args if manually added - additionalArgs = additionalArgs.filter(arg => arg !== '--defender-debug'); - - if (debug) { - additionalArgs.push('--defender-debug'); - core.debug('Debug mode enabled: adding --defender-debug flag'); - } - - // Determine successful exit codes - let successfulExitCodes: number[] = [0]; - - // Generate output path - const outputPath = path.join( - process.env['RUNNER_TEMP'] || process.cwd(), - 'defender.sarif' - ); - - // Get policy from input, default to 'mdc' - const policyInput: string = core.getInput(Inputs.Policy) || 'mdc'; - let policy: string; - if (policyInput === 'none') { - policy = ''; - } else { - policy = policyInput; - } - - // Log scan information - core.debug(`Scan Type: ${scanType}`); - core.debug(`Target: ${target}`); - core.debug(`Policy: ${policy}`); - core.debug(`Output Path: ${outputPath}`); - if (additionalArgs.length > 0) { - core.debug(`Additional Arguments: ${additionalArgs.join(' ')}`); - } - - // Set environment variable to indicate execution via extension - process.env['Defender_Extension'] = 'true'; - core.debug('Environment variable set: Defender_Extension=true'); - - // Set the sarifFile output so downstream steps can reference it - core.setOutput('sarifFile', outputPath); - core.exportVariable('DEFENDER_SARIF_FILE', outputPath); - core.debug(`sarifFile output set to: ${outputPath}`); - - try { - switch (scanType) { - case ScanType.FileSystem: - await scanDirectory(target, policy, outputPath, successfulExitCodes, additionalArgs); - break; - - case ScanType.Image: - await scanImage(target, policy, outputPath, successfulExitCodes, additionalArgs); - break; - - case ScanType.Model: - await scanModel(target, policy, outputPath, successfulExitCodes, additionalArgs); - break; - } - - if (this.prSummaryEnabled) { - core.debug('Posting job summary...'); - await postJobSummary(outputPath, scanType, target); - } - } catch (error) { - // Still try to post summary on error if enabled (for partial results) - if (this.prSummaryEnabled) { - try { - await postJobSummary(outputPath, scanType, target); - } catch (summaryError) { - core.debug(`Failed to post summary after error: ${summaryError}`); - } - } - - core.error(`Defender CLI execution failed: ${error}`); - throw error; - } - } - -} diff --git a/src/v2/defender-client.ts b/src/v2/defender-client.ts deleted file mode 100644 index b5e833c5..00000000 --- a/src/v2/defender-client.ts +++ /dev/null @@ -1,156 +0,0 @@ -import * as core from '@actions/core'; -import * as exec from '@actions/exec'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as os from 'os'; -import * as installer from './defender-installer'; - -/** - * Scans a local filesystem directory for security vulnerabilities. - */ -export async function scanDirectory( - directoryPath: string, - policy?: string, - outputPath?: string, - successfulExitCodes?: number[], - additionalArgs?: string[] -): Promise { - await scan('fs', directoryPath, policy, outputPath, successfulExitCodes, additionalArgs); -} - -/** - * Scans a container image for security vulnerabilities. - */ -export async function scanImage( - imageName: string, - policy?: string, - outputPath?: string, - successfulExitCodes?: number[], - additionalArgs?: string[] -): Promise { - await scan('image', imageName, policy, outputPath, successfulExitCodes, additionalArgs); -} - -/** - * Scans an AI model for security vulnerabilities. - */ -export async function scanModel( - modelPath: string, - policy?: string, - outputPath?: string, - successfulExitCodes?: number[], - additionalArgs?: string[] -): Promise { - await scan('model', modelPath, policy, outputPath, successfulExitCodes, additionalArgs); -} - -/** - * Generic scan function used by scanDirectory and scanImage. - */ -async function scan( - scanType: string, - target: string, - policy?: string, - outputPath?: string, - successfulExitCodes?: number[], - additionalArgs?: string[] -): Promise { - const resolvedPolicy = policy || 'mdc'; - const resolvedOutputPath = outputPath || path.join( - process.env['RUNNER_TEMP'] || process.cwd(), - 'defender.sarif' - ); - - const inputArgs: string[] = [ - 'scan', - scanType, - target, - '--defender-policy', - resolvedPolicy, - '--defender-output', - resolvedOutputPath - ]; - - if (additionalArgs && additionalArgs.length > 0) { - inputArgs.push(...additionalArgs); - } - - await runDefenderCli(inputArgs, successfulExitCodes); -} - -/** - * Executes the Defender CLI with the given arguments. - */ -async function runDefenderCli( - inputArgs: string[], - successfulExitCodes?: number[] -): Promise { - await setupEnvironment(); - - const cliFilePath = getCliFilePath(); - if (!cliFilePath) { - throw new Error('DEFENDER_FILEPATH environment variable is not set. Defender CLI may not be installed.'); - } - - core.debug(`Running Defender CLI: ${cliFilePath} ${inputArgs.join(' ')}`); - - // Add debug flag if runner debug is enabled - const isDebug = process.env['RUNNER_DEBUG'] === '1' || core.isDebug(); - if (isDebug && !inputArgs.includes('--defender-debug')) { - inputArgs.push('--defender-debug'); - } - - const exitCode = await exec.exec(cliFilePath, inputArgs, { - ignoreReturnCode: true - }); - - const validExitCodes = successfulExitCodes || [0]; - - if (!validExitCodes.includes(exitCode)) { - throw new Error(`Defender CLI exited with an error exit code: ${exitCode}`); - } - - core.debug(`Defender CLI completed successfully with exit code: ${exitCode}`); -} - -/** - * Sets up the environment for the Defender CLI. - */ -export async function setupEnvironment(): Promise { - const toolCacheDir = process.env['RUNNER_TOOL_CACHE'] || path.join(os.homedir(), '.defender'); - const defenderDir = path.join(toolCacheDir, '_defender'); - - if (!fs.existsSync(defenderDir)) { - fs.mkdirSync(defenderDir, { recursive: true }); - } - - const packagesDirectory = process.env['DEFENDER_PACKAGES_DIRECTORY'] || path.join(defenderDir, 'packages'); - process.env['DEFENDER_PACKAGES_DIRECTORY'] = packagesDirectory; - - if (!process.env['DEFENDER_FILEPATH']) { - const cliVersion = resolveCliVersion(); - core.debug(`Installing Defender CLI version: ${cliVersion}`); - await installer.install(cliVersion); - } -} - -/** - * Resolves the CLI version to install. - */ -function resolveCliVersion(): string { - let version = process.env['DEFENDER_VERSION'] || 'latest'; - - if (version.includes('*')) { - version = 'Latest'; - } - - core.debug(`Resolved Defender CLI version: ${version}`); - return version; -} - -/** - * Gets the Defender CLI file path from environment. - */ -function getCliFilePath(): string | undefined { - return process.env['DEFENDER_FILEPATH']; -} diff --git a/src/v2/defender-helpers.ts b/src/v2/defender-helpers.ts deleted file mode 100644 index f230586e..00000000 --- a/src/v2/defender-helpers.ts +++ /dev/null @@ -1,215 +0,0 @@ -import * as core from '@actions/core'; -import * as fs from 'fs'; -import * as os from 'os'; -import { Writable } from 'stream'; - -/** - * Enum for the possible inputs for the task (specified in action.yml) - */ -export enum Inputs { - Command = 'command', - Args = 'args', - FileSystemPath = 'fileSystemPath', - ImageName = 'imageName', - ModelPath = 'modelPath', - Break = 'break', - Debug = 'debug', - PrSummary = 'pr-summary', - Policy = 'policy' -} - -/* - * Enum for the possible scan type values for the Inputs.Command - */ -export enum ScanType { - FileSystem = 'fs', - Image = 'image', - Model = 'model' -} - -/** - * Enum for defining constants used in the task. - */ -export enum Constants { - Unknown = 'unknown', - PreJobStartTime = 'PREJOBSTARTTIME', - DefenderExecutable = 'Defender' -} - -/** - * Validates the scan type input and returns the corresponding enum value. - */ -export function validateScanType(scanTypeInput: string): ScanType { - const scanType = scanTypeInput as ScanType; - if (!Object.values(ScanType).includes(scanType)) { - throw new Error(`Invalid scan type: ${scanTypeInput}. Valid options are: ${Object.values(ScanType).join(', ')}`); - } - return scanType; -} - -/** - * Validates the filesystem path input for filesystem scans. - */ -export function validateFileSystemPath(fsPath: string): string { - if (!fsPath || fsPath.trim() === '') { - throw new Error('Filesystem path cannot be empty for filesystem scan'); - } - - const trimmedPath = fsPath.trim(); - - if (!fs.existsSync(trimmedPath)) { - throw new Error(`Filesystem path does not exist: ${trimmedPath}`); - } - - return trimmedPath; -} - -/** - * Checks if a given string is a URL (http:// or https://). - */ -export function isUrl(input: string): boolean { - if (!input) { - return false; - } - const lowercased = input.toLowerCase(); - return lowercased.startsWith('http://') || lowercased.startsWith('https://'); -} - -/** - * Validates a URL for model scanning. - */ -export function validateModelUrl(url: string): string { - try { - const parsedUrl = new URL(url); - - if (parsedUrl.protocol !== 'http:' && parsedUrl.protocol !== 'https:') { - throw new Error(`Invalid URL protocol: ${parsedUrl.protocol}. Only http:// and https:// are supported.`); - } - - if (!parsedUrl.hostname) { - throw new Error('URL must have a valid hostname.'); - } - - return url; - } catch (error) { - if (error instanceof TypeError) { - throw new Error(`Invalid URL format: ${url}`); - } - throw error; - } -} - -/** - * Validates the model path input for AI model scans. - * Supports both local file paths and URLs. - */ -export function validateModelPath(modelPath: string): string { - if (!modelPath || modelPath.trim() === '') { - throw new Error('Model path cannot be empty for model scan'); - } - - const trimmedPath = modelPath.trim(); - - if (isUrl(trimmedPath)) { - return validateModelUrl(trimmedPath); - } - - if (!fs.existsSync(trimmedPath)) { - throw new Error(`Model path does not exist: ${trimmedPath}`); - } - - const stats = fs.statSync(trimmedPath); - if (!stats.isFile() && !stats.isDirectory()) { - throw new Error(`Model path must be a file or directory: ${trimmedPath}`); - } - - return trimmedPath; -} - -/** - * Validates the image name input for container image scans. - */ -export function validateImageName(imageName: string): string { - if (!imageName || imageName.trim() === '') { - throw new Error('Image name cannot be empty for image scan'); - } - - const trimmedImageName = imageName.trim(); - - const imageNameRegex = /^(?:(?:[a-zA-Z0-9._-]+(?:\.[a-zA-Z0-9._-]+)*(?::[0-9]+)?\/)?[a-zA-Z0-9._-]+(?:\/[a-zA-Z0-9._-]+)*)(?::[a-zA-Z0-9._-]+|@sha256:[a-fA-F0-9]{64})?$/; - - if (!imageNameRegex.test(trimmedImageName)) { - throw new Error(`Invalid image name format: ${trimmedImageName}. Image name should follow container image naming conventions.`); - } - - return trimmedImageName; -} - -/** - * Sets up debug logging. When enabled, sets RUNNER_DEBUG to enable verbose logging. - */ -export function setupDebugLogging(enabled: boolean): void { - if (enabled) { - process.env['RUNNER_DEBUG'] = '1'; - core.debug('Debug logging enabled'); - } -} - -/** - * Writes the specified data to the specified output stream, followed by the platform-specific end-of-line character. - */ -export function writeToOutStream(data: string, outStream: Writable = process.stdout): void { - outStream.write(data.trim() + os.EOL); -} - -/** - * Encodes a string to base64. - */ -export const encode = (str: string): string => Buffer.from(str, 'binary').toString('base64'); - -/** - * Returns the encoded content of the Docker version, Docker events, and Docker images. - */ -export function getEncodedContent( - dockerVersion: string, - dockerEvents: string, - dockerImages: string -): string { - let data: string[] = []; - data.push('DockerVersion: ' + dockerVersion); - data.push('DockerEvents:'); - data.push(dockerEvents); - data.push('DockerImages:'); - data.push(dockerImages); - return encode(data.join(os.EOL)); -} - -/** - * Parses additional CLI arguments from a string into an array. - * Handles quoted strings and splits on whitespace. - */ -export function parseAdditionalArgs(additionalArgs: string | undefined): string[] { - if (!additionalArgs || additionalArgs.trim() === '') { - return []; - } - - const args: string[] = []; - const trimmedArgs = additionalArgs.trim(); - - const regex = /(?:[^\s"']+|"[^"]*"|'[^']*')+/g; - const matches = trimmedArgs.match(regex); - - if (matches) { - for (const match of matches) { - let arg = match; - if ((arg.startsWith('"') && arg.endsWith('"')) || - (arg.startsWith("'") && arg.endsWith("'"))) { - arg = arg.slice(1, -1); - } - args.push(arg); - } - } - - core.debug(`Parsed additional arguments: ${JSON.stringify(args)}`); - return args; -} diff --git a/src/v2/defender-installer.ts b/src/v2/defender-installer.ts deleted file mode 100644 index 613c8a92..00000000 --- a/src/v2/defender-installer.ts +++ /dev/null @@ -1,261 +0,0 @@ -import * as core from '@actions/core'; -import * as crypto from 'crypto'; -import * as fs from 'fs'; -import * as https from 'https'; -import * as path from 'path'; -import * as os from 'os'; - -const downloadBaseUrl = 'https://cli.dfd.security.azure.com/public'; -const maxRetries = 3; -const downloadTimeoutMs = 30000; - -/** - * Installs the Defender CLI if not already present. - * @param cliVersion - The version of the CLI to install (default: 'latest') - */ -export async function install(cliVersion: string = 'latest'): Promise { - // If DEFENDER_FILEPATH is already set and the file exists, skip installation - const existingPath = process.env['DEFENDER_FILEPATH']; - if (existingPath && fs.existsSync(existingPath)) { - core.debug(`Defender CLI already installed at: ${existingPath}`); - return; - } - - // Check if DEFENDER_DIRECTORY is set (pre-installed CLI) - const existingDir = process.env['DEFENDER_DIRECTORY']; - if (existingDir && fs.existsSync(existingDir)) { - const fileName = resolveFileName(); - const filePath = path.join(existingDir, fileName); - if (fs.existsSync(filePath)) { - core.debug(`Found pre-installed Defender CLI at: ${filePath}`); - setVariables(existingDir, fileName, cliVersion); - return; - } - } - - // Determine packages directory - const toolCacheDir = process.env['RUNNER_TOOL_CACHE'] || path.join(os.homedir(), '.defender'); - const packagesDirectory = process.env['DEFENDER_PACKAGES_DIRECTORY'] || path.join(toolCacheDir, '_defender', 'packages'); - - if (!fs.existsSync(packagesDirectory)) { - fs.mkdirSync(packagesDirectory, { recursive: true }); - } - - const fileName = resolveFileName(); - - // Retry download up to maxRetries times - let lastError: Error | undefined; - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - core.info(`Downloading Defender CLI (attempt ${attempt}/${maxRetries})...`); - await downloadDefenderCli(packagesDirectory, fileName, cliVersion); - setVariables(packagesDirectory, fileName, cliVersion, true); - core.info(`Defender CLI installed successfully.`); - return; - } catch (error) { - lastError = error as Error; - core.warning(`Download attempt ${attempt} failed: ${lastError.message}`); - if (attempt < maxRetries) { - core.info('Retrying...'); - } - } - } - - throw new Error(`Failed to install Defender CLI after ${maxRetries} attempts: ${lastError?.message}`); -} - -/** - * Downloads the Defender CLI binary. - */ -async function downloadDefenderCli( - packagesDirectory: string, - fileName: string, - cliVersion: string -): Promise { - const versionDir = path.join(packagesDirectory, `defender-cli.${cliVersion}`); - if (!fs.existsSync(versionDir)) { - fs.mkdirSync(versionDir, { recursive: true }); - } - - const filePath = path.join(versionDir, fileName); - const downloadUrl = `${downloadBaseUrl}/${cliVersion.toLowerCase()}/${fileName}`; - - core.debug(`Downloading from: ${downloadUrl}`); - core.debug(`Saving to: ${filePath}`); - - await downloadFile(downloadUrl, filePath); - - await verifyIntegrity(filePath, downloadUrl); - - // Make executable on non-Windows platforms - if (process.platform !== 'win32') { - fs.chmodSync(filePath, 0o755); - } -} - -/** - * Downloads a file from a URL, following redirects. - */ -function downloadFile(url: string, filePath: string): Promise { - return new Promise((resolve, reject) => { - const file = fs.createWriteStream(filePath); - const request = https.get(url, { timeout: downloadTimeoutMs }, (response) => { - // Follow redirects (301, 302) - if (response.statusCode === 301 || response.statusCode === 302) { - file.close(); - fs.unlinkSync(filePath); - const redirectUrl = response.headers.location; - if (!redirectUrl) { - return reject(new Error('Redirect without location header')); - } - // Validate redirect stays on trusted host - const allowedHost = new URL(downloadBaseUrl).hostname; - const redirectHost = new URL(redirectUrl).hostname; - if (redirectHost !== allowedHost) { - return reject(new Error(`Redirect to untrusted host: ${redirectHost}. Expected: ${allowedHost}`)); - } - core.debug(`Following redirect to: ${redirectUrl}`); - downloadFile(redirectUrl, filePath).then(resolve).catch(reject); - return; - } - - if (response.statusCode !== 200) { - file.close(); - fs.unlinkSync(filePath); - return reject(new Error(`Download failed with status code: ${response.statusCode}`)); - } - - response.pipe(file); - file.on('finish', () => { - file.close(); - resolve(); - }); - }); - - request.on('error', (error) => { - file.close(); - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - reject(new Error(`Download error: ${error.message}`)); - }); - - request.on('timeout', () => { - request.destroy(); - file.close(); - if (fs.existsSync(filePath)) { - fs.unlinkSync(filePath); - } - reject(new Error('Download timed out')); - }); - }); -} - -/** - * Verifies the SHA-256 integrity of a downloaded file against a checksum sidecar. - */ -async function verifyIntegrity(filePath: string, downloadUrl: string): Promise { - const checksumUrl = `${downloadUrl}.sha256`; - const expectedHash = await downloadString(checksumUrl); - const expected = expectedHash.trim().split(/\s+/)[0].toLowerCase(); - const fileBuffer = fs.readFileSync(filePath); - const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex'); - if (actualHash !== expected) { - fs.unlinkSync(filePath); - throw new Error(`Integrity check failed for ${path.basename(filePath)}: expected ${expected}, got ${actualHash}`); - } - core.debug(`Integrity verified: ${actualHash}`); -} - -/** - * Downloads a URL and returns the response body as a string, following redirects with origin pinning. - */ -function downloadString(url: string): Promise { - return new Promise((resolve, reject) => { - const request = https.get(url, { timeout: downloadTimeoutMs }, (response) => { - // Follow redirects (301, 302) - if (response.statusCode === 301 || response.statusCode === 302) { - const redirectUrl = response.headers.location; - if (!redirectUrl) { - return reject(new Error('Redirect without location header')); - } - // Validate redirect stays on trusted host - const allowedHost = new URL(downloadBaseUrl).hostname; - const redirectHost = new URL(redirectUrl).hostname; - if (redirectHost !== allowedHost) { - return reject(new Error(`Redirect to untrusted host: ${redirectHost}. Expected: ${allowedHost}`)); - } - core.debug(`Following redirect to: ${redirectUrl}`); - downloadString(redirectUrl).then(resolve).catch(reject); - return; - } - - if (response.statusCode !== 200) { - return reject(new Error(`Download failed with status code: ${response.statusCode}`)); - } - - const chunks: Buffer[] = []; - response.on('data', (chunk: Buffer) => chunks.push(chunk)); - response.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8'))); - response.on('error', (error) => reject(new Error(`Download error: ${error.message}`))); - }); - - request.on('error', (error) => { - reject(new Error(`Download error: ${error.message}`)); - }); - - request.on('timeout', () => { - request.destroy(); - reject(new Error('Download timed out')); - }); - }); -} - -/** - * Resolves the platform-specific Defender CLI binary filename. - */ -export function resolveFileName(): string { - const platform = os.platform(); - const arch = os.arch(); - - switch (platform) { - case 'win32': - if (arch === 'arm64') return 'Defender_win-arm64.exe'; - if (arch === 'ia32') return 'Defender_win-x86.exe'; - return 'Defender_win-x64.exe'; - case 'linux': - if (arch === 'arm64') return 'Defender_linux-arm64'; - return 'Defender_linux-x64'; - case 'darwin': - if (arch === 'arm64') return 'Defender_osx-arm64'; - return 'Defender_osx-x64'; - default: - core.warning(`Unknown platform: ${platform}. Defaulting to linux-x64.`); - return 'Defender_linux-x64'; - } -} - -/** - * Sets environment variables for the Defender CLI location. - */ -export function setVariables( - packagesDirectory: string, - fileName: string, - cliVersion: string, - validate: boolean = false -): void { - const defenderDir = path.join(packagesDirectory, `defender-cli.${cliVersion}`); - const defenderFilePath = path.join(defenderDir, fileName); - - if (validate && !fs.existsSync(defenderFilePath)) { - throw new Error(`Defender CLI not found after download: ${defenderFilePath}`); - } - - process.env['DEFENDER_DIRECTORY'] = defenderDir; - process.env['DEFENDER_FILEPATH'] = defenderFilePath; - process.env['DEFENDER_INSTALLEDVERSION'] = cliVersion; - - core.debug(`DEFENDER_DIRECTORY=${defenderDir}`); - core.debug(`DEFENDER_FILEPATH=${defenderFilePath}`); - core.debug(`DEFENDER_INSTALLEDVERSION=${cliVersion}`); -} diff --git a/src/v2/defender-interface.ts b/src/v2/defender-interface.ts deleted file mode 100644 index ab9b30f8..00000000 --- a/src/v2/defender-interface.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Interface for the MicrosoftDefenderCLI task. - * Mirrors the AzDevOps v2 defender-interface.ts, adapted for GitHub Actions 3-phase lifecycle. - */ -export interface IMicrosoftDefenderCLI { - readonly succeedOnError: boolean; - runPreJob(): any; - runMain(): any; - runPostJob(): any; -} - -/* - * Factory interface for creating IMicrosoftDefenderCLI instances. - */ -export interface IMicrosoftDefenderCLIFactory { - new(): IMicrosoftDefenderCLI; -} - -/** - * Returns an instance of IMicrosoftDefenderCLI based on the input runner. - * @param runner - The factory to use to create the instance. - * @returns An instance of IMicrosoftDefenderCLI. - */ -export function getDefenderExecutor(runner: IMicrosoftDefenderCLIFactory): IMicrosoftDefenderCLI { - return new runner(); -} diff --git a/src/v2/defender-main.ts b/src/v2/defender-main.ts deleted file mode 100644 index ffc51e34..00000000 --- a/src/v2/defender-main.ts +++ /dev/null @@ -1,34 +0,0 @@ -import * as core from '@actions/core'; -import { MicrosoftDefenderCLI } from './defender-cli'; -import { IMicrosoftDefenderCLI, IMicrosoftDefenderCLIFactory, getDefenderExecutor } from './defender-interface'; -import { writeToOutStream } from './defender-helpers'; - -let succeedOnError = false; - -/** - * Returns an instance of IMicrosoftDefenderCLI. - * The scan type (fs, image, model) is determined by the CLI class based on action inputs. - */ -function _getDefenderRunner(): IMicrosoftDefenderCLI { - return getDefenderExecutor(MicrosoftDefenderCLI); -} - -/** - * Main entry point for the Defender CLI v2 action. - * Creates and runs the Defender CLI which handles all scan types (filesystem, image, model). - */ -async function run() { - core.debug('Starting Microsoft Defender for DevOps scan'); - const defenderRunner = _getDefenderRunner(); - succeedOnError = defenderRunner.succeedOnError; - await defenderRunner.runMain(); -} - -run().catch(error => { - if (succeedOnError) { - writeToOutStream('Ran into error: ' + error); - core.info('Finished execution with error (succeedOnError=true)'); - } else { - core.setFailed(error); - } -}); diff --git a/src/v2/job-summary.ts b/src/v2/job-summary.ts deleted file mode 100644 index b29e1988..00000000 --- a/src/v2/job-summary.ts +++ /dev/null @@ -1,392 +0,0 @@ -import * as core from '@actions/core'; -import * as fs from 'fs'; - -/** - * SARIF result level (severity) mappings - */ -export enum SarifLevel { - Error = 'error', - Warning = 'warning', - Note = 'note', - None = 'none' -} - -/** - * Vulnerability severity levels - */ -export enum Severity { - Critical = 'critical', - High = 'high', - Medium = 'medium', - Low = 'low', - Unknown = 'unknown' -} - -/** - * Represents a parsed vulnerability from SARIF - */ -export interface Vulnerability { - ruleId: string; - message: string; - severity: Severity; - location?: string; - cveId?: string; -} - -/** - * Summary statistics for vulnerabilities - */ -export interface VulnerabilitySummary { - total: number; - critical: number; - high: number; - medium: number; - low: number; - unknown: number; - vulnerabilities: Vulnerability[]; -} - -interface SarifLocation { - physicalLocation?: { - artifactLocation?: { - uri?: string; - }; - region?: { - startLine?: number; - }; - }; -} - -interface SarifResult { - ruleId?: string; - message?: { - text?: string; - }; - level?: string; - locations?: SarifLocation[]; - properties?: { - severity?: string; - cveId?: string; - [key: string]: unknown; - }; -} - -interface SarifRule { - id: string; - shortDescription?: { - text?: string; - }; - defaultConfiguration?: { - level?: string; - }; - properties?: { - severity?: string; - [key: string]: unknown; - }; -} - -interface SarifRun { - tool?: { - driver?: { - name?: string; - rules?: SarifRule[]; - }; - }; - results?: SarifResult[]; -} - -interface SarifDocument { - $schema?: string; - version?: string; - runs?: SarifRun[]; -} - -/** - * Maps SARIF level to severity - */ -export function mapLevelToSeverity(level: string | undefined, properties?: { severity?: string }): Severity { - if (properties?.severity) { - const propSeverity = properties.severity.toLowerCase(); - if (propSeverity === 'critical') return Severity.Critical; - if (propSeverity === 'high') return Severity.High; - if (propSeverity === 'medium') return Severity.Medium; - if (propSeverity === 'low') return Severity.Low; - } - - switch (level?.toLowerCase()) { - case SarifLevel.Error: - return Severity.High; - case SarifLevel.Warning: - return Severity.Medium; - case SarifLevel.Note: - return Severity.Low; - case SarifLevel.None: - return Severity.Low; - default: - return Severity.Unknown; - } -} - -/** - * Extracts CVE ID from rule ID or properties - */ -export function extractCveId(ruleId: string | undefined, properties?: { cveId?: string }): string | undefined { - if (properties?.cveId) { - return properties.cveId; - } - - if (ruleId) { - const cveMatch = ruleId.match(/CVE-\d{4}-\d+/i); - if (cveMatch) { - return cveMatch[0].toUpperCase(); - } - } - - return undefined; -} - -/** - * Formats a location from SARIF into a readable string - */ -export function formatLocation(locations?: SarifLocation[]): string | undefined { - if (!locations || locations.length === 0) { - return undefined; - } - - const loc = locations[0]; - const uri = loc.physicalLocation?.artifactLocation?.uri; - const line = loc.physicalLocation?.region?.startLine; - - if (uri) { - return line ? `${uri}:${line}` : uri; - } - - return undefined; -} - -/** - * Parses a SARIF document and extracts vulnerability information - */ -export function parseSarifContent(sarifContent: string): VulnerabilitySummary { - const summary: VulnerabilitySummary = { - total: 0, - critical: 0, - high: 0, - medium: 0, - low: 0, - unknown: 0, - vulnerabilities: [] - }; - - let sarif: SarifDocument; - try { - sarif = JSON.parse(sarifContent) as SarifDocument; - } catch (error) { - core.warning(`Failed to parse SARIF content: ${error}`); - return summary; - } - - if (!sarif.runs || sarif.runs.length === 0) { - core.debug('No runs found in SARIF document'); - return summary; - } - - const rulesMap = new Map(); - - for (const run of sarif.runs) { - if (run.tool?.driver?.rules) { - for (const rule of run.tool.driver.rules) { - rulesMap.set(rule.id, rule); - } - } - - if (run.results) { - for (const result of run.results) { - const ruleId = result.ruleId || 'unknown'; - const rule = rulesMap.get(ruleId); - - const severity = mapLevelToSeverity( - result.level || rule?.defaultConfiguration?.level, - result.properties || rule?.properties - ); - - const vulnerability: Vulnerability = { - ruleId, - message: result.message?.text || rule?.shortDescription?.text || 'No description available', - severity, - location: formatLocation(result.locations), - cveId: extractCveId(ruleId, result.properties) - }; - - summary.vulnerabilities.push(vulnerability); - summary.total++; - - switch (severity) { - case Severity.Critical: - summary.critical++; - break; - case Severity.High: - summary.high++; - break; - case Severity.Medium: - summary.medium++; - break; - case Severity.Low: - summary.low++; - break; - default: - summary.unknown++; - } - } - } - } - - return summary; -} - -/** - * Generates a markdown summary from vulnerability data - */ -export function generateMarkdownSummary( - summary: VulnerabilitySummary, - scanType: string, - target: string, - hasCriticalOrHigh: boolean -): string { - const lines: string[] = []; - - lines.push('# Microsoft Defender for DevOps Scan Results'); - lines.push(''); - - lines.push('## Summary'); - lines.push('| Severity | Count |'); - lines.push('|----------|-------|'); - lines.push(`| 🔴 Critical | ${summary.critical} |`); - lines.push(`| 🟠 High | ${summary.high} |`); - lines.push(`| 🟡 Medium | ${summary.medium} |`); - lines.push(`| 🟢 Low | ${summary.low} |`); - if (summary.unknown > 0) { - lines.push(`| ⚪ Unknown | ${summary.unknown} |`); - } - lines.push(''); - lines.push(`**Total Vulnerabilities**: ${summary.total}`); - lines.push(''); - - if (summary.critical > 0 || summary.high > 0) { - lines.push('## Critical and High Findings'); - - const criticalAndHigh = summary.vulnerabilities.filter( - v => v.severity === Severity.Critical || v.severity === Severity.High - ); - - let index = 1; - for (const vuln of criticalAndHigh.slice(0, 20)) { - const severityIcon = vuln.severity === Severity.Critical ? '🔴' : '🟠'; - const identifier = vuln.cveId || vuln.ruleId; - const location = vuln.location ? ` in \`${vuln.location}\`` : ''; - lines.push(`${index}. ${severityIcon} **${identifier}** - ${vuln.message}${location}`); - index++; - } - - if (criticalAndHigh.length > 20) { - lines.push(`... and ${criticalAndHigh.length - 20} more`); - } - - lines.push(''); - } - - lines.push('## Scan Details'); - lines.push(`- **Scan Type**: ${formatScanType(scanType)}`); - lines.push(`- **Target**: \`${target}\``); - - const statusIcon = hasCriticalOrHigh ? '❌' : '✅'; - const statusText = hasCriticalOrHigh - ? 'Failed (Critical/High vulnerabilities found)' - : 'Passed'; - lines.push(`- **Status**: ${statusIcon} ${statusText}`); - lines.push(''); - - lines.push('---'); - lines.push('*Generated by Microsoft Defender for DevOps*'); - - return lines.join('\n'); -} - -/** - * Formats the scan type for display - */ -function formatScanType(scanType: string): string { - switch (scanType.toLowerCase()) { - case 'fs': - return 'Filesystem'; - case 'image': - return 'Container Image'; - case 'model': - return 'AI Model'; - default: - return scanType; - } -} - -/** - * Creates a no-results summary when no vulnerabilities are found - */ -export function generateNoFindingsSummary(scanType: string, target: string): string { - const lines: string[] = []; - - lines.push('# Microsoft Defender for DevOps Scan Results'); - lines.push(''); - lines.push('## Summary'); - lines.push('✅ **No vulnerabilities found!**'); - lines.push(''); - lines.push('## Scan Details'); - lines.push(`- **Scan Type**: ${formatScanType(scanType)}`); - lines.push(`- **Target**: \`${target}\``); - lines.push('- **Status**: ✅ Passed'); - lines.push(''); - lines.push('---'); - lines.push('*Generated by Microsoft Defender for DevOps*'); - - return lines.join('\n'); -} - -/** - * Posts the vulnerability summary to GitHub Job Summary. - * Reads SARIF output, parses it, generates markdown, and writes to job summary. - */ -export async function postJobSummary( - sarifPath: string, - scanType: string, - target: string -): Promise { - try { - core.debug(`Attempting to post job summary from SARIF: ${sarifPath}`); - - if (!fs.existsSync(sarifPath)) { - core.warning(`SARIF file not found at ${sarifPath}. Skipping job summary.`); - return false; - } - - const sarifContent = fs.readFileSync(sarifPath, 'utf8'); - const summary = parseSarifContent(sarifContent); - - core.debug(`Parsed ${summary.total} vulnerabilities from SARIF`); - - const hasCriticalOrHigh = summary.critical > 0 || summary.high > 0; - - let markdown: string; - if (summary.total === 0) { - markdown = generateNoFindingsSummary(scanType, target); - } else { - markdown = generateMarkdownSummary(summary, scanType, target, hasCriticalOrHigh); - } - - await core.summary.addRaw(markdown).write(); - core.debug('Posted summary to GitHub Job Summary'); - - return true; - } catch (error) { - core.warning(`Failed to post job summary: ${error}`); - return false; - } -} diff --git a/src/v2/post.ts b/src/v2/post.ts deleted file mode 100644 index f2374316..00000000 --- a/src/v2/post.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as core from '@actions/core'; -import { ContainerMapping } from './container-mapping'; -import { getDefenderExecutor } from './defender-interface'; - -async function runPost() { - await getDefenderExecutor(ContainerMapping).runPostJob(); -} - -runPost().catch((error) => { - core.debug(error); -}); diff --git a/src/v2/pre.ts b/src/v2/pre.ts deleted file mode 100644 index de2eb59b..00000000 --- a/src/v2/pre.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as core from '@actions/core'; -import { ContainerMapping } from './container-mapping'; -import { getDefenderExecutor } from './defender-interface'; - -async function runPre() { - await getDefenderExecutor(ContainerMapping).runPreJob(); -} - -runPre().catch((error) => { - core.debug(error); -}); diff --git a/test/defender-client.tests.ts b/test/defender-client.tests.ts deleted file mode 100644 index cdb0ca2d..00000000 --- a/test/defender-client.tests.ts +++ /dev/null @@ -1,79 +0,0 @@ -import assert from 'assert'; -import sinon from 'sinon'; -import * as exec from '@actions/exec'; -import * as core from '@actions/core'; -import * as installer from '../lib/v2/defender-installer'; - -describe('defender-client', () => { - let execStub: sinon.SinonStub; - let installStub: sinon.SinonStub; - - beforeEach(() => { - execStub = sinon.stub(exec, 'exec'); - installStub = sinon.stub(installer, 'install'); - - // Set up environment for tests - process.env['DEFENDER_FILEPATH'] = '/path/to/defender'; - process.env['RUNNER_TOOL_CACHE'] = '/tmp/tool-cache'; - - installStub.resolves(); - execStub.resolves(0); - }); - - afterEach(() => { - execStub.restore(); - installStub.restore(); - delete process.env['DEFENDER_FILEPATH']; - delete process.env['RUNNER_TOOL_CACHE']; - delete process.env['DEFENDER_PACKAGES_DIRECTORY']; - delete process.env['RUNNER_DEBUG']; - }); - - it('should call exec with correct args for filesystem scan', async () => { - const { scanDirectory } = require('../lib/v2/defender-client'); - await scanDirectory('/test/path', 'github', '/output/defender.sarif', [0], []); - - sinon.assert.calledOnce(execStub); - const args = execStub.firstCall.args; - assert.strictEqual(args[0], '/path/to/defender'); - assert.ok(args[1].includes('scan')); - assert.ok(args[1].includes('fs')); - assert.ok(args[1].includes('/test/path')); - assert.ok(args[1].includes('--defender-policy')); - assert.ok(args[1].includes('github')); - assert.ok(args[1].includes('--defender-output')); - }); - - it('should call exec with correct args for image scan', async () => { - const { scanImage } = require('../lib/v2/defender-client'); - await scanImage('nginx:latest', 'mdc', '/output/defender.sarif', [0], ['--defender-break']); - - sinon.assert.calledOnce(execStub); - const args = execStub.firstCall.args; - assert.strictEqual(args[0], '/path/to/defender'); - assert.ok(args[1].includes('scan')); - assert.ok(args[1].includes('image')); - assert.ok(args[1].includes('nginx:latest')); - assert.ok(args[1].includes('--defender-break')); - }); - - it('should throw when CLI exits with non-zero code', async () => { - execStub.resolves(1); - const { scanDirectory } = require('../lib/v2/defender-client'); - - await assert.rejects( - () => scanDirectory('/test/path'), - /error exit code: 1/ - ); - }); - - it('should add --defender-debug when RUNNER_DEBUG is set', async () => { - process.env['RUNNER_DEBUG'] = '1'; - const { scanDirectory } = require('../lib/v2/defender-client'); - await scanDirectory('/test/path', 'github', '/output/defender.sarif', [0], []); - - sinon.assert.calledOnce(execStub); - const args = execStub.firstCall.args[1]; - assert.ok(args.includes('--defender-debug')); - }); -}); diff --git a/test/defender-helpers.tests.ts b/test/defender-helpers.tests.ts deleted file mode 100644 index b2639920..00000000 --- a/test/defender-helpers.tests.ts +++ /dev/null @@ -1,180 +0,0 @@ -import assert from 'assert'; -import sinon from 'sinon'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as os from 'os'; -import { - validateScanType, - validateFileSystemPath, - validateImageName, - validateModelPath, - validateModelUrl, - isUrl, - parseAdditionalArgs, - ScanType -} from '../lib/v2/defender-helpers'; - -describe('defender-helpers', () => { - - describe('validateScanType', () => { - it('should accept "fs" as a valid scan type', () => { - assert.strictEqual(validateScanType('fs'), ScanType.FileSystem); - }); - - it('should accept "image" as a valid scan type', () => { - assert.strictEqual(validateScanType('image'), ScanType.Image); - }); - - it('should accept "model" as a valid scan type', () => { - assert.strictEqual(validateScanType('model'), ScanType.Model); - }); - - it('should throw for invalid scan type', () => { - assert.throws(() => validateScanType('invalid'), /Invalid scan type/); - }); - - it('should throw for empty string', () => { - assert.throws(() => validateScanType(''), /Invalid scan type/); - }); - }); - - describe('validateFileSystemPath', () => { - it('should return trimmed path when it exists', () => { - // Use __dirname as a known-existing path - const result = validateFileSystemPath(` ${__dirname} `); - assert.strictEqual(result, __dirname); - }); - - it('should throw when path is empty', () => { - assert.throws(() => validateFileSystemPath(''), /cannot be empty/); - }); - - it('should throw when path is whitespace', () => { - assert.throws(() => validateFileSystemPath(' '), /cannot be empty/); - }); - - it('should throw when path does not exist', () => { - assert.throws(() => validateFileSystemPath('/definitely/nonexistent/path/abc123'), /does not exist/); - }); - }); - - describe('validateImageName', () => { - it('should accept simple image name', () => { - assert.strictEqual(validateImageName('nginx'), 'nginx'); - }); - - it('should accept image with tag', () => { - assert.strictEqual(validateImageName('nginx:latest'), 'nginx:latest'); - }); - - it('should accept fully qualified image name', () => { - assert.strictEqual( - validateImageName('myregistry.azurecr.io/myapp:v1.0'), - 'myregistry.azurecr.io/myapp:v1.0' - ); - }); - - it('should accept image with sha256 digest', () => { - const digest = 'nginx@sha256:' + 'a'.repeat(64); - assert.strictEqual(validateImageName(digest), digest); - }); - - it('should throw for empty image name', () => { - assert.throws(() => validateImageName(''), /cannot be empty/); - }); - - it('should trim whitespace', () => { - assert.strictEqual(validateImageName(' nginx:latest '), 'nginx:latest'); - }); - }); - - describe('isUrl', () => { - it('should return true for http URL', () => { - assert.strictEqual(isUrl('http://example.com'), true); - }); - - it('should return true for https URL', () => { - assert.strictEqual(isUrl('https://example.com/model'), true); - }); - - it('should return false for local path', () => { - assert.strictEqual(isUrl('/local/path'), false); - }); - - it('should return false for empty string', () => { - assert.strictEqual(isUrl(''), false); - }); - - it('should return false for null/undefined', () => { - assert.strictEqual(isUrl(null as any), false); - assert.strictEqual(isUrl(undefined as any), false); - }); - }); - - describe('validateModelUrl', () => { - it('should accept valid https URL', () => { - assert.strictEqual(validateModelUrl('https://example.com/model'), 'https://example.com/model'); - }); - - it('should accept valid http URL', () => { - assert.strictEqual(validateModelUrl('http://example.com/model'), 'http://example.com/model'); - }); - - it('should throw for invalid URL format', () => { - assert.throws(() => validateModelUrl('not-a-url'), /Invalid URL/); - }); - }); - - describe('validateModelPath', () => { - it('should throw for empty path', () => { - assert.throws(() => validateModelPath(''), /cannot be empty/); - }); - - it('should accept URL without checking filesystem', () => { - const result = validateModelPath('https://example.com/model'); - assert.strictEqual(result, 'https://example.com/model'); - }); - - it('should accept existing directory as model path', () => { - // Use __dirname as a known-existing directory - const result = validateModelPath(__dirname); - assert.strictEqual(result, __dirname); - }); - - it('should throw when local path does not exist', () => { - assert.throws(() => validateModelPath('/definitely/nonexistent/model/path'), /does not exist/); - }); - }); - - describe('parseAdditionalArgs', () => { - it('should return empty array for undefined', () => { - assert.deepStrictEqual(parseAdditionalArgs(undefined), []); - }); - - it('should return empty array for empty string', () => { - assert.deepStrictEqual(parseAdditionalArgs(''), []); - }); - - it('should return empty array for whitespace', () => { - assert.deepStrictEqual(parseAdditionalArgs(' '), []); - }); - - it('should parse simple arguments', () => { - assert.deepStrictEqual(parseAdditionalArgs('--flag1 --flag2'), ['--flag1', '--flag2']); - }); - - it('should handle quoted arguments', () => { - assert.deepStrictEqual( - parseAdditionalArgs('--flag "value with spaces"'), - ['--flag', 'value with spaces'] - ); - }); - - it('should handle single-quoted arguments', () => { - assert.deepStrictEqual( - parseAdditionalArgs("--flag 'value with spaces'"), - ['--flag', 'value with spaces'] - ); - }); - }); -}); diff --git a/test/defender-installer.tests.ts b/test/defender-installer.tests.ts deleted file mode 100644 index c52fb368..00000000 --- a/test/defender-installer.tests.ts +++ /dev/null @@ -1,76 +0,0 @@ -import assert from 'assert'; -import sinon from 'sinon'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as os from 'os'; -import { resolveFileName, setVariables } from '../lib/v2/defender-installer'; - -describe('defender-installer', () => { - - describe('resolveFileName', () => { - it('should return a platform-appropriate binary name', () => { - const result = resolveFileName(); - const platform = process.platform; - - if (platform === 'win32') { - assert.ok(result.startsWith('Defender_win-'), `Expected Windows binary, got: ${result}`); - assert.ok(result.endsWith('.exe'), `Expected .exe extension, got: ${result}`); - } else if (platform === 'linux') { - assert.ok(result.startsWith('Defender_linux-'), `Expected Linux binary, got: ${result}`); - assert.ok(!result.endsWith('.exe'), `Unexpected .exe extension on Linux`); - } else if (platform === 'darwin') { - assert.ok(result.startsWith('Defender_osx-'), `Expected macOS binary, got: ${result}`); - assert.ok(!result.endsWith('.exe'), `Unexpected .exe extension on macOS`); - } - }); - - it('should include architecture in the filename', () => { - const result = resolveFileName(); - assert.ok( - result.includes('x64') || result.includes('arm64') || result.includes('x86'), - `Expected architecture in filename, got: ${result}` - ); - }); - - it('should return a non-empty string', () => { - const result = resolveFileName(); - assert.ok(result.length > 0); - }); - }); - - describe('setVariables', () => { - beforeEach(() => { - delete process.env['DEFENDER_DIRECTORY']; - delete process.env['DEFENDER_FILEPATH']; - delete process.env['DEFENDER_INSTALLEDVERSION']; - }); - - afterEach(() => { - delete process.env['DEFENDER_DIRECTORY']; - delete process.env['DEFENDER_FILEPATH']; - delete process.env['DEFENDER_INSTALLEDVERSION']; - }); - - it('should set environment variables correctly', () => { - const packagesDir = path.join(os.tmpdir(), 'test-packages'); - setVariables(packagesDir, 'Defender_linux-x64', 'latest'); - - assert.ok(process.env['DEFENDER_DIRECTORY']?.includes('test-packages')); - assert.ok(process.env['DEFENDER_FILEPATH']?.includes('Defender_linux-x64')); - assert.strictEqual(process.env['DEFENDER_INSTALLEDVERSION'], 'latest'); - }); - - it('should throw when validate=true and file does not exist', () => { - const packagesDir = path.join(os.tmpdir(), 'nonexistent-test-packages'); - assert.throws( - () => setVariables(packagesDir, 'Defender_linux-x64', 'latest', true), - /not found after download/ - ); - }); - - it('should not throw when validate=false and file does not exist', () => { - const packagesDir = path.join(os.tmpdir(), 'nonexistent-test-packages'); - assert.doesNotThrow(() => setVariables(packagesDir, 'Defender_linux-x64', 'latest', false)); - }); - }); -}); diff --git a/test/job-summary.tests.ts b/test/job-summary.tests.ts deleted file mode 100644 index d802d39a..00000000 --- a/test/job-summary.tests.ts +++ /dev/null @@ -1,230 +0,0 @@ -import assert from 'assert'; -import sinon from 'sinon'; -import * as core from '@actions/core'; -import { - mapLevelToSeverity, - extractCveId, - formatLocation, - parseSarifContent, - generateMarkdownSummary, - generateNoFindingsSummary, - Severity, - SarifLevel -} from '../lib/v2/job-summary'; - -describe('job-summary', () => { - - describe('mapLevelToSeverity', () => { - it('should use properties.severity when available', () => { - assert.strictEqual(mapLevelToSeverity('error', { severity: 'critical' }), Severity.Critical); - }); - - it('should use properties.severity over level', () => { - assert.strictEqual(mapLevelToSeverity('note', { severity: 'high' }), Severity.High); - }); - - it('should map error level to High', () => { - assert.strictEqual(mapLevelToSeverity('error'), Severity.High); - }); - - it('should map warning level to Medium', () => { - assert.strictEqual(mapLevelToSeverity('warning'), Severity.Medium); - }); - - it('should map note level to Low', () => { - assert.strictEqual(mapLevelToSeverity('note'), Severity.Low); - }); - - it('should map none level to Low', () => { - assert.strictEqual(mapLevelToSeverity('none'), Severity.Low); - }); - - it('should return Unknown for undefined level', () => { - assert.strictEqual(mapLevelToSeverity(undefined), Severity.Unknown); - }); - - it('should return Unknown for unrecognized level', () => { - assert.strictEqual(mapLevelToSeverity('unknown-level'), Severity.Unknown); - }); - }); - - describe('extractCveId', () => { - it('should extract CVE from properties', () => { - assert.strictEqual(extractCveId('rule1', { cveId: 'CVE-2024-1234' }), 'CVE-2024-1234'); - }); - - it('should extract CVE from ruleId', () => { - assert.strictEqual(extractCveId('CVE-2024-1234'), 'CVE-2024-1234'); - }); - - it('should extract CVE from mixed case ruleId', () => { - assert.strictEqual(extractCveId('cve-2024-5678'), 'CVE-2024-5678'); - }); - - it('should return undefined when no CVE found', () => { - assert.strictEqual(extractCveId('rule1'), undefined); - }); - - it('should return undefined for undefined inputs', () => { - assert.strictEqual(extractCveId(undefined), undefined); - }); - }); - - describe('formatLocation', () => { - it('should format location with uri and line', () => { - const locations = [{ - physicalLocation: { - artifactLocation: { uri: 'src/main.ts' }, - region: { startLine: 42 } - } - }]; - assert.strictEqual(formatLocation(locations), 'src/main.ts:42'); - }); - - it('should format location with uri only', () => { - const locations = [{ - physicalLocation: { - artifactLocation: { uri: 'src/main.ts' } - } - }]; - assert.strictEqual(formatLocation(locations), 'src/main.ts'); - }); - - it('should return undefined for empty locations', () => { - assert.strictEqual(formatLocation([]), undefined); - }); - - it('should return undefined for undefined locations', () => { - assert.strictEqual(formatLocation(undefined), undefined); - }); - }); - - describe('parseSarifContent', () => { - it('should parse valid SARIF with vulnerabilities', () => { - const sarif = { - version: '2.1.0', - runs: [{ - tool: { - driver: { - name: 'Defender', - rules: [{ - id: 'CVE-2024-1234', - shortDescription: { text: 'Test vulnerability' }, - defaultConfiguration: { level: 'error' } - }] - } - }, - results: [{ - ruleId: 'CVE-2024-1234', - message: { text: 'Found vulnerability' }, - level: 'error', - properties: { severity: 'critical' } - }] - }] - }; - - const summary = parseSarifContent(JSON.stringify(sarif)); - assert.strictEqual(summary.total, 1); - assert.strictEqual(summary.critical, 1); - assert.strictEqual(summary.vulnerabilities[0].ruleId, 'CVE-2024-1234'); - }); - - it('should return empty summary for empty SARIF', () => { - const sarif = { version: '2.1.0', runs: [{ results: [] }] }; - const summary = parseSarifContent(JSON.stringify(sarif)); - assert.strictEqual(summary.total, 0); - }); - - it('should handle invalid JSON gracefully', () => { - const summary = parseSarifContent('not valid json'); - assert.strictEqual(summary.total, 0); - }); - - it('should handle SARIF with no runs', () => { - const summary = parseSarifContent(JSON.stringify({ version: '2.1.0' })); - assert.strictEqual(summary.total, 0); - }); - - it('should count multiple severity levels correctly', () => { - const sarif = { - version: '2.1.0', - runs: [{ - tool: { driver: { name: 'Defender' } }, - results: [ - { ruleId: 'r1', level: 'error', message: { text: 'high' }, properties: { severity: 'high' } }, - { ruleId: 'r2', level: 'warning', message: { text: 'medium' } }, - { ruleId: 'r3', level: 'note', message: { text: 'low' } }, - { ruleId: 'r4', level: 'error', message: { text: 'critical' }, properties: { severity: 'critical' } } - ] - }] - }; - - const summary = parseSarifContent(JSON.stringify(sarif)); - assert.strictEqual(summary.total, 4); - assert.strictEqual(summary.critical, 1); - assert.strictEqual(summary.high, 1); - assert.strictEqual(summary.medium, 1); - assert.strictEqual(summary.low, 1); - }); - }); - - describe('generateMarkdownSummary', () => { - it('should generate summary with critical findings', () => { - const summary = { - total: 2, - critical: 1, - high: 1, - medium: 0, - low: 0, - unknown: 0, - vulnerabilities: [ - { ruleId: 'CVE-2024-1', message: 'Critical issue', severity: Severity.Critical, cveId: 'CVE-2024-1' }, - { ruleId: 'CVE-2024-2', message: 'High issue', severity: Severity.High, cveId: 'CVE-2024-2' } - ] - }; - - const md = generateMarkdownSummary(summary, 'fs', '/src', true); - assert.ok(md.includes('Microsoft Defender')); - assert.ok(md.includes('Critical')); - assert.ok(md.includes('CVE-2024-1')); - assert.ok(md.includes('❌')); - }); - - it('should show passing status when no critical/high findings', () => { - const summary = { - total: 1, - critical: 0, - high: 0, - medium: 1, - low: 0, - unknown: 0, - vulnerabilities: [ - { ruleId: 'r1', message: 'Medium issue', severity: Severity.Medium } - ] - }; - - const md = generateMarkdownSummary(summary, 'image', 'nginx:latest', false); - assert.ok(md.includes('✅')); - assert.ok(md.includes('Passed')); - }); - }); - - describe('generateNoFindingsSummary', () => { - it('should generate clean scan summary', () => { - const md = generateNoFindingsSummary('fs', '/src'); - assert.ok(md.includes('No vulnerabilities found')); - assert.ok(md.includes('Filesystem')); - assert.ok(md.includes('✅')); - }); - - it('should format image scan type correctly', () => { - const md = generateNoFindingsSummary('image', 'nginx:latest'); - assert.ok(md.includes('Container Image')); - }); - - it('should format model scan type correctly', () => { - const md = generateNoFindingsSummary('model', '/models/test.onnx'); - assert.ok(md.includes('AI Model')); - }); - }); -}); diff --git a/v2/action.yml b/v2/action.yml deleted file mode 100644 index 1e511a0b..00000000 --- a/v2/action.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: 'security-devops-action-v2' -description: 'Run Microsoft Defender for DevOps security scans.' -author: 'Microsoft' -branding: - icon: 'shield' - color: 'black' -inputs: - command: - description: 'The scan type to perform. Options: fs (filesystem), image (container image), model (AI model).' - default: 'fs' - fileSystemPath: - description: 'The filesystem path to scan. Used when command is fs.' - default: ${{ github.workspace }} - imageName: - description: 'The container image name to scan. Used when command is image. Example: nginx:latest' - modelPath: - description: 'The AI model path or URL to scan. Used when command is model. Supports local paths and http:// or https:// URLs.' - policy: - description: 'Policy to apply. Options: mdc (default), github, microsoft, azuredevops, none.' - default: 'mdc' - break: - description: 'If true, the action will fail the build when critical vulnerabilities are detected.' - default: 'false' - debug: - description: 'Enable debug logging for verbose output.' - default: 'false' - pr-summary: - description: 'Post a vulnerability summary to the GitHub Job Summary.' - default: 'true' - args: - description: 'Additional arguments to pass to the Defender CLI.' - tools: - description: 'A comma separated list of tools. Used for container-mapping backward compatibility.' -outputs: - sarifFile: - description: A file path to a SARIF results file. -runs: - using: 'node20' - main: '../lib/v2/defender-main.js' - pre: '../lib/v2/pre.js' - post: '../lib/v2/post.js'