{
  "schema": "aaes/capability-pack/1",
  "provider": "mssql",
  "version": "1.0.0",
  "description": "Microsoft SQL Server: query one table and insert, update or delete its rows through the native SQL Server adapter and a deployment statement allowlist. Draft awaiting owner review.",
  "notes": "Draft awaiting owner review. Select the SQL Server adapter with native_executors.sql.engine set to sqlserver; every SQL capability in that deployment must then use a sqlserver:// endpoint, and a registration for another engine is refused when the configuration loads. The adapter (aaes/internal/connectors/native_sqlserver.go, microsoft/go-mssqldb v1.11.2, reviewed in ADR-046) connects only to the registered host, port and database. The URL path is the database, never an instance name: the adapter does not query the SQL Server Browser, so a named instance must be registered by its fixed port. It resolves the host through the connectors' pinned egress dial, and its dialer refuses any other host or port, so a routing redirect from an availability group listener or a failover partner cannot move the connection. It requires encryption with TLS 1.2 or later and the certificate verified against the registered host name, and refuses a server that declines encryption before LOGIN7, which carries the password. It uses SQL authentication only; a login name containing a backslash is refused because the driver would switch to NTLM. Statements must match the deployment allowlist byte for byte and must start with SELECT, INSERT, UPDATE, DELETE, MERGE or WITH. A T-SQL batch needs no semicolon between statements, so EXEC, EXECUTE, any sp_ or xp_ procedure, OPENQUERY, OPENROWSET, OPENDATASOURCE, BULK, WAITFOR and the DDL, permission, transaction and administrative keywords are refused anywhere in a statement.\n\nSQL Server has no session read-only setting that the adapter could use. A query capability runs inside a transaction that is always rolled back, so a write that reaches the server is undone; sequence values, identity increments and other non-transactional effects are not undone. The adapter does not set ApplicationIntent=ReadOnly. That intent does not make a primary replica or a standalone server read-only. It only matters in an availability group: through a listener it asks for a read-only routing redirect, which the adapter refuses; a primary set to ALLOW_CONNECTIONS = READ_WRITE rejects it; and a secondary set to ALLOW_CONNECTIONS = READ_ONLY admits only read-intent connections, so the adapter cannot reach such a secondary. A secondary set to ALL accepts the adapter and serves its databases for read access only. Read-only therefore rests on the login's grants and the statement checks, with the rollback as a backstop. The adapter was tested against SQL Server 2022.\n\nDB_HOST, DB_NAME and TABLE_NAME are placeholders. Microsoft SQL Server listens on TCP 1433 by default (a default instance uses TCP 1433, while named instances use dynamic ports unless an administrator fixes one); substitute the real host, port and database in the endpoint, the egress allowlist and the resource locator together.\n\nRead operations are registered at R0, inserts and updates at R2, and deletion at R3. On supported AAES-mediated execution paths, delete requires approval by an authorized person; bypass and observation-only paths are outside that guarantee. The SQL connector's method vocabulary is query or exec, not HTTP methods. A capability named row.insert is a promise about the statement the deployment allowlist will permit, not a check this file makes. Every capability touches one table resource classified pii as a starting point; a deployment whose table holds no personal data may lower it, and one holding payment or health data must raise it.\n\nLeast privilege lives in the database, not in this pack: db_datareader grants SELECT on the whole database and db_datawriter grants INSERT, UPDATE and DELETE on the whole database, which is broader than one table; prefer object-level GRANT SELECT, INSERT, UPDATE or DELETE on the one table for the capabilities you enable. Review the login's grants and the statement allowlist before enabling any write.\n\nAAES has not been certified by the database vendor; the endpoints here are configuration to review and test, and the vendor has not endorsed it.",
  "credential": {
    "secret_ref_id": "secret-ref-mssql-role",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Authorization",
    "note": "A dedicated least-privilege Microsoft SQL Server login held in the client's own secret manager. The adapter reads the material as strict JSON with exactly user and password, presents it only during the TLS-protected login and never interpolates it into a statement. A result that echoes the password is refused. Windows, NTLM, Kerberos, Entra and certificate authentication are not covered."
  },
  "capabilities": [
    {
      "capability_id": "mssql.table.query",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:1433"
      ],
      "endpoint": "sqlserver://DB_HOST:1433/DB_NAME",
      "method": "query",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mssql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "mssql-table",
      "description": "Run an allowlisted SELECT against the table. DB_HOST, DB_NAME and the table behind mssql-table are placeholders.",
      "requires_review": true
    },
    {
      "capability_id": "mssql.row.insert",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:1433"
      ],
      "endpoint": "sqlserver://DB_HOST:1433/DB_NAME",
      "method": "exec",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mssql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create"
      ],
      "resource_id": "mssql-table",
      "description": "Run an allowlisted INSERT against the table. The deployment statement allowlist must hold the exact statement; this registration names the operation and does not supply that control.",
      "requires_review": true
    },
    {
      "capability_id": "mssql.row.update",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:1433"
      ],
      "endpoint": "sqlserver://DB_HOST:1433/DB_NAME",
      "method": "exec",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mssql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "update"
      ],
      "resource_id": "mssql-table",
      "description": "Run an allowlisted UPDATE against the table, registered at the R2 minimum for update. An overwritten value may be unrecoverable without a backup; assess that separately.",
      "requires_review": true
    },
    {
      "capability_id": "mssql.row.delete",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "pii",
      "egress_allowlist": [
        "DB_HOST:1433"
      ],
      "endpoint": "sqlserver://DB_HOST:1433/DB_NAME",
      "method": "exec",
      "connector": "sql",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-mssql-role",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "delete"
      ],
      "resource_id": "mssql-table",
      "description": "Run an allowlisted DELETE against the table. Delete is one of the designated actions requiring human approval, so the operator approval gate applies.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "mssql-table",
      "kind": "table",
      "locator": "sqlserver://DB_HOST:1433/DB_NAME/TABLE_NAME",
      "data_class": "pii",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "draft",
    "date": "2026-10-29",
    "sources": [
      "https://learn.microsoft.com/en-us/sql/sql-server/install/configure-the-windows-firewall-to-allow-sql-server-access?view=sql-server-ver17 (retrieved 2026-10-29)",
      "https://learn.microsoft.com/en-us/sql/relational-databases/security/authentication-access/database-level-roles?view=sql-server-ver17 (retrieved 2026-10-29)",
      "https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/listeners-client-connectivity-application-failover?view=sql-server-ver17 (retrieved 2026-10-29)",
      "https://learn.microsoft.com/en-us/sql/database-engine/availability-groups/windows/configure-read-only-access-on-an-availability-replica-sql-server?view=sql-server-ver17 (retrieved 2026-10-29)",
      "https://learn.microsoft.com/en-us/sql/t-sql/statements/set-transaction-isolation-level-transact-sql?view=sql-server-ver17 (retrieved 2026-10-29)"
    ],
    "reviewer_id": "aaes-eng"
  }
}
