/**
 * MCP Security Test Fixtures
 * Tests for detecting security issues in Model Context Protocol (MCP) tools
 *
 * Background: MCP enables AI agents to call external tools. Security risks include:
 * - Tool Poisoning: External content returned without validation
 * - Credential Issues: Credentials in tool parameters/responses
 * - Confused Deputy: Operations without proper user context
 *
 * Reference: CVE-2025-6514, 13,000+ MCP servers deployed
 */

import type { TestGroup } from '../../types'

export const aiMcpSecurityTests: TestGroup = {
  name: 'MCP Tool Security',
  tier: 'A',
  layer: 2,
  description: 'Detection of security issues in Model Context Protocol tools',

  truePositives: [
    {
      name: 'MCP Security - Tool Poisoning True Positives',
      expectFindings: true,
      expectedCategories: ['ai_mcp_tool_poisoning'],
      description: 'MCP tools returning unvalidated external content',
      file: {
        path: 'src/mcp/tools/fetch-tool.ts',
        content: `
import { McpServer, Tool } from '@modelcontextprotocol/sdk'

const server = new McpServer({ name: 'fetch-server' })

// Dangerous: Returns raw external content without sanitization
server.tool('fetch_webpage', async ({ url }: { url: string }) => {
  const response = await fetch(url)
  const html = await response.text()
  // Returning raw HTML - could contain prompt injection payloads
  return { content: html }
})

// Dangerous: Raw file content returned without validation
server.tool('read_file', async ({ path }: { path: string }) => {
  const content = await fs.readFile(path, 'utf-8')
  // No validation of file content
  return { content }
})

// Dangerous: Database query results returned directly
server.tool('query_database', async ({ query }: { query: string }) => {
  const results = await db.query(query)
  // Raw database content could contain injected instructions
  return { data: results.rows }
})

// Dangerous: Email content without sanitization
server.tool('get_email', async ({ id }: { id: string }) => {
  const email = await emailClient.getMessage(id)
  return {
    subject: email.subject,
    body: email.body,  // Raw email body - could contain malicious instructions
    attachments: email.attachments
  }
})

// Dangerous: RSS feed content
server.tool('fetch_rss', async ({ feedUrl }: { feedUrl: string }) => {
  const feed = await parser.parseURL(feedUrl)
  // Entire feed content returned - titles, descriptions could be poisoned
  return { items: feed.items }
})
`,
        language: 'typescript',
        size: 1300,
      },
    },
    {
      name: 'MCP Security - Credential Issues True Positives',
      expectFindings: true,
      expectedCategories: ['ai_mcp_credential_issue'],
      description: 'MCP tools with credential exposure in parameters or responses',
      file: {
        path: 'src/mcp/tools/auth-tool.ts',
        content: `
import { McpServer } from '@modelcontextprotocol/sdk'

const server = new McpServer({ name: 'auth-server' })

// Dangerous: API key passed as tool parameter
server.tool('call_api', async ({ apiKey, endpoint }: { apiKey: string; endpoint: string }) => {
  const response = await fetch(endpoint, {
    headers: { 'Authorization': \`Bearer \${apiKey}\` }
  })
  return { data: await response.json() }
})

// Dangerous: Returning credentials in response
server.tool('create_user', async ({ email }: { email: string }) => {
  const user = await db.createUser(email)
  const password = generatePassword()
  await db.setPassword(user.id, password)
  // Returning password to the model!
  return { userId: user.id, password, apiKey: user.apiKey }
})

// Dangerous: Token in response
server.tool('authenticate', async ({ username, password }: { username: string; password: string }) => {
  const result = await auth.login(username, password)
  // JWT token exposed to model
  return { token: result.jwt, refreshToken: result.refreshToken }
})

// Dangerous: Database connection string in parameter
server.tool('connect_db', async ({ connectionString }: { connectionString: string }) => {
  const client = await Database.connect(connectionString)
  return { status: 'connected' }
})

// Dangerous: Private key parameter
server.tool('sign_message', async ({ privateKey, message }: { privateKey: string; message: string }) => {
  const signature = crypto.sign(privateKey, message)
  return { signature }
})

// Dangerous: Returning secrets from environment
server.tool('get_config', async () => {
  return {
    apiUrl: process.env.API_URL,
    apiKey: process.env.API_KEY,  // Secret exposed!
    secretKey: process.env.SECRET_KEY,  // Secret exposed!
  }
})
`,
        language: 'typescript',
        size: 1500,
      },
    },
    {
      name: 'MCP Security - Confused Deputy True Positives',
      expectFindings: true,
      expectedCategories: ['ai_mcp_confused_deputy'],
      description: 'MCP tools performing operations without user context',
      file: {
        path: 'src/mcp/tools/data-tool.ts',
        content: `
import { McpServer } from '@modelcontextprotocol/sdk'

const server = new McpServer({ name: 'data-server' })

// Dangerous: No user context - can access any user's files
server.tool('read_user_file', async ({ fileId }: { fileId: string }) => {
  // No user authentication/authorization
  const file = await db.files.findById(fileId)
  return { content: file.content }
})

// Dangerous: Delete without user verification
server.tool('delete_record', async ({ recordId }: { recordId: string }) => {
  // No check if user owns this record
  await db.records.delete(recordId)
  return { success: true }
})

// Dangerous: Update any user's data
server.tool('update_profile', async ({ userId, data }: { userId: string; data: any }) => {
  // Can update ANY user's profile, not just the current user
  await db.users.update(userId, data)
  return { success: true }
})

// Dangerous: Cross-tenant data access
server.tool('get_organization_data', async ({ orgId }: { orgId: string }) => {
  // No tenant verification - can access any org's data
  const data = await db.organizations.findById(orgId)
  return { data }
})

// Dangerous: Admin action without auth
server.tool('grant_admin', async ({ userId }: { userId: string }) => {
  // No authorization check - any agent can make users admin
  await db.users.update(userId, { role: 'admin' })
  return { success: true }
})

// Dangerous: Sending email as any user
server.tool('send_email', async ({ from, to, subject, body }: { from: string; to: string; subject: string; body: string }) => {
  // No verification that 'from' is the actual user
  await emailService.send({ from, to, subject, body })
  return { sent: true }
})

// Dangerous: Accessing financial data without scope
server.tool('get_transactions', async ({ accountId }: { accountId: string }) => {
  // Can access any account's transactions
  const transactions = await financeService.getTransactions(accountId)
  return { transactions }
})
`,
        language: 'typescript',
        size: 1700,
      },
    },
    {
      name: 'MCP Security - Python MCP Tools True Positives',
      expectFindings: true,
      expectedCategories: ['ai_mcp_tool_poisoning', 'ai_mcp_credential_issue', 'ai_mcp_confused_deputy'],
      description: 'Python MCP server with security issues',
      file: {
        path: 'src/mcp/server.py',
        content: `
from mcp import McpServer
from mcp.server import Tool

server = McpServer(name="python-mcp")

# Tool poisoning: raw external content
@server.tool("fetch_url")
async def fetch_url(url: str) -> dict:
    response = await httpx.get(url)
    # Raw content returned without sanitization
    return {"content": response.text}

# Credential issue: API key parameter
@server.tool("api_call")
async def api_call(api_key: str, endpoint: str) -> dict:
    response = await httpx.get(endpoint, headers={"Authorization": f"Bearer {api_key}"})
    return {"data": response.json()}

# Confused deputy: no user context
@server.tool("delete_file")
async def delete_file(file_id: str) -> dict:
    # No user verification
    await storage.delete(file_id)
    return {"deleted": True}

# Tool poisoning: returning database content
@server.tool("search_documents")
async def search_documents(query: str) -> dict:
    results = await db.search(query)
    return {"documents": results}

# Credential in response
@server.tool("create_api_key")
async def create_api_key(name: str) -> dict:
    key = await keys.create(name)
    return {"api_key": key.secret}  # Exposing secret!
`,
        language: 'python',
        size: 1100,
      },
    },
  ],

  falseNegatives: [
    {
      name: 'MCP Security - Safe Patterns',
      expectFindings: false,
      description: 'Safe MCP tool implementations that should NOT be flagged',
allowedInfoFindings: [
        {
          category: 'ai_mcp_tool_poisoning',
          maxCount: 5,
          reason: 'Some safe patterns may still flag at info level for hygiene',
        },
        {
          category: 'ai_mcp_confused_deputy',
          maxCount: 5,
          reason: 'User context patterns may still flag at info level',
        },
        {
          category: 'ai_overpermissive_tool',
          maxCount: 10,
          reason: 'ai-agent-tools detector flags MCP tool registrations (separate detector)',
        },
        {
          category: 'high_entropy_string',
          maxCount: 2,
          reason: 'DOMPurify import may trigger entropy detection',
        },
      ],
      file: {
        path: 'src/mcp/tools/safe-tools.ts',
        content: `
import { McpServer } from '@modelcontextprotocol/sdk'
import { sanitize } from 'dompurify'
import { validateSchema } from 'zod'

const server = new McpServer({ name: 'safe-server' })

// Safe: Content sanitized before return
server.tool('fetch_safe', async ({ url }: { url: string }, context) => {
  const userId = context.user.id  // User context!
  const response = await fetch(url)
  const html = await response.text()
  // Content is sanitized
  const sanitized = sanitize(html, { ALLOWED_TAGS: ['p', 'a', 'b'] })
  return { content: sanitized }
})

// Safe: User context properly validated
server.tool('get_my_files', async (_params, context) => {
  const userId = context.user.id
  // Only getting the current user's files
  const files = await db.files.findByUser(userId)
  return { files: files.map(f => ({ id: f.id, name: f.name })) }
})

// Safe: Server-side auth, no credentials exposed
server.tool('call_external_api', async ({ endpoint }: { endpoint: string }) => {
  // API key from environment, not passed as parameter
  const response = await fetch(endpoint, {
    headers: { 'Authorization': \`Bearer \${process.env.INTERNAL_API_KEY}\` }
  })
  // Only returning safe data, no credentials
  return { data: await response.json() }
})

// Safe: Proper authorization check
server.tool('delete_my_record', async ({ recordId }: { recordId: string }, context) => {
  const userId = context.user.id
  const record = await db.records.findById(recordId)

  // Authorization check
  if (record.ownerId !== userId) {
    throw new Error('Not authorized')
  }

  await db.records.delete(recordId)
  return { success: true }
})

// Safe: Content validation with schema
server.tool('search_public_data', async ({ query }: { query: string }, context) => {
  const userId = context.user.id
  const results = await db.publicData.search(query)
  // Returning only safe fields, validated structure
  return {
    results: results.map(r => ({
      id: r.id,
      title: sanitize(r.title),  // Sanitized
      summary: sanitize(r.summary)  // Sanitized
    }))
  }
})

// Safe: Tenant-scoped operations
server.tool('get_org_report', async ({ reportId }: { reportId: string }, context) => {
  const tenantId = context.user.tenantId
  const report = await db.reports.findById(reportId)

  // Tenant check
  if (report.tenantId !== tenantId) {
    throw new Error('Not authorized')
  }

  return { report }
})

// Safe: Pure computation, no external data
server.tool('calculate', async ({ expression }: { expression: string }) => {
  // Safe math evaluation, no external content
  const result = mathjs.evaluate(expression)
  return { result }
})

// Safe: Static data only
server.tool('get_documentation', async ({ topic }: { topic: string }) => {
  // Returns static documentation, not user content
  const docs = await loadStaticDocs(topic)
  return { content: docs }
})
`,
        language: 'typescript',
        size: 2500,
      },
    },
    {
      name: 'MCP Security - Test File Patterns',
      expectFindings: false,
      description: 'Test files should not be flagged',
      allowedInfoFindings: [
        {
          category: 'ai_mcp_tool_poisoning',
          maxCount: 3,
          reason: 'Test file may have info-level findings for review',
        },
        {
          category: 'ai_mcp_credential_issue',
          maxCount: 3,
          reason: 'Test file may have info-level findings for review',
        },
        {
          category: 'ai_mcp_confused_deputy',
          maxCount: 3,
          reason: 'Test file may have info-level findings for review',
        },
        {
          category: 'ai_overpermissive_tool',
          maxCount: 3,
          reason: 'ai-agent-tools detector may flag MCP patterns',
        },
      ],
      file: {
        path: 'src/mcp/__tests__/tools.test.ts',
        content: `
import { McpServer } from '@modelcontextprotocol/sdk'

describe('MCP Tools', () => {
  it('should handle raw content in tests', async () => {
    const server = new McpServer({ name: 'test-server' })

    server.tool('fetch_test', async ({ url }) => {
      const response = await fetch(url)
      return { content: await response.text() }
    })

    // Test assertions
  })

  it('should test credential handling', async () => {
    const server = new McpServer({ name: 'test-server' })

    server.tool('auth_test', async ({ apiKey }) => {
      return { token: 'test-token', apiKey }
    })
  })

  it('should test user operations', async () => {
    const server = new McpServer({ name: 'test-server' })

    server.tool('delete_test', async ({ id }) => {
      await mockDb.delete(id)
      return { success: true }
    })
  })
})
`,
        language: 'typescript',
        size: 900,
      },
    },
    {
      name: 'MCP Security - Example/Demo Files',
      expectFindings: false,
      description: 'Example files in documentation should be downgraded',
      allowedInfoFindings: [
        {
          category: 'ai_mcp_tool_poisoning',
          maxCount: 5,
          reason: 'Example files may have info-level findings',
        },
        {
          category: 'ai_mcp_credential_issue',
          maxCount: 3,
          reason: 'Example files may have info-level findings',
        },
        {
          category: 'ai_mcp_confused_deputy',
          maxCount: 3,
          reason: 'Example files may have info-level findings',
        },
        {
          category: 'ai_overpermissive_tool',
          maxCount: 3,
          reason: 'ai-agent-tools detector may flag MCP patterns',
        },
      ],
      file: {
        path: 'examples/mcp-quickstart/server.ts',
        content: `
// This is an example file for documentation
import { McpServer } from '@modelcontextprotocol/sdk'

const server = new McpServer({ name: 'example-server' })

// Example: Simple fetch tool
server.tool('example_fetch', async ({ url }) => {
  const response = await fetch(url)
  return { content: await response.text() }
})

// Example: Data retrieval
server.tool('example_query', async ({ query }) => {
  const results = await db.query(query)
  return { data: results }
})

console.log('Example server started')
`,
        language: 'typescript',
        size: 500,
      },
    },
  ],
}
