---
name: Vulnerability Scanner
engine: copilot
on:
  schedule: daily
  workflow_dispatch: {}
timeout-minutes: 30
permissions:
  contents: read
  issues: read
  pull-requests: read
network:
  allowed:
    - jira.mongodb.org
    - api.github.com
    - github.com
tools:
  github:
    allowed-repos:
      - mongodb-js/mongodb-mcp-server
    min-integrity: unapproved
mcp-scripts:
  jira-search:
    description: "Fetch open vulnerability tickets assigned to Developer Tools for the mongodb-mcp-server repo that need triage."
    script: |
      const token = process.env.JIRA_API_TOKEN;
      const baseUrl = "https://jira.mongodb.org";
      const jql = `project = VULN AND (status not in ('Rejected', 'Remediation Complete')) AND (labels is empty OR labels != test-only) AND 'Assigned Teams' = 'Developer Tools' AND status = "Needs Triage" AND Repo ~ "https://github.com/mongodb-js/mongodb-mcp-server" AND Development[pullrequests].all = 0`;
      const url = `${baseUrl}/rest/api/2/search?jql=${encodeURIComponent(jql)}&maxResults=50&fields=summary,description,status,priority,labels,comment,assignee,reporter,created,updated,customfield_10000`;
      const response = await fetch(url, {
        headers: {
          "Authorization": `Bearer ${token}`,
          "Content-Type": "application/json",
          "Accept": "application/json"
        }
      });
      if (!response.ok) {
        throw new Error(`JIRA API error: ${response.status} ${response.statusText}`);
      }
      const data = await response.json();
      const BOT_EMAIL = 'svc-mdb-mcp-bot@mongodb.com';
      data.issues = data.issues.filter(issue => {
        const comments = issue.fields.comment?.comments ?? [];
        if (comments.length === 0) return true;
        const lastComment = comments[comments.length - 1];
        return lastComment.author?.emailAddress !== BOT_EMAIL;
      });
      return data;
    env:
      JIRA_API_TOKEN: "${{ secrets.JIRA_API_TOKEN }}"
  jira-get-issue:
    description: "Get detailed information about a specific JIRA issue by its key (e.g. VULN-1234)."
    inputs:
      issue_key:
        type: string
        required: true
        description: "JIRA issue key (e.g. VULN-1234)"
    script: |
      const token = process.env.JIRA_API_TOKEN;
      const baseUrl = "https://jira.mongodb.org";
      const url = `${baseUrl}/rest/api/2/issue/${issue_key}?expand=renderedFields`;
      const response = await fetch(url, {
        headers: {
          "Authorization": `Bearer ${token}`,
          "Content-Type": "application/json",
          "Accept": "application/json"
        }
      });
      if (!response.ok) {
        throw new Error(`JIRA API error: ${response.status} ${response.statusText}`);
      }
      return await response.json();
    env:
      JIRA_API_TOKEN: "${{ secrets.JIRA_API_TOKEN }}"
safe-outputs:
  create-pull-request:
    target-repo: mongodb-js/mongodb-mcp-server
    protected-files: allowed
  jobs:
    jira_add_comment:
      description: "Add a comment to a JIRA issue."
      runs-on: ubuntu-latest
      output: "Comment added to JIRA issue successfully!"
      inputs:
        issue_key:
          description: "JIRA issue key (e.g. VULN-1234)"
          required: true
          type: string
        comment:
          description: "Comment text to add to the issue (Jira wiki markup supported)"
          required: true
          type: string
      steps:
        - name: Add comment to JIRA issue
          uses: actions/github-script@v9
          env:
            JIRA_API_TOKEN: "${{ secrets.JIRA_API_TOKEN }}"
          with:
            script: |
              const fs = require('fs');
              const token = process.env.JIRA_API_TOKEN;

              if (!token) {
                core.setFailed('JIRA_API_TOKEN secret is not configured');
                return;
              }

              const outputFile = process.env.GH_AW_AGENT_OUTPUT;
              if (!outputFile) {
                core.info('No GH_AW_AGENT_OUTPUT environment variable found');
                return;
              }

              const agentOutput = JSON.parse(fs.readFileSync(outputFile, 'utf8'));
              const items = agentOutput.items.filter(item => item.type === 'jira_add_comment');

              let failed = false;
              for (const item of items) {
                const { issue_key, comment } = item;
                core.info(`Adding comment to JIRA issue: ${issue_key}`);

                const url = `https://jira.mongodb.org/rest/api/2/issue/${issue_key}/comment`;
                const response = await fetch(url, {
                  method: 'POST',
                  headers: {
                    'Authorization': `Bearer ${token}`,
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                  },
                  body: JSON.stringify({ body: comment })
                });

                if (!response.ok) {
                  const errorText = await response.text();
                  core.error(`JIRA API error for ${issue_key} (${response.status}): ${errorText}`);
                  failed = true;
                  continue;
                }

                const data = await response.json();
                core.info(`Comment added successfully to ${issue_key} (ID: ${data.id})`);
              }

              if (failed) {
                core.setFailed('One or more JIRA comments failed to post');
              }
