Azure/Security-Copilot

API server

Microsoft Security Copilot is a generative AI-powered security solution that helps increase the efficiency and capabilities of defenders to improve security outcomes at machine speed and scale, while remaining compliant to responsible AI principles

AzureYAML, JSON, PowerShell, Python, Markdown648 starsScanned Oct. 14th, 2026at 79ee8eaView source

Trust score

60/100Low trust
Likely 36–84 · low confidence

Scanned Oct. 14th, 2026 at 79ee8ea against 26 rules · How scoring works

For developersOpen rawGET /c/Azure/Security-Copilot/report.json
At a glance

At a glance

Contains
2 API servers · 1 CLI · 9 tools
Its tools can
Use secrets and credentials 2Reach any network address 2Returns untrusted content 4Create or change data 2Read data 8
Issues flagged
2 high2 medium4 of 48 rule checks, severity as assessed for this repo
Where issues are
  • Repository-wide4 of 15
No issues in the other 3 of 4.
Trust signals
Verified publisherUnsigned build23 known CVEs in dependenciesNot actively maintained

What to fix first

4 issues, most severe first. Each one points to the exact code, with the recommended fix and a prompt you can paste into your coding agent.

  1. 1HighDependency Risk & Supply Chain
    Repository-wide · ATR-C0007
    The FunctionAppV2 requirements.txt uses open-ended version ranges ('azure-functions' with no version at all; 'tnefparse>=1.4.0', 'extract-msg>=0.30.0', etc.) with no lockfile pinning exact resolved versions, and GitHub Actions workflow dependencies float on mutable tags (actions/checkout@v4, github/codeql-action/init@v3, github/codeql-action/analyze@v3) rather than pinned SHAs -- flagged by Semgrep (yaml.github-actions.security.github-actions-mutable-action-tag, 3 findings in .github/workflows/codeql.yml) as a supply-chain risk.
        steps:
        - name: Checkout repository
          uses: actions/checkout@v4
    
        # Initializes the CodeQL tools for scanning.
        - name: Initialize CodeQL
          uses: github/codeql-action/init@v3
    Show 2 more locations
    on:
      push:
        branches: [ "main" ]
      pull_request:
        branches: [ "main" ]
      schedule:
        - cron: '38 6 * * 5'
    
    jobs:
      analyze:
    version: 2
    updates:
      - package-ecosystem: "" # See documentation for possible values
        directory: "/" # Location of package manifests
        schedule:
          interval: "weekly"
    Recommended fix

    Generate and commit a pip-compile/poetry lockfile pinning exact transitive versions; pin GitHub Actions to full commit SHAs per Semgrep's guidance; enable Dependabot cooldown.

  2. 2HighPath Traversal & Symlink Bypass (Filesystem Tools)
    Repository-wide · ATR-C0011
    The downloaded promptbook's 'name' field originates from the Security Copilot API response and is passed through a chain of string replacements that strip quote, parenthesis, and bracket characters but leave '..' sequences and path separators intact. The sanitized name is concatenated onto the fixed './Downloads/' prefix and written via Out-File, which resolves '../' segments in the resulting path, so a crafted promptbook name containing traversal sequences can cause the script to write a file outside the intended Downloads directory.
    Promptbook samples/Powershell to Manage Promptbooks/PromptbooksDownload.ps1:30–38
    $response = Invoke-WebRequest -Uri $url -Method GET -Headers $headers
    $jsonContent = $response.content
    $fromJson = $jsonContent | ConvertFrom-Json 
    
    Write-Host "Number of promptbooks Downloaded: $($fromJson.value.Count)" #$jsonContent
    
    # Iterate for each promptbook in JSON
    $folder = './Downloads/'
    foreach ($pb in $fromJson.value) {
    Show 2 more locations
    Promptbook samples/Powershell to Manage Promptbooks/PromptbooksDownload.ps1:41–49
            $fileName = $pb.name + ".json"
            # Remove all special chars from filename
            $fileName= $fileName.replace("'","")
            $fileName= $fileName.replace("(","")
            $fileName= $fileName.replace(")","")
            $fileName= $fileName.replace("[","-")
            $fileName= $fileName.replace("]","-")
            $fileName= $fileName.replace(' ','_') 
            $fileName = $folder + $fileName 
    Promptbook samples/Powershell to Manage Promptbooks/PromptbooksDownload.ps1:65–70
    
            $json = $pb | ConvertTo-Json
            #Write-Host $json
            "$json" | out-file -filepath $fileName #-encoding utf8NoBOM
    
            Write-Host "Written file $($fileName)"
    Recommended fix

    Strip or reject '..' sequences and path separators from the promptbook name (or resolve the final output path and verify it remains within the Downloads directory) before passing it to Out-File.

  3. 3MediumLack of Output Encoding/Sanitization
    Repository-wide · ATR-C0010
    The generate_html_report_functionapp endpoint builds its HTML report using a Jinja2 Template constructed from a raw triple-quoted string, so autoescape defaults to off, and the template interpolates values from the submitted analysis JSON via unescaped {{ value }} expressions. The companion Logic App's mailbox-triggered workflow invokes this function with parsed, attacker-influenced email-analysis content and forwards its rendered output as an outbound HTML report, so content embedded in a reported phishing email (e.g. '<script>' markup) can reach the unescaped template interpolation and be reflected as live markup in the generated report.
    Plugins/Published Plugins/Netskope-Copilot-Plugin/CoPilotOpenAPISpec.yaml:160–12260
        Response:
          type: object
          properties:
            status:
              type: string
              enum:
                - success
                - error
            data:
              oneOf:
                - $ref: '#/components/schemas/Forensics'
    Show 2 more locations
    Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingV2/FunctionAppV2.zip::functions/html_report/generator.py:1–17
    import logging
    from jinja2 import Template
    from urllib.parse import unquote
    import azure.functions as func
    
    logger = logging.getLogger(__name__)
    
    CATEGORY_STYLES = {
        "PHISHING": "background-color: #d9534f; ...",
        ...
    }
    
    def create_html(json_data):
        html_template = '''
    …
    Plugins/Published Plugins/Netskope-Copilot-Plugin/CoPilotOpenAPISpec.yaml:165–12260
        Response:
          type: object
          properties:
            status:
                return func.HttpResponse(... status_code=400)
              enum:
                - success
            return func.HttpResponse(html_report, status_code=200, mimetype="text/html", ...)
    Recommended fix

    Use jinja2.Environment(autoescape=True) or select_autoescape() instead of the bare Template() class, and apply the |e escape filter to every interpolated value sourced from the analysis JSON before rendering.

  4. 4MediumDenial of Wallet / Cost Controls
    Repository-wide · ATR-C0012
    The FunctionAppV2 ARM template explicitly provisions the Y1/Dynamic consumption hosting plan, and the companion Logic App polls a shared mailbox, triggering a metered run -- including attachment parsing and Function invocations -- for each arriving email. No repository-defined per-sender quota, rate limit, or budget gate exists on this mailbox-triggered path, so an attacker able to send email to the monitored mailbox can drive consumption-based billing at volume.
    Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingV2/logicapp_azuredeploy.json:180–203
                        "triggers": {
                            "When_a_new_email_arrives_in_a_shared_mailbox_(V2)": {
                                "recurrence": {
                                    "interval": 3,
                                    "frequency": "Minute"
                                },
                                "evaluatedRecurrence": {
                                    "interval": 3,
                                    "frequency": "Minute"
                                },
                                "splitOn": "@triggerBody()?['value']",
                                "type": "ApiConnection",
                                "inputs": {
                                    "host": {
    …
    Show 1 more location
    Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingV2/functionapp_azuredeploy.json:33–48
                "type": "Microsoft.Web/serverfarms",
                "apiVersion": "2018-02-01",
                "name": "[variables('FunctionAppName')]",
                "location": "[resourceGroup().location]",
                "sku": {
                    "name": "Y1",
                    "tier": "Dynamic"
                },
                "kind": "functionapp,linux",
                "properties": {
                    "name": "[variables('FunctionAppName')]",
                    "workerSize": "0",
                    "workerSizeId": "0",
                    "numberOfWorkers": "1",
    …
    Recommended fix

    Add a per-sender or per-time-window rate limit and a budget alert in front of the mailbox-triggered workflow, and consider a hosting plan with a fixed cost ceiling to bound consumption-driven billing exposure.

What’s inside

BlueRock found 3 components in this repository. Each is checked against the rules for its type.

API serverapi-server/Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingLogic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsing100High trustNone flagged

Its tools can

Reach any network address 1Returns untrusted content 3Read data 3

What stood out

  • This repo is NOT an MCP server / agentic-AI codebase: it is Microsoft's public Security Copilot community content repository (plugin manifests, Logic App ARM templates, Azure Function Apps, promptbook samples, workshop material). All ATR-M (mcp-server) rules are NOT_APPLICABLE -- no MCP server, stdio/SSE/streamable-HTTP transport, or tool registry exists anywhere in the codebase.
  • Two Azure Function Apps (bundled as binary zip deployment artifacts under 'Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsing(V2)') parse untrusted, attacker-influenced phishing-report email content and contain a plausible SSRF primitive: expand_url()/UrlProcessor.expand_url() follows HTTP redirects on URLs extracted from email bodies, gated only by a non-authoritative domain-substring match against known URL-shortener providers (no RFC1918/loopback/metadata-IP denylist). Because this source exists only inside committed binary .zip artifacts rather than plain-text repo files, several related findings (SSRF, DoS via unbounded max_depth, logging hygiene, denial-of-wallet) were adjudicated as UNKNOWN rather than VULNERABLE per the evidence-citation contract, but the underlying code pattern is worth a human follow-up review.
  • generate_html_report_functionapp (FunctionAppV2) renders attacker-controlled JSON directly into an HTML report using a bare Jinja2 Template() with autoescape OFF and no per-value escaping -- a stored/reflected XSS vector for anyone viewing the generated phishing-analysis report.
  • Supply-chain hygiene gaps: FunctionAppV2's requirements.txt uses open-ended '>=' version floors with no lockfile; GitHub Actions workflow (.github/workflows/codeql.yml) pins third-party actions to mutable tags (@v3/@v4) instead of commit SHAs (flagged by Semgrep); Dependabot config has no cooldown period and an empty package-ecosystem value.
  • No MCP server, agent framework, or LLM tool-use component of any kind was found in this repository -- it is a plugin/documentation/IaC repo for Microsoft's first-party Security Copilot product, not an agentic AI system under the MCP threat model this ruleset primarily targets.
  • No evidence of weaponized/offensive-purpose content: all security-adjacent plugins (ransomware-signs detection, AiTM phishing detection, threat-intel lookups) are defensive/detective in framing and function; no malware-builder, credential-harvester, or C2 tooling exists.

MCP server rules 0 of 11 flagged

11 did not applyShow
Does not applyOAuth 2.1 + PKCE Correctness (MCP Authorization)ATR-M0005Details

Why it got this result

No MCP server component exists in this repository.

Why it matters

Misconfig enables token theft and confused-deputy attacks.

Does not applyNo Authentication or Authorization Checks (General)ATR-M0010Details

Why it got this result

No MCP server component exists in this repository.

Why it matters

Prevents unauthorized tool use and data access.

Does not applyOAuth Proxy Per-Client Consent (Confused Deputy Defense)ATR-M0011Details

Why it got this result

No MCP server / OAuth proxy component exists in this repository.

Why it matters

Shared upstream client + dynamic MCP-client registration + no per-client consent enables attacker-registered MCP clients to receive bridge codes carrying victim upstream tokens — the documented MCP Confused Deputy attack.

Does not applyTool Definition Integrity & Versioning (ETDI-style)ATR-M0001Details

Why it got this result

No MCP server component exists in this repository; attached only to satisfy per-component reporting. Repo-wide grep for MCP registration patterns found no matches.

Why it matters

Prevents rug-pull tool poisoning.

Does not applyUnrestricted Tool Permissions (Overprivileged Tools)ATR-M0002Details

Why it got this result

No MCP server component exists in this repository.

Why it matters

Limits blast radius and privilege escalation.

Does not applyCORS / Origin / Host Validation (DNS Rebinding Defense)ATR-M0003Details

Why it got this result

No MCP server (stdio/SSE/streamable-HTTP) component exists in this repository.

Why it matters

Prevents DNS rebinding/browser hijack of local or remote MCP.

Does not applyBinding to 0.0.0.0 Without Auth/TLSATR-M0004Details

Why it got this result

No MCP server component exists in this repository; Azure Function Apps use the managed Functions host, not a self-managed listener.

Why it matters

LAN/Internet exposure enables remote exploitation.

Does not applyCORS Misuse: Wildcard with CredentialsATR-M0008Details

Why it got this result

No MCP server component exists in this repository.

Why it matters

Prevents credentialed cross-origin abuse.

Does not applyTransport Security: TLS Everywhere (Remote)ATR-M0009Details

Why it got this result

This component is an Azure Function App, not an MCP server, and it exposes no remote MCP transport (SSE or streamable-HTTP) for ATR-M0009's TLS-everywhere requirement to apply to. The managed HTTPS endpoint Azure provides is unrelated to the MCP-specific remote-transport TLS obligation this rule targets, so NOT_APPLICABLE reflects the rule's applicability precondition rather than an assessment of TLS configuration quality.

Why it matters

MitM can steal tokens and tamper responses.

Does not applyTool Name Collision / SpoofingATR-M0006Details

Why it got this result

No MCP server/tool registry exists in this repository.

Why it matters

Prevents malicious tool shadowing.

Does not applyPrompt/Metadata Injection in Tool DescriptionsATR-M0007Details

Why it got this result

No MCP server/tool-definition registry exists in this repository.

Why it matters

Prevents covert model manipulation.

Show its 3 tools
ToolIt can
parse_email (FunctionApp v1)
HTTP endpoint (parse_email/__init__.py) that parses a raw email, extracts URLs/IPs/domains, decodes SafeLinks/Proofpoint URLs, and expands shortened URLs via outbound HTTP HEAD requests.
Read dataReach any network addressReturns untrusted content
parse_pdf
HTTP endpoint that decodes a base64-encoded PDF and extracts/cleans its text via pdfminer.
Read dataReturns untrusted content
parse_virustotal_json
HTTP endpoint that parses a VirusTotal API JSON response and extracts malicious/suspicious engine results.
Read dataReturns untrusted content

Before you put it to work

  1. Extract and commit the Azure Function App source code as plain text (not only inside binary .zip blobs) so it can be properly code-reviewed and so future SAST/security scans can analyze it directly.
  2. Add IP-class validation (reject RFC1918/loopback/link-local/169.254.169.254) before following any URL extracted from untrusted email content in expand_url()/UrlProcessor.expand_url(), and re-validate on every redirect hop.
  3. Switch generate_html_report_functionapp's Jinja2 Template() to an Environment with autoescape=True (or select_autoescape()) to prevent HTML/script injection into generated phishing-analysis reports.
  4. Pin GitHub Actions to full commit SHAs instead of mutable version tags; add a Dependabot cooldown period; generate and commit a Python lockfile for the Function App's dependencies.
  5. Clamp the caller-controllable 'max_depth' parameter in parse_email_functionapp to a fixed, small ceiling to prevent unbounded-recursion resource exhaustion.
API serverapi-server/Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingV2Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingV289High trustNone flagged

Its tools can

Reach any network address 1Returns untrusted content 1Read data 4

What stood out

  • This repo is NOT an MCP server / agentic-AI codebase: it is Microsoft's public Security Copilot community content repository (plugin manifests, Logic App ARM templates, Azure Function Apps, promptbook samples, workshop material). All ATR-M (mcp-server) rules are NOT_APPLICABLE -- no MCP server, stdio/SSE/streamable-HTTP transport, or tool registry exists anywhere in the codebase.
  • Two Azure Function Apps (bundled as binary zip deployment artifacts under 'Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsing(V2)') parse untrusted, attacker-influenced phishing-report email content and contain a plausible SSRF primitive: expand_url()/UrlProcessor.expand_url() follows HTTP redirects on URLs extracted from email bodies, gated only by a non-authoritative domain-substring match against known URL-shortener providers (no RFC1918/loopback/metadata-IP denylist). Because this source exists only inside committed binary .zip artifacts rather than plain-text repo files, several related findings (SSRF, DoS via unbounded max_depth, logging hygiene, denial-of-wallet) were adjudicated as UNKNOWN rather than VULNERABLE per the evidence-citation contract, but the underlying code pattern is worth a human follow-up review.
  • generate_html_report_functionapp (FunctionAppV2) renders attacker-controlled JSON directly into an HTML report using a bare Jinja2 Template() with autoescape OFF and no per-value escaping -- a stored/reflected XSS vector for anyone viewing the generated phishing-analysis report.
  • Supply-chain hygiene gaps: FunctionAppV2's requirements.txt uses open-ended '>=' version floors with no lockfile; GitHub Actions workflow (.github/workflows/codeql.yml) pins third-party actions to mutable tags (@v3/@v4) instead of commit SHAs (flagged by Semgrep); Dependabot config has no cooldown period and an empty package-ecosystem value.
  • No MCP server, agent framework, or LLM tool-use component of any kind was found in this repository -- it is a plugin/documentation/IaC repo for Microsoft's first-party Security Copilot product, not an agentic AI system under the MCP threat model this ruleset primarily targets.
  • No evidence of weaponized/offensive-purpose content: all security-adjacent plugins (ransomware-signs detection, AiTM phishing detection, threat-intel lookups) are defensive/detective in framing and function; no malware-builder, credential-harvester, or C2 tooling exists.

MCP server rules 0 of 11 flagged

11 did not applyShow
Does not applyOAuth 2.1 + PKCE Correctness (MCP Authorization)ATR-M0005Details

Why it got this result

No MCP server component exists in this repository to evaluate MCP-specific OAuth 2.1/PKCE requirements against.

Why it matters

Misconfig enables token theft and confused-deputy attacks.

Does not applyNo Authentication or Authorization Checks (General)ATR-M0010Details

Why it got this result

No MCP server component exists in this repository to evaluate MCP tool/resource authn/authz gating against.

Why it matters

Prevents unauthorized tool use and data access.

Does not applyOAuth Proxy Per-Client Consent (Confused Deputy Defense)ATR-M0011Details

Why it got this result

No MCP server / OAuth proxy component exists in this repository; the rule's applicability precondition (an OAuth proxy forwarding to a distinct upstream IdP on behalf of dynamically-registered MCP clients) is not met.

Why it matters

Shared upstream client + dynamic MCP-client registration + no per-client consent enables attacker-registered MCP clients to receive bridge codes carrying victim upstream tokens — the documented MCP Confused Deputy attack.

Does not applyTool Definition Integrity & Versioning (ETDI-style)ATR-M0001Details

Why it got this result

Repo-wide search (grep for FastMCP, server.tool, @mcp.tool, registerTool, add_tool, McpServer, stdio/SSE/streamable-HTTP transport patterns) found zero MCP server implementations anywhere in the repository. This repo is a collection of Microsoft Security Copilot plugin manifests (OpenAPI/YAML), Logic App ARM templates, Azure Function Apps, and documentation/workshop material -- not an MCP server codebase. No mcp-server component exists to evaluate this rule against; attached here only to satisfy the reporting schema's component-scoping requirement.

Why it matters

Prevents rug-pull tool poisoning.

Does not applyUnrestricted Tool Permissions (Overprivileged Tools)ATR-M0002Details

Why it got this result

No MCP server component exists in this repository (verified via repo-wide grep for MCP registration patterns); this rule's mcp-server applicability has no target.

Why it matters

Limits blast radius and privilege escalation.

Does not applyCORS / Origin / Host Validation (DNS Rebinding Defense)ATR-M0003Details

Why it got this result

No MCP server (stdio/SSE/streamable-HTTP) component exists in this repository. The repo contains Security Copilot plugin manifests and Logic App/Function App deployment templates, none of which are an MCP server listener.

Why it matters

Prevents DNS rebinding/browser hijack of local or remote MCP.

Does not applyBinding to 0.0.0.0 Without Auth/TLSATR-M0004Details

Why it got this result

No MCP server component exists in this repository; there is no server bind/listen code to evaluate (Azure Function Apps use the managed Azure Functions host, not a self-managed listener).

Why it matters

LAN/Internet exposure enables remote exploitation.

Does not applyCORS Misuse: Wildcard with CredentialsATR-M0008Details

Why it got this result

No MCP server component exists in this repository; no CORS configuration code exists to evaluate under the MCP remote-transport threat model.

Why it matters

Prevents credentialed cross-origin abuse.

Does not applyTransport Security: TLS Everywhere (Remote)ATR-M0009Details

Why it got this result

This component is an Azure Function App enforcing HTTPS-only access and TLS 1.2, but it is not an MCP server and exposes no remote MCP transport for ATR-M0009's TLS-everywhere requirement to apply to. The rule's applicability precondition -- a remote MCP transport -- is absent, so NOT_APPLICABLE reflects scope rather than an assessment of the Function App's TLS configuration.

Why it matters

MitM can steal tokens and tamper responses.

Does not applyTool Name Collision / SpoofingATR-M0006Details

Why it got this result

No MCP server/tool registry exists in this repository; the 'tools' present (Security Copilot plugins) are OpenAPI-based skills consumed by a closed first-party orchestrator, not MCP tools subject to cross-server name collision.

Why it matters

Prevents malicious tool shadowing.

Does not applyPrompt/Metadata Injection in Tool DescriptionsATR-M0007Details

Why it got this result

No MCP server/tool-definition registry exists in this repository. Plugin YAML descriptions were spot-checked for hidden directives/control characters/RLO-LRO and none were found; this is a distinct surface from MCP tool manifests and the rule's MCP-tool-definition threat model does not map onto Security Copilot plugin manifests.

Why it matters

Prevents covert model manipulation.

Show its 4 tools
ToolIt can
clean_json_functionapp
HTTP endpoint that cleans/normalizes LLM-produced JSON text (strips markdown fences, replaces null with 'None').
Read data
extract_regex_functionapp
HTTP endpoint that runs a caller-supplied regex pattern against a caller-supplied subject string and returns the match.
Read data
generate_html_report_functionapp
HTTP endpoint that renders a Jinja2 HTML report from a submitted JSON phishing-analysis object.
Read data
parse_email_functionapp
HTTP endpoint that parses raw email content (or base64-encoded email) to extract headers, body, attachments, URLs, IPs, and domains; expands shortened URLs via outbound HTTP requests.
Read dataReach any network addressReturns untrusted content

Before you put it to work

  1. Extract and commit the Azure Function App source code as plain text (not only inside binary .zip blobs) so it can be properly code-reviewed and so future SAST/security scans can analyze it directly.
  2. Add IP-class validation (reject RFC1918/loopback/link-local/169.254.169.254) before following any URL extracted from untrusted email content in expand_url()/UrlProcessor.expand_url(), and re-validate on every redirect hop.
  3. Switch generate_html_report_functionapp's Jinja2 Template() to an Environment with autoescape=True (or select_autoescape()) to prevent HTML/script injection into generated phishing-analysis reports.
  4. Pin GitHub Actions to full commit SHAs instead of mutable version tags; add a Dependabot cooldown period; generate and commit a Python lockfile for the Function App's dependencies.
  5. Clamp the caller-controllable 'max_depth' parameter in parse_email_functionapp to a fixed, small ceiling to prevent unbounded-recursion resource exhaustion.
CLIcli/Promptbook samples/Powershell to Manage PromptbooksPromptbook samples/Powershell to Manage Promptbooks82Moderate trustNone flagged

Its tools can

Use secrets and credentials 2Create or change data 2Read data 1

What stood out

  • This repo is NOT an MCP server / agentic-AI codebase: it is Microsoft's public Security Copilot community content repository (plugin manifests, Logic App ARM templates, Azure Function Apps, promptbook samples, workshop material). All ATR-M (mcp-server) rules are NOT_APPLICABLE -- no MCP server, stdio/SSE/streamable-HTTP transport, or tool registry exists anywhere in the codebase.
  • Two Azure Function Apps (bundled as binary zip deployment artifacts under 'Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsing(V2)') parse untrusted, attacker-influenced phishing-report email content and contain a plausible SSRF primitive: expand_url()/UrlProcessor.expand_url() follows HTTP redirects on URLs extracted from email bodies, gated only by a non-authoritative domain-substring match against known URL-shortener providers (no RFC1918/loopback/metadata-IP denylist). Because this source exists only inside committed binary .zip artifacts rather than plain-text repo files, several related findings (SSRF, DoS via unbounded max_depth, logging hygiene, denial-of-wallet) were adjudicated as UNKNOWN rather than VULNERABLE per the evidence-citation contract, but the underlying code pattern is worth a human follow-up review.
  • generate_html_report_functionapp (FunctionAppV2) renders attacker-controlled JSON directly into an HTML report using a bare Jinja2 Template() with autoescape OFF and no per-value escaping -- a stored/reflected XSS vector for anyone viewing the generated phishing-analysis report.
  • Supply-chain hygiene gaps: FunctionAppV2's requirements.txt uses open-ended '>=' version floors with no lockfile; GitHub Actions workflow (.github/workflows/codeql.yml) pins third-party actions to mutable tags (@v3/@v4) instead of commit SHAs (flagged by Semgrep); Dependabot config has no cooldown period and an empty package-ecosystem value.
  • No MCP server, agent framework, or LLM tool-use component of any kind was found in this repository -- it is a plugin/documentation/IaC repo for Microsoft's first-party Security Copilot product, not an agentic AI system under the MCP threat model this ruleset primarily targets.
  • No evidence of weaponized/offensive-purpose content: all security-adjacent plugins (ransomware-signs detection, AiTM phishing detection, threat-intel lookups) are defensive/detective in framing and function; no malware-builder, credential-harvester, or C2 tooling exists.

MCP server rules 0 of 11 flagged

11 did not applyShow
Does not applyOAuth 2.1 + PKCE Correctness (MCP Authorization)ATR-M0005Details

Why it got this result

This is not an MCP server; it is a CLI script that uses an operator-supplied bearer token copied manually from the browser, outside of any OAuth/PKCE flow implemented by this code.

Why it matters

Misconfig enables token theft and confused-deputy attacks.

Does not applyNo Authentication or Authorization Checks (General)ATR-M0010Details

Why it got this result

This is not a server exposing tools/resources to remote callers; it is a local script that itself authenticates outbound to the Security Copilot API using an operator-supplied bearer token. There is no listener for this rule to gate.

Why it matters

Prevents unauthorized tool use and data access.

Does not applyOAuth Proxy Per-Client Consent (Confused Deputy Defense)ATR-M0011Details

Why it got this result

This component is not an OAuth proxy; it does not forward to an upstream IdP on behalf of dynamically-registered MCP clients.

Why it matters

Shared upstream client + dynamic MCP-client registration + no per-client consent enables attacker-registered MCP clients to receive bridge codes carrying victim upstream tokens — the documented MCP Confused Deputy attack.

Does not applyTool Definition Integrity & Versioning (ETDI-style)ATR-M0001Details

Why it got this result

This component is a pair of local, operator-run PowerShell CLI scripts, not an MCP server; there is no tool manifest/registry to version or sign.

Why it matters

Prevents rug-pull tool poisoning.

Does not applyUnrestricted Tool Permissions (Overprivileged Tools)ATR-M0002Details

Why it got this result

No MCP tool registry/capability model exists; this is a local script invoked directly by an operator with their own credentials.

Why it matters

Limits blast radius and privilege escalation.

Does not applyCORS / Origin / Host Validation (DNS Rebinding Defense)ATR-M0003Details

Why it got this result

No listener/remote transport exists; this is an outbound-only CLI script.

Why it matters

Prevents DNS rebinding/browser hijack of local or remote MCP.

Does not applyBinding to 0.0.0.0 Without Auth/TLSATR-M0004Details

Why it got this result

No listener exists in this component; it only makes outbound HTTPS calls.

Why it matters

LAN/Internet exposure enables remote exploitation.

Does not applyCORS Misuse: Wildcard with CredentialsATR-M0008Details

Why it got this result

No listener/CORS configuration exists in this outbound-only CLI script.

Why it matters

Prevents credentialed cross-origin abuse.

Does not applyTransport Security: TLS Everywhere (Remote)ATR-M0009Details

Why it got this result

This component is an operator-run PowerShell CLI script that makes outbound HTTPS requests to a fixed Security Copilot API URL. It exposes no remote MCP server transport (SSE or streamable-HTTP) for ATR-M0009's TLS-everywhere requirement to gate against. The rule's applicability precondition -- a remote MCP transport -- is not met, so NOT_APPLICABLE is the correct verdict rather than an evaluation of the script's outbound TLS usage.

Why it matters

MitM can steal tokens and tamper responses.

Evidence

Promptbook samples/Powershell to Manage Promptbooks/PromptbooksDownload.ps1:19–19
$url = "https://api.securitycopilot.microsoft.com/geo/eastus/promptbooks"
Does not applyTool Name Collision / SpoofingATR-M0006Details

Why it got this result

No MCP tool registry exists for this local CLI script pair.

Why it matters

Prevents malicious tool shadowing.

Does not applyPrompt/Metadata Injection in Tool DescriptionsATR-M0007Details

Why it got this result

No MCP tool definitions/descriptions exist for this component.

Why it matters

Prevents covert model manipulation.

Show its 2 tools
ToolIt can
PromptbooksDownload.ps1
PowerShell script: downloads all Security Copilot Promptbooks for a tenant via GET using an operator-supplied bearer token, strips identifying fields, writes each as a local JSON file.
Read dataCreate or change dataUse secrets and credentials
PromptbooksUpload.ps1
PowerShell script: uploads every JSON file under ./Uploads to the Security Copilot Promptbooks API via POST using an operator-supplied bearer token.
Create or change dataUse secrets and credentials

Before you put it to work

  1. Extract and commit the Azure Function App source code as plain text (not only inside binary .zip blobs) so it can be properly code-reviewed and so future SAST/security scans can analyze it directly.
  2. Add IP-class validation (reject RFC1918/loopback/link-local/169.254.169.254) before following any URL extracted from untrusted email content in expand_url()/UrlProcessor.expand_url(), and re-validate on every redirect hop.
  3. Switch generate_html_report_functionapp's Jinja2 Template() to an Environment with autoescape=True (or select_autoescape()) to prevent HTML/script injection into generated phishing-analysis reports.
  4. Pin GitHub Actions to full commit SHAs instead of mutable version tags; add a Dependabot cooldown period; generate and commit a Python lockfile for the Function App's dependencies.
  5. Clamp the caller-controllable 'max_depth' parameter in parse_email_functionapp to a fixed, small ceiling to prevent unbounded-recursion resource exhaustion.

Repository-wide rules

Checked once for the whole repository: dependencies, secrets, inputs, networking, CI and more. 4 of 15 flagged.

Could not determineRuntime Command Injection via Process ExecutionATR-C0003Details

Why it got this result

Manual archive extraction found only a single subprocess call in application code: a fixed-argv invocation of 'antiword' with a locally generated temp-file path as its only argument, with no shell=True and no attacker-controlled command text reaching the sink; no eval()/Function()/dynamic-code-execution pattern was found elsewhere. However, this code exists only inside a committed binary ZIP deployment artifact and is not present as an extracted, plain-text file at a stable repository path, so the required source citation cannot be independently re-opened and re-verified at that path. Given this unresolved source-attestation gap, the verdict is UNKNOWN rather than SECURE.

Why it matters

Unsanitized command construction in shipped runtime code can give a remote attacker arbitrary code execution with the application's privileges.

Evidence

Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingV2/FunctionAppV2.zip::extractors/word_extractor.py:100–110
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
    temp_file.write(word_data)
    temp_file_path = temp_file.name
try:
    result = subprocess.run(
        ["antiword", temp_file_path],
        capture_output=True,
        text=True,
        check=True
    )
Could not determineUnrestricted Network Fetch / SSRFATR-C0001Details

Why it got this result

Manual review (via python zipfile extraction, not committed as extracted source) of FunctionApp.zip's parse_email/__init__.py shows an expand_url() helper that calls requests.Session().head(url, allow_redirects=True) on email-derived URLs gated only by a hardcoded shortener-domain allowlist (no private-IP/loopback/metadata denylist), which looks like a plausible SSRF primitive. However, this code exists ONLY inside a committed binary zip deployment artifact (FunctionApp.zip) and is not present as an extracted/plain-text file anywhere in the repository tree, so it cannot be cited with a repo-relative path + line-number snippet that is independently verifiable by opening that path in the repo (the actual repo file at that path is binary zip bytes, not the Python source). Per the evidence contract, required_evidence classes could not be established against a verifiable repo-relative source file. Abstaining rather than asserting VULNERABLE against unverifiable evidence.

Why it matters

Enables internal pivoting and data exfiltration.

Could not determineMissing or Inadequate Input ValidationATR-C0008Details

Why it got this result

Archive review confirms that the extract_regex_functionapp endpoint (functions/regex_extractor/extractor.py) accepts a caller-supplied pattern and subject from the request JSON and calls Python's re.search(pattern, subject) with no length or complexity limit, a plausible ReDoS primitive given Python re's susceptibility to catastrophic backtracking. However, the only repository-visible invocation of this function, from the deployed Logic App, supplies a fixed regex pattern and a trigger-derived subject, and direct endpoint access requires a Function-level key; whether an external attacker can control the pattern or obtain a valid function key is not established anywhere in the repository. This unresolved trigger-capability gap, not any unreviewability of the ZIP contents, is why the verdict remains UNKNOWN.

Why it matters

Frontline control to prevent multiple classes of vulns.

Affected tools

extract_regex_functionapp
Could not determineDoS Protections: Timeouts, Concurrency, Streaming LimitsATR-C0004Details

Why it got this result

Manual extraction of FunctionAppV2.zip shows function_app.py accepting a caller-supplied 'max_depth' query parameter with no upper-bound clamp, passed into parsers/email_parser.py's recursive parse_email(), and no visible request-body-size limit in host.json. This looks like a plausible unbounded-recursion/resource-exhaustion vector, but the code exists only inside a committed binary zip deployment artifact, not as an extracted plain-text file at a reviewable repo path -- so a reviewer opening the cited path sees zip bytes, not the Python source quoted in evidence. Abstaining to UNKNOWN rather than asserting VULNERABLE against unverifiable evidence.

Why it matters

Resource exhaustion can crash or stall MCP.

Could not determineLogging Hygiene (No Secrets/PII/Tokens) & Audit TrailATR-C0005Details

Why it got this result

The Function App code (committed only as binary FunctionApp.zip / FunctionAppV2.zip, not extracted plain text) uses Python logging extensively with DEBUG-level statements that could log request bodies/content in some paths (e.g. parse_email/__init__.py logs 'Raw request body (as bytes)' truncated to 100 chars, and various functions log full decoded content at DEBUG level). host.json sets default logLevel to 'Debug' for both Function Apps, which in production would surface verbose content (including potentially PII from parsed phishing emails) into Application Insights logs. However, because this code exists only inside committed zip binaries and not as reviewable plain-text repo files, a definitive source-to-sink citation at a stable repo path/line cannot be established per the evidence contract. The PowerShell Promptbook scripts avoid logging the bearer token itself (Write-Host calls never echo $token or $headers). Abstaining on the Function App logging question specifically due to the binary-artifact citation issue; the PowerShell scripts alone would be SECURE but are evaluated together with the Function Apps for this repo-level rule.

Why it matters

Prevents sensitive data leakage; enables forensics.

Affected tools

parse_email_functionappparse_email (FunctionApp v1)
4 rules passed · 2 did not applyShow
PassedWeaponization: Malicious-Purpose Artifact (Offensive Capability by Design)ATR-R0001Details

Why it got this result

This repository is entirely composed of defensive/detective security-operations content: Microsoft Security Copilot plugin manifests for threat intelligence lookups (CrowdSec, GreyNoise, HaveIBeenPwned), KQL hunting-query plugins for detecting ransomware activity signs, phishing/AiTM detection, anomaly detection, and incident investigation; Logic App automation for triage/reporting; and promptbook samples for SOC analyst workflows. Manual review of the 'Ransomware Signs' plugin (hunts for taskkill.exe/cipher.exe/wevtutil indicators to DETECT ransomware activity, not perform it), the QR-code AiTM phishing DETECTION plugin, and the GEO-IP workshop's 'script_decode.txt' (a benign training exercise asking students to decode and analyze a hex-encoded PowerShell snippet as a SOC exercise, explicitly framed as 'suspicious script analysis' for a defender audience, not an operational payload) found no artifact whose declared purpose is to generate or perform offensive capability. No ransomware-builder, credential-harvester, C2/loader, or mass-abuse tooling exists; all offensive-adjacent content is framed and used for detection/defense/training.

Why it matters

An artifact whose purpose is to weaponize the agent -- to produce malware, steal credentials, evade defenses, or run attacks at scale -- is dangerous regardless of how securely it is built; posture controls (auth, input validation, least privilege) cannot make a weapon safe, which is why a confirmed weaponization finding caps the trust verdict to `blocked` rather than being averaged against a good posture score. Catching malicious PURPOSE is a distinct safety control, and it must be conservative because offensive capability is dual-use: legitimate pentest, CTF, defensive, and research work must abstain (UNKNOWN), never block -- unless the artifact itself hides from the people who install, run or audit it.

Evidence

Plugins/Community Based Plugins/Microsoft Defender XDR Custom Plugin Scenarios/Ransomware Signs/Readme.md:1–11
### Signs of ransomware activity Custom Plugin

Welcome to the Signs of ransomware activity custom plugin! Here, you'll discover a Skills that looking up signs of ransomware activity in Defender Advanced Hunting that create queries that locate individual artifacts associated with ransomware activity...
Show 1 more location
Technical Workshops/Custom Plugin Workshop/Task04_GEO_IP_script/readme.md:48–61
####  Use case
You are Soc Analyst in woodgrove Domain, During an endpoint Incident investigation, you found suspicious script that run on your DMZ server, your job is to analyze this script, and share your final to your team member.
Your task is:
1. Input this information about the script from the script_decode.txt and ask Copilot to decode it
2. Extract only the IPs from this script.
3. Employ the MDTI reputation score to assess these Ip.
PassedStatic SecretsATR-C0002Details

Why it got this result

Gitleaks scan (0 findings) plus manual grep for API_KEY/SECRET/TOKEN/PASSWORD literal assignments across YAML/JSON/PS1/Python files found no committed secret VALUES -- only schema/property names (e.g. 'apiAccessToken', 'token:' as an OpenAPI schema field name in Netskope/CrowdSec plugin specs) and placeholder example URLs (https://example.com). The PromptBooks PowerShell scripts require the operator to pass a bearer token as a command-line parameter (-token) copied manually from browser devtools each run; no token is hardcoded or persisted in the repo. No .env files with real values, no AKIA/ghp_/sk- patterns found.

Why it matters

Plaintext secrets are easily exfiltrated.

Evidence

Plugins/Published Plugins/CrowdSecTI/crowdsecTI_smoke.yaml:62–68
      security:
        - api_key: []
  
components:
  securitySchemes:
    api_key:
      type: "apiKey"
      name: "x-api-key"
      in: "header"
PassedInsecure DeserializationATR-C0009Details

Why it got this result

No pickle.loads, yaml.load() (unsafe), marshal.load, or other unsafe deserializer was found in any Python code across both Function App zip archives (grepped across all extracted .py files excluding vendored site-packages). All JSON handling uses the standard json.loads/json.dumps (safe), and YAML files in the repo (plugin manifests) are static configuration consumed by Security Copilot's own backend, not deserialized by application code in this repo.

Why it matters

Can lead to arbitrary code execution.

Evidence

Logic Apps/SecCopilot-UserReportedPhishing-FuncApp_parsingV2/FunctionAppV2.zip::functions/json_cleaner/cleaner.py:1–5
import json
import logging
import re
import azure.functions as func
PassedCI/CD Workflow Script InjectionATR-C0014Details

Why it got this result

The only workflow in this repo, .github/workflows/codeql.yml, has a single 'run:' block (lines 78-83) that is a static, hardcoded multi-line echo/message with NO interpolation of any github.event.*, github.head_ref, inputs.*, or needs.* expression -- it only prints fixed instructional text about manual build steps. Triggers are push/pull_request (both scoped to the 'main' branch) and a weekly schedule; none of pull_request_target, issue_comment, workflow_run, or repository_dispatch are used. The only '${{ }}' expressions in the file (matrix.language, matrix.build-mode) are workflow-internal matrix values defined in the same file, not attacker-controlled external input, and none of them reach a shell run block -- they are consumed only as 'with:' action inputs (languages:, build-mode:, category:) to the github/codeql-action, not re-parsed by a shell.

Why it matters

CI/CD script injection can execute attacker-controlled commands with workflow tokens, repository secrets, trusted-runner access, artifact or cache authority, and release or deployment privileges.

Evidence

    - if: matrix.build-mode == 'manual'
      run: |
        echo 'If you are using a "manual" build mode for one or more of the' \
          'languages you are analyzing, replace this with the commands to build' \
          'your code, for example:'
        echo '  make bootstrap'
        echo '  make release'
        exit 1
Show 1 more location
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]
  schedule:
    - cron: '38 6 * * 5'
Does not applyTool Output Sanitization & Injection ContainmentATR-C0013Details

Why it got this result

This rule targets MCP tool-output handling that gets concatenated into an LLM's context window (envelope/untrustedReturn annotation, control-character stripping before injection into a model prompt). No MCP server or LLM-tool-calling host exists in this repository; the Function Apps here return data to a Logic App orchestration / Azure consumer, not into an LLM context window managed by this codebase. The HTML-report-generation XSS issue (ATR-C0010) is a related but distinct rendering-sanitization concern already captured under its own rule.

Why it matters

Tool outputs are a primary prompt-injection vector. Third-party content (web pages, files, API responses) may contain text crafted to override instructions or impersonate system/tool messages. Without a host-enforced envelope, sanitization, and an explicit untrusted-return signal, the model can be coerced into executing attacker-controlled directives, leaking secrets, or chaining further tool calls on behalf of the adversary.

Does not applyContainer/Runtime HardeningATR-C0006Details

Why it got this result

No Dockerfile, docker-compose, Helm chart, or Kubernetes manifest exists anywhere in this repository (confirmed via repo-wide find for Dockerfile*/docker-compose*/*.tf and directory listing). Deployment artifacts present are Azure ARM/Logic App JSON templates and Azure Function App zip packages, which run on Azure's managed PaaS compute (Functions/Logic Apps/App Service) rather than a repo-controlled container image, so there is no container hardening surface (USER, capabilities, seccomp, hostNetwork) for this rule to evaluate.

Why it matters

Reduces blast radius of exploits.

From build to runtime

You know what it is. Now see what it does.

A scan helps you understand what you’re building with. When your agent runs, BlueRock shows you what actually happens across tools and systems. Follow actions in context, understand behavior as it unfolds and add controls when you need them.

BUILDRegistry + scan
RUNSee actual execution
OPERATEObserve, control, optimize

Results reflect the code at the commit and date shown.