opencode-skills-collection 4.0.68 → 4.0.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled-skills/.antigravity-install-manifest.json +266 -1
- package/bundled-skills/access-review/SKILL.md +394 -0
- package/bundled-skills/access-review/references/details.md +121 -0
- package/bundled-skills/agent-evals/SKILL.md +420 -0
- package/bundled-skills/agent-observability/SKILL.md +346 -0
- package/bundled-skills/agent-observability/references/details.md +786 -0
- package/bundled-skills/ai-agent-security/SKILL.md +393 -0
- package/bundled-skills/ai-agent-security/references/details.md +912 -0
- package/bundled-skills/ai-coding-agent-guardrails/SKILL.md +442 -0
- package/bundled-skills/ai-coding-agent-guardrails/references/details.md +753 -0
- package/bundled-skills/ai-inference-service-mesh/SKILL.md +449 -0
- package/bundled-skills/ai-pipeline-orchestration/SKILL.md +287 -0
- package/bundled-skills/ai-red-teaming/SKILL.md +409 -0
- package/bundled-skills/ai-security-hardening/SKILL.md +343 -0
- package/bundled-skills/ai-sre-incident-response/SKILL.md +336 -0
- package/bundled-skills/alerting-oncall/SKILL.md +458 -0
- package/bundled-skills/alerting-oncall/references/details.md +84 -0
- package/bundled-skills/apk-redteam-pipeline/SKILL.md +446 -0
- package/bundled-skills/argocd-gitops/SKILL.md +469 -0
- package/bundled-skills/arm-templates/SKILL.md +438 -0
- package/bundled-skills/arm-templates/references/details.md +64 -0
- package/bundled-skills/asset-inventory/SKILL.md +412 -0
- package/bundled-skills/asset-inventory/references/details.md +127 -0
- package/bundled-skills/audit-logging/SKILL.md +476 -0
- package/bundled-skills/aws-cloudtrail/SKILL.md +486 -0
- package/bundled-skills/aws-cost-optimization/SKILL.md +331 -0
- package/bundled-skills/aws-ec2/SKILL.md +426 -0
- package/bundled-skills/aws-ecs-fargate/SKILL.md +388 -0
- package/bundled-skills/aws-iam/SKILL.md +463 -0
- package/bundled-skills/aws-lambda/SKILL.md +428 -0
- package/bundled-skills/aws-rds/SKILL.md +380 -0
- package/bundled-skills/aws-s3/SKILL.md +434 -0
- package/bundled-skills/aws-secrets-manager/SKILL.md +486 -0
- package/bundled-skills/aws-vpc/SKILL.md +436 -0
- package/bundled-skills/azure-ai-document-intelligence-ts/SKILL.md +1 -1
- package/bundled-skills/azure-aks/SKILL.md +423 -0
- package/bundled-skills/azure-devops/SKILL.md +457 -0
- package/bundled-skills/azure-functions-devsec/SKILL.md +436 -0
- package/bundled-skills/azure-keyvault/SKILL.md +455 -0
- package/bundled-skills/azure-keyvault/references/details.md +83 -0
- package/bundled-skills/azure-monitor-audit/SKILL.md +379 -0
- package/bundled-skills/azure-networking/SKILL.md +448 -0
- package/bundled-skills/azure-networking/references/details.md +135 -0
- package/bundled-skills/azure-sql/SKILL.md +413 -0
- package/bundled-skills/azure-sql/references/details.md +113 -0
- package/bundled-skills/azure-vms/SKILL.md +402 -0
- package/bundled-skills/azure-vms/references/details.md +134 -0
- package/bundled-skills/backup-recovery/SKILL.md +388 -0
- package/bundled-skills/bb-methodology/SKILL.md +451 -0
- package/bundled-skills/bb-methodology/references/details.md +120 -0
- package/bundled-skills/block-storage/SKILL.md +371 -0
- package/bundled-skills/blue-green-deploy/SKILL.md +453 -0
- package/bundled-skills/blue-green-deploy/references/details.md +90 -0
- package/bundled-skills/bug-bounty/SKILL.md +447 -0
- package/bundled-skills/bug-bounty/references/details.md +1316 -0
- package/bundled-skills/bugcrowd-reporting/SKILL.md +351 -0
- package/bundled-skills/business-continuity/SKILL.md +463 -0
- package/bundled-skills/career-ops/SKILL.md +186 -0
- package/bundled-skills/cdn-setup/SKILL.md +374 -0
- package/bundled-skills/change-management/SKILL.md +438 -0
- package/bundled-skills/change-management/references/details.md +105 -0
- package/bundled-skills/circleci/SKILL.md +475 -0
- package/bundled-skills/cis-benchmarks/SKILL.md +150 -0
- package/bundled-skills/cloudflare-pages/SKILL.md +318 -0
- package/bundled-skills/cloudflare-r2/SKILL.md +353 -0
- package/bundled-skills/cloudflare-workers/SKILL.md +415 -0
- package/bundled-skills/cloudflare-zero-trust/SKILL.md +361 -0
- package/bundled-skills/cloudformation/SKILL.md +461 -0
- package/bundled-skills/constraint-driven-development/SKILL.md +335 -0
- package/bundled-skills/constraint-driven-development/references/floor-guard.md +99 -0
- package/bundled-skills/container-hardening/SKILL.md +126 -0
- package/bundled-skills/container-registries/SKILL.md +435 -0
- package/bundled-skills/container-scanning/SKILL.md +416 -0
- package/bundled-skills/convex-backend/SKILL.md +338 -0
- package/bundled-skills/dast-scanning/SKILL.md +437 -0
- package/bundled-skills/database-backups/SKILL.md +425 -0
- package/bundled-skills/datadog/SKILL.md +487 -0
- package/bundled-skills/dependency-scanning/SKILL.md +457 -0
- package/bundled-skills/devcontainers-nix/SKILL.md +416 -0
- package/bundled-skills/disaster-recovery/SKILL.md +374 -0
- package/bundled-skills/disaster-recovery/references/details.md +219 -0
- package/bundled-skills/dns-management/SKILL.md +375 -0
- package/bundled-skills/docker-compose/SKILL.md +482 -0
- package/bundled-skills/docker-management/SKILL.md +426 -0
- package/bundled-skills/ebpf-observability/SKILL.md +436 -0
- package/bundled-skills/ebpf-observability/references/details.md +542 -0
- package/bundled-skills/elk-stack/SKILL.md +487 -0
- package/bundled-skills/enterprise-vpn-attack/SKILL.md +395 -0
- package/bundled-skills/evidence-hygiene/SKILL.md +404 -0
- package/bundled-skills/feature-flags/SKILL.md +426 -0
- package/bundled-skills/feature-flags/references/details.md +86 -0
- package/bundled-skills/fedramp-compliance/SKILL.md +453 -0
- package/bundled-skills/firebase-app-platform/SKILL.md +381 -0
- package/bundled-skills/firewall-config/SKILL.md +479 -0
- package/bundled-skills/gcp-audit-logs/SKILL.md +452 -0
- package/bundled-skills/gcp-audit-logs/references/details.md +56 -0
- package/bundled-skills/gcp-cloud-functions/SKILL.md +284 -0
- package/bundled-skills/gcp-cloud-sql/SKILL.md +277 -0
- package/bundled-skills/gcp-compute/SKILL.md +319 -0
- package/bundled-skills/gcp-gke/SKILL.md +307 -0
- package/bundled-skills/gcp-networking/SKILL.md +293 -0
- package/bundled-skills/gcp-secret-manager/SKILL.md +421 -0
- package/bundled-skills/gcp-secret-manager/references/details.md +131 -0
- package/bundled-skills/gdpr-compliance/SKILL.md +451 -0
- package/bundled-skills/gdpr-compliance/references/details.md +145 -0
- package/bundled-skills/geo-audit/SKILL.md +368 -0
- package/bundled-skills/geo-brand-mentions/SKILL.md +68 -0
- package/bundled-skills/geo-brand-mentions/references/details.md +471 -0
- package/bundled-skills/geo-citability/SKILL.md +350 -0
- package/bundled-skills/geo-compare/SKILL.md +340 -0
- package/bundled-skills/geo-content/SKILL.md +383 -0
- package/bundled-skills/geo-crawlers/SKILL.md +408 -0
- package/bundled-skills/geo-llmstxt/SKILL.md +464 -0
- package/bundled-skills/geo-platform-optimizer/SKILL.md +314 -0
- package/bundled-skills/geo-proposal/SKILL.md +378 -0
- package/bundled-skills/geo-prospect/SKILL.md +225 -0
- package/bundled-skills/geo-report/SKILL.md +436 -0
- package/bundled-skills/geo-report-pdf/SKILL.md +157 -0
- package/bundled-skills/geo-schema/SKILL.md +408 -0
- package/bundled-skills/geo-technical/SKILL.md +78 -0
- package/bundled-skills/geo-technical/references/details.md +543 -0
- package/bundled-skills/git-workflow/SKILL.md +460 -0
- package/bundled-skills/github-actions/SKILL.md +368 -0
- package/bundled-skills/gitlab-ci/SKILL.md +340 -0
- package/bundled-skills/gpu-kubernetes-operations/SKILL.md +468 -0
- package/bundled-skills/gpu-server-management/SKILL.md +236 -0
- package/bundled-skills/hashicorp-vault/SKILL.md +408 -0
- package/bundled-skills/helm-charts/SKILL.md +469 -0
- package/bundled-skills/hipaa-compliance/SKILL.md +451 -0
- package/bundled-skills/hunt-aspnet/SKILL.md +321 -0
- package/bundled-skills/hunt-ato/SKILL.md +184 -0
- package/bundled-skills/hunt-auth-bypass/SKILL.md +426 -0
- package/bundled-skills/hunt-auth-bypass/references/details.md +80 -0
- package/bundled-skills/hunt-brute-force/SKILL.md +341 -0
- package/bundled-skills/hunt-business-logic/SKILL.md +281 -0
- package/bundled-skills/hunt-cache-poison/SKILL.md +382 -0
- package/bundled-skills/hunt-captcha-bypass/SKILL.md +136 -0
- package/bundled-skills/hunt-cicd/SKILL.md +311 -0
- package/bundled-skills/hunt-clickjacking/SKILL.md +110 -0
- package/bundled-skills/hunt-cors/SKILL.md +335 -0
- package/bundled-skills/hunt-dom/SKILL.md +323 -0
- package/bundled-skills/hunt-exceptional-conditions/SKILL.md +111 -0
- package/bundled-skills/hunt-file-upload/SKILL.md +202 -0
- package/bundled-skills/hunt-fintech-graphql/SKILL.md +289 -0
- package/bundled-skills/hunt-forgot-password/SKILL.md +114 -0
- package/bundled-skills/hunt-grpc/SKILL.md +317 -0
- package/bundled-skills/hunt-host-header/SKILL.md +309 -0
- package/bundled-skills/hunt-html-injection/SKILL.md +106 -0
- package/bundled-skills/hunt-http-smuggling/SKILL.md +129 -0
- package/bundled-skills/hunt-http-smuggling/references/phase2h-smuggling-cachepoison.md +177 -0
- package/bundled-skills/hunt-idor/SKILL.md +434 -0
- package/bundled-skills/hunt-jwt-crypto/SKILL.md +221 -0
- package/bundled-skills/hunt-k8s/SKILL.md +337 -0
- package/bundled-skills/hunt-laravel/SKILL.md +255 -0
- package/bundled-skills/hunt-ldap/SKILL.md +351 -0
- package/bundled-skills/hunt-lfi/SKILL.md +311 -0
- package/bundled-skills/hunt-llm-ai/SKILL.md +289 -0
- package/bundled-skills/hunt-mfa-bypass/SKILL.md +177 -0
- package/bundled-skills/hunt-misc/SKILL.md +378 -0
- package/bundled-skills/hunt-nextjs/SKILL.md +299 -0
- package/bundled-skills/hunt-nodejs/SKILL.md +263 -0
- package/bundled-skills/hunt-nosqli/SKILL.md +210 -0
- package/bundled-skills/hunt-ntlm-info/SKILL.md +314 -0
- package/bundled-skills/hunt-oauth/SKILL.md +459 -0
- package/bundled-skills/hunt-open-redirect/SKILL.md +223 -0
- package/bundled-skills/hunt-race-condition/SKILL.md +381 -0
- package/bundled-skills/hunt-race-condition/references/details.md +159 -0
- package/bundled-skills/hunt-rag-vector/SKILL.md +212 -0
- package/bundled-skills/hunt-rce/SKILL.md +444 -0
- package/bundled-skills/hunt-rce/references/details.md +110 -0
- package/bundled-skills/hunt-saml/SKILL.md +156 -0
- package/bundled-skills/hunt-session/SKILL.md +342 -0
- package/bundled-skills/hunt-shadow-api/SKILL.md +198 -0
- package/bundled-skills/hunt-source-leak/SKILL.md +345 -0
- package/bundled-skills/hunt-spa-api/SKILL.md +163 -0
- package/bundled-skills/hunt-springboot/SKILL.md +285 -0
- package/bundled-skills/hunt-sqli/SKILL.md +466 -0
- package/bundled-skills/hunt-ssrf/SKILL.md +396 -0
- package/bundled-skills/hunt-ssrf/references/details.md +179 -0
- package/bundled-skills/hunt-ssti/SKILL.md +163 -0
- package/bundled-skills/hunt-subdomain/SKILL.md +379 -0
- package/bundled-skills/hunt-tls-network/SKILL.md +399 -0
- package/bundled-skills/hunt-xxe/SKILL.md +466 -0
- package/bundled-skills/i-have-adhd/SKILL.md +170 -0
- package/bundled-skills/identity-access-management/SKILL.md +382 -0
- package/bundled-skills/identity-access-management/references/details.md +524 -0
- package/bundled-skills/incident-management/SKILL.md +484 -0
- package/bundled-skills/incident-response/SKILL.md +448 -0
- package/bundled-skills/incident-response/references/details.md +113 -0
- package/bundled-skills/interview-me/SKILL.md +248 -0
- package/bundled-skills/iso27001-compliance/SKILL.md +460 -0
- package/bundled-skills/jenkins/SKILL.md +462 -0
- package/bundled-skills/jev-use/SKILL.md +158 -0
- package/bundled-skills/kubernetes-hardening/SKILL.md +154 -0
- package/bundled-skills/kubernetes-ops/SKILL.md +449 -0
- package/bundled-skills/kubernetes-ops/references/details.md +108 -0
- package/bundled-skills/kustomize/SKILL.md +478 -0
- package/bundled-skills/linux-administration/SKILL.md +367 -0
- package/bundled-skills/linux-hardening/SKILL.md +154 -0
- package/bundled-skills/llm-app-security/SKILL.md +389 -0
- package/bundled-skills/llm-app-security/references/details.md +674 -0
- package/bundled-skills/llm-caching/SKILL.md +334 -0
- package/bundled-skills/llm-cost-optimization/SKILL.md +311 -0
- package/bundled-skills/llm-fine-tuning/SKILL.md +329 -0
- package/bundled-skills/llm-gateway/SKILL.md +282 -0
- package/bundled-skills/llm-inference-scaling/SKILL.md +286 -0
- package/bundled-skills/llmops-platform-engineering/SKILL.md +472 -0
- package/bundled-skills/load-balancing/SKILL.md +403 -0
- package/bundled-skills/loki-logging/SKILL.md +479 -0
- package/bundled-skills/m365-entra-attack/SKILL.md +423 -0
- package/bundled-skills/mac-mini-llm-lab/SKILL.md +350 -0
- package/bundled-skills/mcp-server-security/SKILL.md +356 -0
- package/bundled-skills/mcp-server-security/references/details.md +745 -0
- package/bundled-skills/mdm-device-management/SKILL.md +404 -0
- package/bundled-skills/mdm-device-management/references/details.md +410 -0
- package/bundled-skills/meme-coin-audit/SKILL.md +402 -0
- package/bundled-skills/mid-engagement-ir-detection/SKILL.md +377 -0
- package/bundled-skills/model-registry-governance/SKILL.md +452 -0
- package/bundled-skills/model-serving-kubernetes/SKILL.md +339 -0
- package/bundled-skills/model-supply-chain-security/SKILL.md +427 -0
- package/bundled-skills/mongodb/SKILL.md +436 -0
- package/bundled-skills/multi-tenant-llm-hosting/SKILL.md +435 -0
- package/bundled-skills/multi-tenant-llm-hosting/references/details.md +211 -0
- package/bundled-skills/mysql/SKILL.md +390 -0
- package/bundled-skills/new-relic/SKILL.md +472 -0
- package/bundled-skills/nfs-storage/SKILL.md +356 -0
- package/bundled-skills/object-storage/SKILL.md +378 -0
- package/bundled-skills/offensive-osint/SKILL.md +443 -0
- package/bundled-skills/okta-attack/SKILL.md +436 -0
- package/bundled-skills/ollama-stack/SKILL.md +379 -0
- package/bundled-skills/openclaw-deployment-hardening/SKILL.md +135 -0
- package/bundled-skills/openclaw-local-mac-mini/SKILL.md +426 -0
- package/bundled-skills/openclaw-local-mac-mini/references/details.md +221 -0
- package/bundled-skills/openclaw-security-hardening/SKILL.md +135 -0
- package/bundled-skills/openshift/SKILL.md +485 -0
- package/bundled-skills/opentelemetry/SKILL.md +438 -0
- package/bundled-skills/opentelemetry/references/details.md +78 -0
- package/bundled-skills/opentofu-migration/SKILL.md +349 -0
- package/bundled-skills/osint-methodology/SKILL.md +460 -0
- package/bundled-skills/osint-methodology/references/details.md +1350 -0
- package/bundled-skills/pci-dss-compliance/SKILL.md +446 -0
- package/bundled-skills/penetration-testing/SKILL.md +152 -0
- package/bundled-skills/performance-tuning/SKILL.md +381 -0
- package/bundled-skills/planetscale/SKILL.md +297 -0
- package/bundled-skills/platform-engineering/SKILL.md +348 -0
- package/bundled-skills/platform-engineering/references/details.md +944 -0
- package/bundled-skills/podman/SKILL.md +405 -0
- package/bundled-skills/policy-as-code/SKILL.md +434 -0
- package/bundled-skills/policy-as-code/references/details.md +204 -0
- package/bundled-skills/postgresql-devsec/SKILL.md +378 -0
- package/bundled-skills/prometheus-grafana/SKILL.md +469 -0
- package/bundled-skills/prompt-injection-defense/SKILL.md +483 -0
- package/bundled-skills/rag-infrastructure/SKILL.md +269 -0
- package/bundled-skills/rag-observability-evals/SKILL.md +444 -0
- package/bundled-skills/rag-observability-evals/references/details.md +92 -0
- package/bundled-skills/recon-scope-triage/SKILL.md +128 -0
- package/bundled-skills/redis/SKILL.md +421 -0
- package/bundled-skills/redteam-report-template/SKILL.md +370 -0
- package/bundled-skills/report-writing/SKILL.md +426 -0
- package/bundled-skills/report-writing/references/details.md +187 -0
- package/bundled-skills/reverse-proxy/SKILL.md +420 -0
- package/bundled-skills/runbook-creation/SKILL.md +438 -0
- package/bundled-skills/runbook-creation/references/details.md +71 -0
- package/bundled-skills/saas-security-posture/SKILL.md +415 -0
- package/bundled-skills/sast-scanning/SKILL.md +444 -0
- package/bundled-skills/sbom-supply-chain/SKILL.md +433 -0
- package/bundled-skills/security-arsenal/SKILL.md +446 -0
- package/bundled-skills/security-arsenal/references/details.md +540 -0
- package/bundled-skills/security-automation/SKILL.md +146 -0
- package/bundled-skills/semantic-versioning/SKILL.md +434 -0
- package/bundled-skills/semantic-versioning/references/details.md +83 -0
- package/bundled-skills/service-mesh/SKILL.md +422 -0
- package/bundled-skills/soc2-compliance/SKILL.md +409 -0
- package/bundled-skills/sops-encryption/SKILL.md +124 -0
- package/bundled-skills/sre-dashboards/SKILL.md +143 -0
- package/bundled-skills/ssh-configuration/SKILL.md +324 -0
- package/bundled-skills/ssl-tls-management/SKILL.md +428 -0
- package/bundled-skills/ssl-tls-management/references/details.md +99 -0
- package/bundled-skills/startup-it-troubleshooting/SKILL.md +415 -0
- package/bundled-skills/supply-chain-attack-recon/SKILL.md +453 -0
- package/bundled-skills/supply-chain-attack-recon/references/details.md +258 -0
- package/bundled-skills/systemd-services/SKILL.md +379 -0
- package/bundled-skills/terraform-aws/SKILL.md +125 -0
- package/bundled-skills/terraform-azure/SKILL.md +415 -0
- package/bundled-skills/terraform-azure/references/details.md +231 -0
- package/bundled-skills/terraform-gcp/SKILL.md +369 -0
- package/bundled-skills/threat-modeling/SKILL.md +487 -0
- package/bundled-skills/user-management/SKILL.md +383 -0
- package/bundled-skills/using-agent-skills/SKILL.md +220 -0
- package/bundled-skills/vector-database-ops/SKILL.md +300 -0
- package/bundled-skills/vendor-management/SKILL.md +439 -0
- package/bundled-skills/vendor-management/references/details.md +109 -0
- package/bundled-skills/vercel-deployments/SKILL.md +296 -0
- package/bundled-skills/vllm-server/SKILL.md +236 -0
- package/bundled-skills/vmware-vcenter-attack/SKILL.md +412 -0
- package/bundled-skills/vpn-setup/SKILL.md +452 -0
- package/bundled-skills/vulnerability-scanning/SKILL.md +448 -0
- package/bundled-skills/waf-setup/SKILL.md +354 -0
- package/bundled-skills/waf-setup/references/details.md +211 -0
- package/bundled-skills/web2-recon/SKILL.md +440 -0
- package/bundled-skills/web2-recon/references/details.md +319 -0
- package/bundled-skills/web3-audit/SKILL.md +445 -0
- package/bundled-skills/web3-audit/references/details.md +224 -0
- package/bundled-skills/windows-hardening/SKILL.md +454 -0
- package/bundled-skills/windows-hardening/references/details.md +204 -0
- package/bundled-skills/windows-server/SKILL.md +318 -0
- package/bundled-skills/zero-trust/SKILL.md +461 -0
- package/package.json +1 -1
- package/skills_index.json +6943 -323
|
@@ -0,0 +1,745 @@
|
|
|
1
|
+
# Details (moved from SKILL.md)
|
|
2
|
+
|
|
3
|
+
> Extended reference content for `mcp-server-security`, kept under `references/` so the entrypoint stays within the audit budget.
|
|
4
|
+
|
|
5
|
+
## 6. Input Validation
|
|
6
|
+
|
|
7
|
+
### 6.1 JSON Schema for Tool Parameters
|
|
8
|
+
|
|
9
|
+
Define strict schemas for every tool's input. Reject anything that does not conform.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// src/validation.ts
|
|
13
|
+
import Ajv from "ajv";
|
|
14
|
+
import addFormats from "ajv-formats";
|
|
15
|
+
|
|
16
|
+
const ajv = new Ajv({ allErrors: true, removeAdditional: true });
|
|
17
|
+
addFormats(ajv);
|
|
18
|
+
|
|
19
|
+
// Schema registry keyed by tool name
|
|
20
|
+
const toolSchemas: Record<string, object> = {
|
|
21
|
+
read_file: {
|
|
22
|
+
type: "object",
|
|
23
|
+
properties: {
|
|
24
|
+
path: {
|
|
25
|
+
type: "string",
|
|
26
|
+
pattern: "^[a-zA-Z0-9_/\\-.]+$", // No path traversal chars
|
|
27
|
+
maxLength: 256,
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ["path"],
|
|
31
|
+
additionalProperties: false,
|
|
32
|
+
},
|
|
33
|
+
query_database: {
|
|
34
|
+
type: "object",
|
|
35
|
+
properties: {
|
|
36
|
+
query: { type: "string", maxLength: 2048 },
|
|
37
|
+
database: { type: "string", enum: ["analytics", "public_catalog"] },
|
|
38
|
+
parameters: {
|
|
39
|
+
type: "array",
|
|
40
|
+
items: { type: ["string", "number", "boolean"] },
|
|
41
|
+
maxItems: 20,
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ["query", "database"],
|
|
45
|
+
additionalProperties: false,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export function validateToolInput(toolName: string, params: unknown): { valid: boolean; errors?: string } {
|
|
50
|
+
const schema = toolSchemas[toolName];
|
|
51
|
+
if (!schema) return { valid: false, errors: `No schema registered for tool: ${toolName}` };
|
|
52
|
+
|
|
53
|
+
const validate = ajv.compile(schema);
|
|
54
|
+
if (validate(params)) return { valid: true };
|
|
55
|
+
|
|
56
|
+
const errorMsg = validate.errors?.map((e) => `${e.instancePath} ${e.message}`).join("; ");
|
|
57
|
+
return { valid: false, errors: errorMsg };
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 6.2 SQL Injection Prevention
|
|
62
|
+
|
|
63
|
+
Never pass raw agent-supplied strings into queries. Use parameterized queries and
|
|
64
|
+
statement-level restrictions.
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// src/safeSql.ts
|
|
68
|
+
const FORBIDDEN_PATTERNS = [
|
|
69
|
+
/;\s*(DROP|ALTER|TRUNCATE|DELETE|UPDATE|INSERT|CREATE|GRANT|REVOKE)/i,
|
|
70
|
+
/UNION\s+SELECT/i,
|
|
71
|
+
/INTO\s+OUTFILE/i,
|
|
72
|
+
/LOAD_FILE\s*\(/i,
|
|
73
|
+
/xp_cmdshell/i,
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
export function sanitizeSqlQuery(query: string): { safe: boolean; reason?: string } {
|
|
77
|
+
for (const pattern of FORBIDDEN_PATTERNS) {
|
|
78
|
+
if (pattern.test(query)) {
|
|
79
|
+
return { safe: false, reason: `Query matches forbidden pattern: ${pattern}` };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Only allow SELECT statements
|
|
84
|
+
const trimmed = query.trim().toUpperCase();
|
|
85
|
+
if (!trimmed.startsWith("SELECT")) {
|
|
86
|
+
return { safe: false, reason: "Only SELECT queries are permitted" };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return { safe: true };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Usage in a database tool handler
|
|
93
|
+
export async function handleDatabaseQuery(params: { query: string; parameters?: any[] }, db: any) {
|
|
94
|
+
const check = sanitizeSqlQuery(params.query);
|
|
95
|
+
if (!check.safe) {
|
|
96
|
+
throw new Error(`Query rejected: ${check.reason}`);
|
|
97
|
+
}
|
|
98
|
+
// Always use parameterized execution
|
|
99
|
+
return db.query(params.query, params.parameters || []);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 6.3 Filesystem Path Injection Prevention
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// src/safePath.ts
|
|
107
|
+
import path from "path";
|
|
108
|
+
|
|
109
|
+
const SANDBOX_ROOT = "/home/mcpuser/workspace";
|
|
110
|
+
|
|
111
|
+
export function resolveSafePath(userPath: string): string {
|
|
112
|
+
// Resolve to absolute, then verify it is inside the sandbox
|
|
113
|
+
const resolved = path.resolve(SANDBOX_ROOT, userPath);
|
|
114
|
+
|
|
115
|
+
if (!resolved.startsWith(SANDBOX_ROOT + path.sep) && resolved !== SANDBOX_ROOT) {
|
|
116
|
+
throw new Error(`Path traversal blocked: "${userPath}" resolves outside sandbox.`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Block symlink escape
|
|
120
|
+
const real = require("fs").realpathSync.native(resolved);
|
|
121
|
+
if (!real.startsWith(SANDBOX_ROOT)) {
|
|
122
|
+
throw new Error(`Symlink escape blocked: "${userPath}" -> "${real}"`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return resolved;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
## 7. Resource Access Control
|
|
133
|
+
|
|
134
|
+
### 7.1 Filesystem Sandboxing
|
|
135
|
+
|
|
136
|
+
Use OS-level controls in addition to application-level path validation.
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
#!/usr/bin/env bash
|
|
140
|
+
# run-mcp-sandboxed.sh - Launch MCP server with Linux namespace sandboxing
|
|
141
|
+
|
|
142
|
+
exec unshare --map-root-user --mount --pid --fork -- bash -c '
|
|
143
|
+
# Create a read-only bind mount for the workspace
|
|
144
|
+
mount --bind /home/mcpuser/workspace /home/mcpuser/workspace
|
|
145
|
+
mount -o remount,ro,bind /home/mcpuser/workspace
|
|
146
|
+
|
|
147
|
+
# Make the writable output directory available
|
|
148
|
+
mount --bind /home/mcpuser/output /home/mcpuser/output
|
|
149
|
+
|
|
150
|
+
# Block access to sensitive host paths
|
|
151
|
+
mount -t tmpfs tmpfs /etc/ssh
|
|
152
|
+
mount -t tmpfs tmpfs /root
|
|
153
|
+
mount -t tmpfs tmpfs /home/mcpuser/.ssh
|
|
154
|
+
|
|
155
|
+
# Run the MCP server
|
|
156
|
+
exec node /opt/mcp-server/dist/index.js
|
|
157
|
+
'
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### 7.2 Database Query Restrictions
|
|
161
|
+
|
|
162
|
+
Create a dedicated read-only database user for MCP servers:
|
|
163
|
+
|
|
164
|
+
```sql
|
|
165
|
+
-- PostgreSQL: MCP server database user
|
|
166
|
+
CREATE ROLE mcp_readonly WITH LOGIN PASSWORD 'use-a-vault-generated-secret';
|
|
167
|
+
|
|
168
|
+
-- Grant read-only access to specific schemas only
|
|
169
|
+
GRANT USAGE ON SCHEMA public TO mcp_readonly;
|
|
170
|
+
GRANT SELECT ON ALL TABLES IN SCHEMA public TO mcp_readonly;
|
|
171
|
+
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO mcp_readonly;
|
|
172
|
+
|
|
173
|
+
-- Restrict row-level access where needed
|
|
174
|
+
ALTER TABLE customer_data ENABLE ROW LEVEL SECURITY;
|
|
175
|
+
CREATE POLICY mcp_access ON customer_data
|
|
176
|
+
FOR SELECT TO mcp_readonly
|
|
177
|
+
USING (sensitivity_level < 3);
|
|
178
|
+
|
|
179
|
+
-- Set resource limits to prevent expensive queries
|
|
180
|
+
ALTER ROLE mcp_readonly SET statement_timeout = '10s';
|
|
181
|
+
ALTER ROLE mcp_readonly SET work_mem = '64MB';
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 7.3 Network Access Controls
|
|
185
|
+
|
|
186
|
+
Prevent MCP tools from reaching internal services (SSRF mitigation):
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// src/networkPolicy.ts
|
|
190
|
+
import { URL } from "url";
|
|
191
|
+
import net from "net";
|
|
192
|
+
|
|
193
|
+
const BLOCKED_CIDRS = [
|
|
194
|
+
"10.0.0.0/8",
|
|
195
|
+
"172.16.0.0/12",
|
|
196
|
+
"192.168.0.0/16",
|
|
197
|
+
"127.0.0.0/8",
|
|
198
|
+
"169.254.169.254/32", // Cloud metadata endpoint
|
|
199
|
+
"fd00::/8",
|
|
200
|
+
];
|
|
201
|
+
|
|
202
|
+
const ALLOWED_DOMAINS = [
|
|
203
|
+
"api.github.com",
|
|
204
|
+
"registry.npmjs.org",
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
export function validateOutboundUrl(urlString: string): { allowed: boolean; reason?: string } {
|
|
208
|
+
let parsed: URL;
|
|
209
|
+
try {
|
|
210
|
+
parsed = new URL(urlString);
|
|
211
|
+
} catch {
|
|
212
|
+
return { allowed: false, reason: "Invalid URL" };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Block non-HTTPS
|
|
216
|
+
if (parsed.protocol !== "https:") {
|
|
217
|
+
return { allowed: false, reason: "Only HTTPS is allowed" };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Domain allowlist
|
|
221
|
+
if (!ALLOWED_DOMAINS.includes(parsed.hostname)) {
|
|
222
|
+
return { allowed: false, reason: `Domain ${parsed.hostname} is not in the allowlist` };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return { allowed: true };
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
## 8. Rate Limiting
|
|
233
|
+
|
|
234
|
+
### 8.1 Per-Client Rate Limits
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// src/rateLimit.ts
|
|
238
|
+
|
|
239
|
+
interface RateBucket {
|
|
240
|
+
tokens: number;
|
|
241
|
+
lastRefill: number;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const buckets = new Map<string, RateBucket>();
|
|
245
|
+
|
|
246
|
+
const DEFAULT_RATE = 60; // requests per minute
|
|
247
|
+
const DEFAULT_BURST = 10; // max burst
|
|
248
|
+
|
|
249
|
+
export function checkRateLimit(
|
|
250
|
+
clientId: string,
|
|
251
|
+
maxPerMinute: number = DEFAULT_RATE,
|
|
252
|
+
burst: number = DEFAULT_BURST
|
|
253
|
+
): { allowed: boolean; retryAfterMs?: number } {
|
|
254
|
+
const now = Date.now();
|
|
255
|
+
let bucket = buckets.get(clientId);
|
|
256
|
+
|
|
257
|
+
if (!bucket) {
|
|
258
|
+
bucket = { tokens: burst, lastRefill: now };
|
|
259
|
+
buckets.set(clientId, bucket);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Refill tokens based on elapsed time
|
|
263
|
+
const elapsed = now - bucket.lastRefill;
|
|
264
|
+
const refill = (elapsed / 60000) * maxPerMinute;
|
|
265
|
+
bucket.tokens = Math.min(burst, bucket.tokens + refill);
|
|
266
|
+
bucket.lastRefill = now;
|
|
267
|
+
|
|
268
|
+
if (bucket.tokens < 1) {
|
|
269
|
+
const waitMs = ((1 - bucket.tokens) / maxPerMinute) * 60000;
|
|
270
|
+
return { allowed: false, retryAfterMs: Math.ceil(waitMs) };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
bucket.tokens -= 1;
|
|
274
|
+
return { allowed: true };
|
|
275
|
+
}
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### 8.2 Token Budget Enforcement
|
|
279
|
+
|
|
280
|
+
Limit how many LLM tokens a single session can consume through tool calls:
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
// src/tokenBudget.ts
|
|
284
|
+
|
|
285
|
+
interface SessionBudget {
|
|
286
|
+
usedInputTokens: number;
|
|
287
|
+
usedOutputTokens: number;
|
|
288
|
+
maxInputTokens: number;
|
|
289
|
+
maxOutputTokens: number;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const sessionBudgets = new Map<string, SessionBudget>();
|
|
293
|
+
|
|
294
|
+
export function initSessionBudget(sessionId: string, maxInput = 500_000, maxOutput = 100_000) {
|
|
295
|
+
sessionBudgets.set(sessionId, {
|
|
296
|
+
usedInputTokens: 0,
|
|
297
|
+
usedOutputTokens: 0,
|
|
298
|
+
maxInputTokens: maxInput,
|
|
299
|
+
maxOutputTokens: maxOutput,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function consumeTokens(
|
|
304
|
+
sessionId: string,
|
|
305
|
+
inputTokens: number,
|
|
306
|
+
outputTokens: number
|
|
307
|
+
): { allowed: boolean; remaining: { input: number; output: number } } {
|
|
308
|
+
const budget = sessionBudgets.get(sessionId);
|
|
309
|
+
if (!budget) return { allowed: false, remaining: { input: 0, output: 0 } };
|
|
310
|
+
|
|
311
|
+
budget.usedInputTokens += inputTokens;
|
|
312
|
+
budget.usedOutputTokens += outputTokens;
|
|
313
|
+
|
|
314
|
+
const remaining = {
|
|
315
|
+
input: budget.maxInputTokens - budget.usedInputTokens,
|
|
316
|
+
output: budget.maxOutputTokens - budget.usedOutputTokens,
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
if (remaining.input < 0 || remaining.output < 0) {
|
|
320
|
+
return { allowed: false, remaining };
|
|
321
|
+
}
|
|
322
|
+
return { allowed: true, remaining };
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
## 9. Audit Logging
|
|
330
|
+
|
|
331
|
+
### 9.1 Structured Tool Invocation Logging
|
|
332
|
+
|
|
333
|
+
Log every tool call with full context. Never log raw secrets or credentials.
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// src/auditLog.ts
|
|
337
|
+
import { randomUUID } from "crypto";
|
|
338
|
+
|
|
339
|
+
interface AuditEntry {
|
|
340
|
+
id: string;
|
|
341
|
+
timestamp: string;
|
|
342
|
+
userId: string;
|
|
343
|
+
sessionId: string;
|
|
344
|
+
toolName: string;
|
|
345
|
+
parameters: Record<string, unknown>;
|
|
346
|
+
result: "success" | "error" | "denied";
|
|
347
|
+
durationMs: number;
|
|
348
|
+
error?: string;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const REDACT_KEYS = ["password", "token", "secret", "api_key", "authorization"];
|
|
352
|
+
|
|
353
|
+
function redactSensitive(params: Record<string, unknown>): Record<string, unknown> {
|
|
354
|
+
const redacted: Record<string, unknown> = {};
|
|
355
|
+
for (const [key, value] of Object.entries(params)) {
|
|
356
|
+
if (REDACT_KEYS.some((k) => key.toLowerCase().includes(k))) {
|
|
357
|
+
redacted[key] = "[REDACTED]";
|
|
358
|
+
} else if (typeof value === "object" && value !== null) {
|
|
359
|
+
redacted[key] = redactSensitive(value as Record<string, unknown>);
|
|
360
|
+
} else {
|
|
361
|
+
redacted[key] = value;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
return redacted;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function logToolInvocation(entry: Omit<AuditEntry, "id" | "timestamp">): AuditEntry {
|
|
368
|
+
const full: AuditEntry = {
|
|
369
|
+
...entry,
|
|
370
|
+
id: randomUUID(),
|
|
371
|
+
timestamp: new Date().toISOString(),
|
|
372
|
+
parameters: redactSensitive(entry.parameters),
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
// Write structured JSON to stdout for collection by log aggregator
|
|
376
|
+
process.stdout.write(JSON.stringify(full) + "\n");
|
|
377
|
+
return full;
|
|
378
|
+
}
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### 9.2 OpenTelemetry Integration
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
// src/otelTracing.ts
|
|
385
|
+
import { trace, SpanStatusCode, context, propagation } from "@opentelemetry/api";
|
|
386
|
+
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
387
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
388
|
+
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
389
|
+
import { Resource } from "@opentelemetry/resources";
|
|
390
|
+
|
|
391
|
+
const provider = new NodeTracerProvider({
|
|
392
|
+
resource: new Resource({
|
|
393
|
+
"service.name": "mcp-server",
|
|
394
|
+
"service.version": "1.0.0",
|
|
395
|
+
}),
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
provider.addSpanProcessor(
|
|
399
|
+
new BatchSpanProcessor(
|
|
400
|
+
new OTLPTraceExporter({ url: "http://otel-collector:4318/v1/traces" })
|
|
401
|
+
)
|
|
402
|
+
);
|
|
403
|
+
provider.register();
|
|
404
|
+
|
|
405
|
+
const tracer = trace.getTracer("mcp-server");
|
|
406
|
+
|
|
407
|
+
export async function traceToolCall<T>(
|
|
408
|
+
toolName: string,
|
|
409
|
+
userId: string,
|
|
410
|
+
params: Record<string, unknown>,
|
|
411
|
+
fn: () => Promise<T>
|
|
412
|
+
): Promise<T> {
|
|
413
|
+
return tracer.startActiveSpan(`mcp.tool.${toolName}`, async (span) => {
|
|
414
|
+
span.setAttribute("mcp.tool.name", toolName);
|
|
415
|
+
span.setAttribute("mcp.user.id", userId);
|
|
416
|
+
span.setAttribute("mcp.params.keys", Object.keys(params).join(","));
|
|
417
|
+
|
|
418
|
+
try {
|
|
419
|
+
const result = await fn();
|
|
420
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
421
|
+
return result;
|
|
422
|
+
} catch (err: any) {
|
|
423
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
424
|
+
span.recordException(err);
|
|
425
|
+
throw err;
|
|
426
|
+
} finally {
|
|
427
|
+
span.end();
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
## 10. Deployment Hardening
|
|
437
|
+
|
|
438
|
+
### 10.1 Docker Container Configuration
|
|
439
|
+
|
|
440
|
+
```dockerfile
|
|
441
|
+
# Dockerfile.mcp-server
|
|
442
|
+
FROM node:22-slim AS build
|
|
443
|
+
WORKDIR /app
|
|
444
|
+
COPY package*.json ./
|
|
445
|
+
RUN npm ci --ignore-scripts
|
|
446
|
+
COPY src/ src/
|
|
447
|
+
COPY tsconfig.json ./
|
|
448
|
+
RUN npm run build
|
|
449
|
+
|
|
450
|
+
FROM node:22-slim
|
|
451
|
+
RUN groupadd -r mcp && useradd -r -g mcp -d /home/mcp -s /usr/sbin/nologin mcp
|
|
452
|
+
|
|
453
|
+
# Remove unnecessary packages
|
|
454
|
+
RUN apt-get purge -y --auto-remove curl wget && \
|
|
455
|
+
rm -rf /var/lib/apt/lists/*
|
|
456
|
+
|
|
457
|
+
WORKDIR /app
|
|
458
|
+
COPY --from=build /app/dist ./dist
|
|
459
|
+
COPY --from=build /app/node_modules ./node_modules
|
|
460
|
+
COPY --from=build /app/package.json ./
|
|
461
|
+
|
|
462
|
+
# Create minimal writable directories
|
|
463
|
+
RUN mkdir -p /home/mcp/workspace /home/mcp/output && \
|
|
464
|
+
chown -R mcp:mcp /home/mcp
|
|
465
|
+
|
|
466
|
+
USER mcp
|
|
467
|
+
|
|
468
|
+
# Health check
|
|
469
|
+
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
|
|
470
|
+
CMD node -e "fetch('http://localhost:3001/health').then(r => process.exit(r.ok ? 0 : 1))"
|
|
471
|
+
|
|
472
|
+
EXPOSE 3001
|
|
473
|
+
CMD ["node", "dist/index.js"]
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
### 10.2 Kubernetes Network Policy
|
|
477
|
+
|
|
478
|
+
```yaml
|
|
479
|
+
# k8s/network-policy.yaml
|
|
480
|
+
apiVersion: networking.k8s.io/v1
|
|
481
|
+
kind: NetworkPolicy
|
|
482
|
+
metadata:
|
|
483
|
+
name: mcp-server-policy
|
|
484
|
+
namespace: ai-platform
|
|
485
|
+
spec:
|
|
486
|
+
podSelector:
|
|
487
|
+
matchLabels:
|
|
488
|
+
app: mcp-server
|
|
489
|
+
policyTypes:
|
|
490
|
+
- Ingress
|
|
491
|
+
- Egress
|
|
492
|
+
ingress:
|
|
493
|
+
# Only allow traffic from the agent orchestrator
|
|
494
|
+
- from:
|
|
495
|
+
- podSelector:
|
|
496
|
+
matchLabels:
|
|
497
|
+
app: agent-orchestrator
|
|
498
|
+
ports:
|
|
499
|
+
- protocol: TCP
|
|
500
|
+
port: 3001
|
|
501
|
+
egress:
|
|
502
|
+
# Allow DNS
|
|
503
|
+
- to:
|
|
504
|
+
- namespaceSelector: {}
|
|
505
|
+
ports:
|
|
506
|
+
- protocol: UDP
|
|
507
|
+
port: 53
|
|
508
|
+
# Allow access to the internal database
|
|
509
|
+
- to:
|
|
510
|
+
- podSelector:
|
|
511
|
+
matchLabels:
|
|
512
|
+
app: postgres
|
|
513
|
+
ports:
|
|
514
|
+
- protocol: TCP
|
|
515
|
+
port: 5432
|
|
516
|
+
# Allow HTTPS outbound to specific external APIs
|
|
517
|
+
- to:
|
|
518
|
+
- ipBlock:
|
|
519
|
+
cidr: 0.0.0.0/0
|
|
520
|
+
except:
|
|
521
|
+
- 10.0.0.0/8
|
|
522
|
+
- 172.16.0.0/12
|
|
523
|
+
- 192.168.0.0/16
|
|
524
|
+
ports:
|
|
525
|
+
- protocol: TCP
|
|
526
|
+
port: 443
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
### 10.3 Seccomp Profile
|
|
530
|
+
|
|
531
|
+
```json
|
|
532
|
+
{
|
|
533
|
+
"defaultAction": "SCMP_ACT_ERRNO",
|
|
534
|
+
"architectures": ["SCMP_ARCH_X86_64"],
|
|
535
|
+
"syscalls": [
|
|
536
|
+
{
|
|
537
|
+
"names": [
|
|
538
|
+
"read", "write", "close", "fstat", "lseek", "mmap", "mprotect",
|
|
539
|
+
"munmap", "brk", "rt_sigaction", "rt_sigprocmask", "ioctl",
|
|
540
|
+
"access", "pipe", "select", "sched_yield", "mremap", "madvise",
|
|
541
|
+
"dup", "dup2", "nanosleep", "getpid", "socket", "connect",
|
|
542
|
+
"accept", "sendto", "recvfrom", "bind", "listen", "getsockname",
|
|
543
|
+
"getpeername", "setsockopt", "getsockopt", "clone", "execve",
|
|
544
|
+
"exit", "wait4", "fcntl", "getdents64", "getcwd", "chdir",
|
|
545
|
+
"openat", "newfstatat", "readlinkat", "exit_group", "epoll_create1",
|
|
546
|
+
"epoll_ctl", "epoll_wait", "eventfd2", "futex", "set_robust_list",
|
|
547
|
+
"clock_gettime", "getrandom", "statx", "pread64", "pwrite64"
|
|
548
|
+
],
|
|
549
|
+
"action": "SCMP_ACT_ALLOW"
|
|
550
|
+
}
|
|
551
|
+
]
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Apply the seccomp profile in your Kubernetes pod spec:
|
|
556
|
+
|
|
557
|
+
```yaml
|
|
558
|
+
# k8s/deployment.yaml (relevant snippet)
|
|
559
|
+
spec:
|
|
560
|
+
containers:
|
|
561
|
+
- name: mcp-server
|
|
562
|
+
image: registry.example.com/mcp-server:1.0
|
|
563
|
+
securityContext:
|
|
564
|
+
runAsNonRoot: true
|
|
565
|
+
runAsUser: 1000
|
|
566
|
+
readOnlyRootFilesystem: true
|
|
567
|
+
allowPrivilegeEscalation: false
|
|
568
|
+
capabilities:
|
|
569
|
+
drop: ["ALL"]
|
|
570
|
+
seccompProfile:
|
|
571
|
+
type: Localhost
|
|
572
|
+
localhostProfile: profiles/mcp-server-seccomp.json
|
|
573
|
+
resources:
|
|
574
|
+
limits:
|
|
575
|
+
memory: "512Mi"
|
|
576
|
+
cpu: "500m"
|
|
577
|
+
requests:
|
|
578
|
+
memory: "256Mi"
|
|
579
|
+
cpu: "250m"
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
## 11. Testing
|
|
586
|
+
|
|
587
|
+
### 11.1 Authorization Boundary Tests
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
// tests/toolAuth.test.ts
|
|
591
|
+
import { describe, it, expect } from "vitest";
|
|
592
|
+
import { isToolAllowed } from "../src/toolAuth";
|
|
593
|
+
|
|
594
|
+
describe("Tool Authorization", () => {
|
|
595
|
+
it("should deny developer access to execute_command", () => {
|
|
596
|
+
expect(isToolAllowed("developer", "execute_command")).toBe(false);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
it("should allow developer access to read_file", () => {
|
|
600
|
+
expect(isToolAllowed("developer", "read_file")).toBe(true);
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
it("should allow admin access to everything", () => {
|
|
604
|
+
expect(isToolAllowed("admin", "execute_command")).toBe(true);
|
|
605
|
+
expect(isToolAllowed("admin", "delete_file")).toBe(true);
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
it("should deny unknown roles", () => {
|
|
609
|
+
expect(isToolAllowed("unknown-role", "read_file")).toBe(false);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it("should deny readonly-agent access to write_file", () => {
|
|
613
|
+
expect(isToolAllowed("readonly-agent", "write_file")).toBe(false);
|
|
614
|
+
});
|
|
615
|
+
});
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### 11.2 Input Validation Fuzz Tests
|
|
619
|
+
|
|
620
|
+
```typescript
|
|
621
|
+
// tests/validation.test.ts
|
|
622
|
+
import { describe, it, expect } from "vitest";
|
|
623
|
+
import { validateToolInput } from "../src/validation";
|
|
624
|
+
|
|
625
|
+
describe("Input Validation", () => {
|
|
626
|
+
const maliciousInputs = [
|
|
627
|
+
{ path: "../../../etc/passwd" },
|
|
628
|
+
{ path: "/etc/shadow" },
|
|
629
|
+
{ path: "file\x00.txt" },
|
|
630
|
+
{ path: "a".repeat(1000) },
|
|
631
|
+
{ path: "valid.txt", extraField: "injected" },
|
|
632
|
+
];
|
|
633
|
+
|
|
634
|
+
for (const input of maliciousInputs) {
|
|
635
|
+
it(`should reject malicious read_file input: ${JSON.stringify(input)}`, () => {
|
|
636
|
+
const result = validateToolInput("read_file", input);
|
|
637
|
+
expect(result.valid).toBe(false);
|
|
638
|
+
});
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
it("should accept valid read_file input", () => {
|
|
642
|
+
const result = validateToolInput("read_file", { path: "src/index.ts" });
|
|
643
|
+
expect(result.valid).toBe(true);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const sqlInjections = [
|
|
647
|
+
{ query: "SELECT 1; DROP TABLE users;", database: "analytics" },
|
|
648
|
+
{ query: "SELECT * FROM t UNION SELECT password FROM users", database: "analytics" },
|
|
649
|
+
{ query: "DELETE FROM users WHERE 1=1", database: "analytics" },
|
|
650
|
+
];
|
|
651
|
+
|
|
652
|
+
for (const input of sqlInjections) {
|
|
653
|
+
it(`should reject SQL injection: ${input.query.slice(0, 40)}...`, () => {
|
|
654
|
+
const result = validateToolInput("query_database", input);
|
|
655
|
+
// Even if schema validation passes, the SQL sanitizer should catch it
|
|
656
|
+
expect(result.valid).toBe(true); // schema is valid
|
|
657
|
+
// The SQL check happens at the handler level - tested separately
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
});
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### 11.3 SSRF Prevention Tests
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
// tests/networkPolicy.test.ts
|
|
667
|
+
import { describe, it, expect } from "vitest";
|
|
668
|
+
import { validateOutboundUrl } from "../src/networkPolicy";
|
|
669
|
+
|
|
670
|
+
describe("Outbound URL Validation", () => {
|
|
671
|
+
const blockedUrls = [
|
|
672
|
+
"http://169.254.169.254/latest/meta-data/",
|
|
673
|
+
"http://localhost:8080/admin",
|
|
674
|
+
"https://evil.com/exfiltrate",
|
|
675
|
+
"ftp://internal-server/data",
|
|
676
|
+
"https://10.0.0.1:8443/internal",
|
|
677
|
+
"http://[::1]/admin",
|
|
678
|
+
];
|
|
679
|
+
|
|
680
|
+
for (const url of blockedUrls) {
|
|
681
|
+
it(`should block: ${url}`, () => {
|
|
682
|
+
expect(validateOutboundUrl(url).allowed).toBe(false);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
it("should allow requests to explicitly allowed domains", () => {
|
|
687
|
+
expect(validateOutboundUrl("https://api.github.com/repos").allowed).toBe(true);
|
|
688
|
+
expect(validateOutboundUrl("https://registry.npmjs.org/express").allowed).toBe(true);
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
### 11.4 Rate Limit Tests
|
|
694
|
+
|
|
695
|
+
```typescript
|
|
696
|
+
// tests/rateLimit.test.ts
|
|
697
|
+
import { describe, it, expect } from "vitest";
|
|
698
|
+
import { checkRateLimit } from "../src/rateLimit";
|
|
699
|
+
|
|
700
|
+
describe("Rate Limiting", () => {
|
|
701
|
+
it("should allow requests within the burst limit", () => {
|
|
702
|
+
const clientId = "test-burst-" + Date.now();
|
|
703
|
+
for (let i = 0; i < 10; i++) {
|
|
704
|
+
expect(checkRateLimit(clientId).allowed).toBe(true);
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
|
|
708
|
+
it("should deny requests exceeding the burst limit", () => {
|
|
709
|
+
const clientId = "test-exceed-" + Date.now();
|
|
710
|
+
for (let i = 0; i < 10; i++) {
|
|
711
|
+
checkRateLimit(clientId);
|
|
712
|
+
}
|
|
713
|
+
const result = checkRateLimit(clientId);
|
|
714
|
+
expect(result.allowed).toBe(false);
|
|
715
|
+
expect(result.retryAfterMs).toBeGreaterThan(0);
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
---
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
## Quick Reference Checklist
|
|
724
|
+
|
|
725
|
+
Use this checklist when deploying any MCP server to production:
|
|
726
|
+
|
|
727
|
+
```
|
|
728
|
+
[ ] TLS 1.3 enabled on all HTTP transports
|
|
729
|
+
[ ] mTLS configured for server-to-server communication
|
|
730
|
+
[ ] OAuth 2.1 or API key authentication enforced on every endpoint
|
|
731
|
+
[ ] Tool allowlist defined per role/user
|
|
732
|
+
[ ] Dangerous tools flagged and require explicit approval
|
|
733
|
+
[ ] JSON Schema validation on every tool's parameters
|
|
734
|
+
[ ] SQL queries use parameterized statements and are restricted to SELECT
|
|
735
|
+
[ ] Filesystem access sandboxed to explicit directories
|
|
736
|
+
[ ] Outbound network requests limited to an allowlist (SSRF mitigation)
|
|
737
|
+
[ ] Per-client rate limits and session token budgets enforced
|
|
738
|
+
[ ] Every tool invocation logged with user, params (redacted), and result
|
|
739
|
+
[ ] OpenTelemetry tracing integrated for observability
|
|
740
|
+
[ ] Container runs as non-root with read-only filesystem
|
|
741
|
+
[ ] Seccomp profile applied to restrict syscalls
|
|
742
|
+
[ ] Network policies restrict pod-to-pod communication
|
|
743
|
+
[ ] Authorization and input validation tests pass in CI
|
|
744
|
+
```
|
|
745
|
+
|