{
  "question": "Our MCP server lets operators list actions to block or to confirm with the user first (a deny list and a confirm list), matched by name. The AWS API MCP Server advisory published November 10, 2026 says its lists never matched some operations. How does a deny list miss the exact call it names, and how do we test ours?",
  "slug": "why-an-mcp-servers-tool-deny-list-can-silently-miss-dangerous-calls",
  "url": "https://www.rightshold.com/answers/why-an-mcp-servers-tool-deny-list-can-silently-miss-dangerous-calls",
  "short_answer": "Because the list is a string the operator wrote and the check compares it with a string the server builds from the call, and if the two are built differently they never match, no error is raised and the call goes through. In the AWS API MCP Server (versions 0.2.13 up to 1.5.1), operators write entries in AWS CLI form such as \"aws iam create-saml-provider\", but the server turned the API's operation name into that form with a regex that put a hyphen before every capital letter, so CreateSAMLProvider became create-s-a-m-l-provider and a correctly written denyList or elicitList entry never matched. AWS's advisory (GHSA-4wq9-grf3-qg2p, CVE-2026-125271, high, published November 10, 2026) gives creating a federated identity provider, deactivating an MFA device and deleting database clusters as examples of calls that could run anyway. The fix in 1.5.1 uses botocore's own name conversion, and there is no workaround other than upgrading. The general lesson: a name-matched list fails open on any mismatch, so build the comparison key with the same code that routes the call, test real entries for names with acronyms, and back the list with a control that fails closed, such as IAM permissions or an allow list.",
  "checklist": [
    "If you run the AWS API MCP Server locally with ~/.aws/aws-api-mcp/mcp-security-policy.json, upgrade to awslabs.aws-api-mcp-server 1.5.1 or later (1.6.0 was the newest on PyPI on November 11, 2026). The remote AWS API MCP Server offering and the READ_OPERATIONS_ONLY and REQUIRE_MUTATION_CONSENT settings weren't affected.",
    "Check CloudTrail for the period you ran 0.2.13 to 1.5.0 for any operation in your denyList or elicitList whose name contains consecutive capitals (SAML, MFA, SSH, OpenID, DB and similar). Those are the ones the policy couldn't have stopped or asked about.",
    "In your own server, find where a call becomes the string you compare against the list. Build it with the same function that dispatches the call or with the upstream SDK's converter, never a hand-written regex, and normalize case and separators once, on both sides.",
    "Add tests that load the policy file exactly as an operator would write it and assert that every entry blocks a real call. Include names with acronyms and digits, names that differ only by case, underscores versus hyphens, and aliases or a second command path that reaches the same operation.",
    "Make the policy loader refuse to start, or warn loudly, when an entry matches no known operation. A typo or a converter mismatch then shows up at startup instead of as silence.",
    "Prefer an allow list for anything that can delete, change identity settings or move money: with an allow list, a mismatch blocks the call instead of letting it through.",
    "Keep a control outside the MCP server that fails closed. The AWS server's README says IAM permissions remain the primary security control and the security policy cannot override IAM restrictions; give the credentials the server runs with only the permissions its job needs."
  ],
  "sections": [
    {
      "heading": "How the AWS API MCP Server policy missed its own entries",
      "points": [
        "The server's README tells operators to write each entry as aws <service> <operation> with the CLI's kebab-case operation name, matched by exact string with no wildcards. At decision time the server took the SDK operation name (for example CreateSAMLProvider) and converted it with a regex that inserted a hyphen before every capital letter and lowercased the result. For most operations that matches the CLI name; for any name with consecutive capitals it doesn't, so CreateVirtualMFADevice, UploadSSHPublicKey and AddClientIDToOpenIDConnectProvider produced strings no operator would ever write.",
        "The fix, released as 1.5.1 on August 10, 2026 and disclosed in the advisory three months later, replaces the regex with botocore's xform_name, the conversion the AWS SDK itself uses, in both places the policy is checked, and adds tests that deny four acronym operations and still allow a neighbouring one. The advisory says exploiting it needed a locally run server with a custom policy listing affected operations and user interaction with the AI assistant."
      ]
    },
    {
      "heading": "Why name-matched deny lists fail open",
      "points": [
        "A deny list answers one question: does this call equal an entry? Every way the two sides can differ (case, separators, acronyms, aliases, a second command or tool that reaches the same API, an argument that changes what a call does) is a way to get no match, and no match means allow. Nothing tells the operator; the policy file looks right and the server logs nothing unusual.",
        "The same holds for confirm lists. An elicitList entry that never matches means the call runs without asking, which is worse than having no list, because the operator believes a person will be asked. Treat a confirm list as a convenience on top of permissions, not as the permission."
      ]
    },
    {
      "heading": "How to test a deny list before you trust it",
      "points": [
        "Test the policy as data, not the code path alone: for every entry an operator writes, dispatch the real call it names against a stub backend and assert it is refused or asks for confirmation. Generate the cases from the upstream service model where you can, so every operation name, including ones with acronyms, is covered.",
        "Run the same suite in CI on every dependency bump, and log each policy decision with the exact key that was compared. If a log shows a key that no entry could ever match, such as a word split into single letters, the converter is wrong."
      ]
    }
  ],
  "checks": [],
  "ask_rightshold": {
    "start": "https://www.rightshold.com/start?ref=seo",
    "mcp_endpoint": "https://www.rightshold.com/mcp",
    "ask": "Before my assistant runs this AWS call through the MCP server, ask me to approve that exact operation, so a confirm list that silently doesn't match can't be the only thing standing between the assistant and an identity change.",
    "mcp_call": {
      "tool": "request_human_approval",
      "arguments": {
        "action": {
          "tool": "aws iam create-saml-provider",
          "api_operation": "iam:CreateSAMLProvider",
          "account_ref": "<your AWS account alias>",
          "mcp_server": "awslabs.aws-api-mcp-server"
        },
        "summary": "Approve creating a SAML identity provider in the production AWS account",
        "expires_in_minutes": 30
      }
    },
    "also": [
      "Needs a key for your account. It hashes the action details and returns an approval id and a one-time token for the owner. Only the account owner can decide it, with an account key; delegated agent keys are refused, so the assistant can't approve its own request. Anyone can check the signed decision free with verify_human_approval, including that the operation matches what was approved.",
      "It only covers calls your agent or harness routes through it before running them. It doesn't read your MCP server's policy file, check how that server names operations, or stop a call the server runs on its own; upgrade the server, test your lists and keep IAM permissions tight."
    ],
    "scope": "Rightshold isn't affiliated with AWS. It doesn't scan MCP server code or policy files, and an approval record isn't a finding that a server's own lists work. Advisory details are as published by AWS on GitHub and in its security bulletin on November 10, 2026, and the fix as shown in the linked commit; release dates are as listed on PyPI, read November 11, 2026."
  },
  "sources": [
    {
      "title": "AWS (awslabs/mcp): GHSA-4wq9-grf3-qg2p, AWS API MCP Server security policy bypass via incorrect operation name normalization (CVE-2026-125271)",
      "url": "https://github.com/awslabs/mcp/security/advisories/GHSA-4wq9-grf3-qg2p"
    },
    {
      "title": "AWS Security Bulletin 2026-176-AWS: CVE-2026-125271 (November 10, 2026)",
      "url": "https://aws.amazon.com/security/security-bulletins/2026-176-aws/"
    },
    {
      "title": "awslabs/mcp commit 52bf00157e: use botocore.xform_name for operation name conversion in security policy",
      "url": "https://github.com/awslabs/mcp/commit/52bf00157e"
    },
    {
      "title": "AWS API MCP Server README: custom security policy configuration (denyList, elicitList, exact string matching)",
      "url": "https://github.com/awslabs/mcp/blob/main/src/aws-api-mcp-server/README.md"
    },
    {
      "title": "PyPI: awslabs.aws-api-mcp-server release history",
      "url": "https://pypi.org/project/awslabs.aws-api-mcp-server/"
    }
  ],
  "related": [
    {
      "title": "How to require human confirmation before MCP write tools run",
      "url": "https://www.rightshold.com/answers/how-to-require-human-confirmation-before-mcp-write-tools-run"
    },
    {
      "title": "How to keep MCP as a thin adapter and recheck authorization on every tool call",
      "url": "https://www.rightshold.com/answers/how-to-keep-mcp-as-a-thin-adapter-and-recheck-authorization-on-every-tool-call"
    },
    {
      "title": "How to ask the user to confirm an action from an MCP server with elicitation",
      "url": "https://www.rightshold.com/answers/how-to-ask-the-user-to-confirm-an-action-from-an-mcp-server-with-elicitation"
    },
    {
      "title": "How to restrict an MCP database tool to read-only queries",
      "url": "https://www.rightshold.com/answers/how-to-restrict-an-mcp-database-tool-to-read-only-queries"
    }
  ],
  "computed_from": "Curated guidance; license verdicts computed from Rightshold's license profiles at render time. Not legal advice.",
  "updated": "2026-11-11"
}