strict: true
---

# Vulnerability Scanner Workflow

You are a security analyst responsible for monitoring and addressing vulnerability reports for the MongoDB MCP Server project.

## Your Task

1. **Fetch vulnerability tickets** by calling the `jira-search` tool (no arguments needed).

2. **Group related tickets** before taking action. Tickets should be handled together when they:
   - Stem from the same vulnerable third-party dependency
   - Affect the same file or component
   - Represent the same underlying vulnerability class

   For each group, open a single PR or emit a single `jira_add_comment` per ticket referencing the shared resolution, rather than creating separate PRs or unrelated comments.

3. **For each ticket (or group of tickets)**, use `jira-get-issue` to fetch full details, then perform the following analysis:

   a. **Read the ticket details** including:
   - Summary and description
   - Affected components/files mentioned
   - Severity level
   - Any attached reports or evidence
   - **Existing comments** — if humans have already commented on the ticket, read them carefully and factor their input into your evaluation. A human may have provided additional context, corrected a previous assessment, or asked you to take a specific action. Treat human input as authoritative and prioritize it over your own initial assessment.

   b. **Analyze the codebase** to determine if the reported vulnerability is valid:
   - Examine the mentioned files/components
   - Check for the specific vulnerability patterns described
   - Consider the context of how the code is used in this project
   - Look for existing mitigations or safeguards

   c. **Take appropriate action**:

   **If the vulnerability is NOT valid for this project:**
   - Emit a `jira_add_comment` safe-output with the issue key and a comment explaining why the vulnerability doesn't apply
   - Include specific technical details about why the code pattern is safe in this context
   - Reference specific files and line numbers where relevant
   - Use a professional, helpful tone

   **If the vulnerability IS valid:**
   - Attempt to create a fix by modifying the affected code
   - Create a pull request with the fix using the create_pull_request safe output tool — the PR title MUST follow conventional commits format with the JIRA ticket key(s) appended at the end:
     - Dependency version bumps: `chore(deps): <description> VULN-1234`
     - Code fixes: `fix: <description> VULN-1234`
     - For grouped tickets, append all keys: `fix: <description> VULN-1234, VULN-1235`
   - Emit a `jira_add_comment` safe-output with the issue key and a comment linking to the PR with a summary of the fix

   **If the vulnerability is valid but you cannot confidently fix it:**
   - Emit a `jira_add_comment` safe-output with the issue key and a comment explaining what you found and why you couldn't automatically fix it
   - Provide detailed analysis of the issue
   - Suggest potential approaches for manual remediation

## Guidelines

### When assessing validity:

- Consider that this is a Model Context Protocol (MCP) server primarily used for AI assistants
- Check if vulnerable patterns are actually reachable in the deployment context
- Look for existing input validation, sanitization, or other security controls
- Consider the principle of least privilege and how the server is typically deployed

### When creating fixes:

- Make minimal, targeted changes that address the specific vulnerability
- Preserve existing functionality and API contracts
- Add appropriate tests if possible
- Follow the project's coding standards and patterns
- Include clear commit messages referencing the vulnerability

### When commenting:

- Be specific and technical in your explanations
- Provide file names, line numbers, and code snippets when relevant
- Explain the security implications clearly
- Be respectful and professional
- Include links to relevant documentation or standards when helpful

## Important Notes

- Always read the full vulnerability report carefully before making any determinations
- If in doubt about validity, err on the side of caution and flag for human review
- Never dismiss a report without providing technical justification
- Ensure all fixes are thoroughly tested before creating PRs
- Follow responsible disclosure practices - don't include sensitive details in public comments

## Error Handling

If you encounter any errors:

- Log the issue clearly
- Continue processing other tickets
- Don't stop the entire workflow for individual ticket failures
- Provide meaningful error messages for debugging

Begin by fetching the current vulnerability tickets and processing each one systematically.
