Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 120 additions & 6 deletions .bestpractices.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
"sharpfuzz.commandline": {
"version": "2.3.0",
"commands": ["sharpfuzz"]
},
"coverlet.console": {
"version": "10.1.0",
"commands": ["coverlet"]
}
}
}
42 changes: 42 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Repository-wide formatting and the C# style rules that CI enforces.
# `dotnet format style postgresql-sharp-mcp.slnx --verify-no-changes --no-restore --severity warn`
# fails on any warning below; see CONTRIBUTING.md#coding-standards.
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true

[*.{yml,yaml,json,csproj,props,slnx,md}]
indent_size = 2

[*.cs]
# Using directives: no unused ones, outside the namespace.
dotnet_diagnostic.IDE0005.severity = warning
csharp_using_directive_placement = outside_namespace:warning
# File-scoped namespaces.
csharp_style_namespace_declarations = file_scoped:warning
# Explicit accessibility on every non-interface member.
dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning

# Naming (.NET Framework Design Guidelines): PascalCase types and members, I-prefixed interfaces.
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum, delegate
dotnet_naming_symbols.members.applicable_kinds = method, property, event
dotnet_naming_symbols.members.applicable_accessibilities = public, internal, protected, protected_internal
dotnet_naming_symbols.interfaces.applicable_kinds = interface
dotnet_naming_style.pascal_case.capitalization = pascal_case
dotnet_naming_style.i_prefix.capitalization = pascal_case
dotnet_naming_style.i_prefix.required_prefix = I
dotnet_naming_rule.interfaces_i_prefix.symbols = interfaces
dotnet_naming_rule.interfaces_i_prefix.style = i_prefix
dotnet_naming_rule.interfaces_i_prefix.severity = warning
dotnet_naming_rule.types_pascal.symbols = types
dotnet_naming_rule.types_pascal.style = pascal_case
dotnet_naming_rule.types_pascal.severity = warning
dotnet_naming_rule.members_pascal.symbols = members
dotnet_naming_rule.members_pascal.style = pascal_case
dotnet_naming_rule.members_pascal.severity = warning
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,19 @@ jobs:
node-version: '24'
- name: Restore locked dependencies
run: dotnet restore postgresql-sharp-mcp.slnx --locked-mode
- name: Enforce C# coding style (.editorconfig)
run: dotnet format style postgresql-sharp-mcp.slnx --verify-no-changes --no-restore --severity warn
- name: Real PostgreSQL MCP integration
run: dotnet run --project tools/PostgreSqlMcp.Verify -c Release -- integration
# Re-runs the same scenarios with FLOSS coverlet instrumenting the real server processes; fails below the line-coverage gate.
- name: Measure statement coverage of the integration scenarios
run: dotnet run --project tools/PostgreSqlMcp.Verify -c Release -- coverage --threshold 80
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
if: ${{ !cancelled() }}
with:
name: coverage
path: artifacts/coverage/coverage.cobertura.xml
if-no-files-found: warn
- name: Build npm and NuGet packages with .NET
run: dotnet run --project tools/PostgreSqlMcp.Build -c Release -- package --output artifacts/packages
- name: Install and exercise both distributions
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## Unreleased

Upgrade note: connections to non-loopback hosts now verify the server certificate unless the connection string sets `SSL Mode` explicitly (see the first entry). Everything else below is documentation, policy and tooling only.

- **Breaking:** verified TLS by default. A connection string (base `POSTGRES_CONNECTION_STRING`/`--connection-string` or any targets JSON/file profile) that does not set `SSL Mode` previously got Npgsql's `Prefer`, which uses TLS without verifying the server and silently falls back to plaintext. It now gets `SSL Mode=VerifyFull` when its host is not loopback (certificate chain and host name are verified before any credential is sent, with no plaintext or unverified fallback) and `SSL Mode=Disable` when every host is `localhost`, `127.0.0.0/8`, `::1` or a Unix-domain socket (no TLS is used, so no TLS without verification; a `Root Certificate` on such a profile selects `VerifyFull`). An explicit `SSL Mode`, including `Prefer`, `Require` and `Disable`, is the operator's opt-out and is honored unchanged; the existing startup/`--validate` `[warning]` still flags non-loopback profiles without `VerifyCA`/`VerifyFull`. New fixed errors, on stderr for `--validate` and as structured tool errors, name only the profile and the fix: `tls_verification_failed` (untrusted CA or host name not in the certificate: add `Root Certificate=<CA file>`, or opt out with `SSL Mode=Require`) and `tls_unavailable` (the server offers no TLS: enable it, or opt out with `SSL Mode=Disable`). **Upgrade guidance:** servers with a publicly trusted certificate need no change; a private CA needs `Root Certificate=<absolute CA file path>`; a non-TLS remote server, including a host PostgreSQL reached as `host.docker.internal`, needs `SSL Mode=Disable`; a loopback or forwarded server that requires TLS (`hostssl` rules) needs an explicit `SSL Mode`. To restore the old behavior for a profile add `SSL Mode=Prefer` explicitly. Run `--validate` before reconnecting your client. `--help`, README, SECURITY and the assurance case are updated. Verifier: a disposable TLS container with a throwaway CA proves that an unset mode refuses an unverifiable non-loopback server before the login is sent (the server log shows no user), succeeds with `Root Certificate`, honors `Require` and `Disable` with the warning, rejects a host name missing from a trusted certificate, and uses plaintext on loopback.
- A connection the server resets during login now reports `connection_error` instead of `internal_error` ("Unexpected server failure"), and the startup database-lock check reports it as a connection finding instead of failing. Npgsql 10.0.3 can let a raw `SocketException` escape while it applies a socket timeout to a connection that was already reset; the package verifier hit this on macOS when opening a loopback profile without TLS.
- Add [GOVERNANCE.md](GOVERNANCE.md) (decision-making, roles and what happens if the sole maintainer is unavailable; it states that `access_continuity` is not met), [docs/architecture.md](docs/architecture.md), [docs/roadmap.md](docs/roadmap.md) and [docs/assurance-case.md](docs/assurance-case.md) (requirements, threat model, trust boundaries, secure-design principles, common weaknesses and residual risks). GOVERNANCE.md, the roadmap and the assurance case are unconfirmed maintainer drafts written by an AI coding agent and say so at the top; the assurance case's review record is pending the maintainer.
- CONTRIBUTING: add coding standards, code-review standards and small-task guidance. Add an `.editorconfig` and a CI step (`dotnet format style --verify-no-changes --severity warn`) that enforces the C# style rules. Source files gain SPDX copyright and license headers, and unused `using` directives are removed.
- SECURITY: document how vulnerability reports are handled.
- Propose Silver and Gold Best Practices answers in `.bestpractices.json`, and record the live Scorecard figures, the reference project's figures, the criteria that remain unmet and an owner checklist in [docs/security-posture.md](docs/security-posture.md). Nothing is saved on bestpractices.dev until the owner reviews it.
- Make the `.nupkg` and the CycloneDX SBOM reproducible, so every release asset except the per-run provenance bundle is byte-identical across rebuilds of the same source, and `SHA256SUMS` with them. `tools/PostgreSqlMcp.Build` passes NuGet's supported `DeterministicTimestamp` property to `dotnet pack`, fixing every zip entry time at 1980-01-01 (previously the wall clock; the entry order, contents and `nuget.psmdcp` name were already stable, so published package layouts are unchanged). `release-tool sbom` rewrites dotnet-CycloneDX's random `serialNumber` and wall-clock `metadata.timestamp` (the generator can only omit the serial number and cannot fix the timestamp) to a version 8 UUID derived from the SHA-256 of the BOM and the checked-out commit's time; the BOM stays valid CycloneDX JSON with the same properties and component order. `release-regressions` now proves both offline (SBOM normalization and two `dotnet pack` runs of a probe project more than two seconds apart), and `packages` rejects a `.nupkg` entry that carries a build-time stamp. The new `.nupkg` entry timestamps and SBOM fields apply to the next release; already published versions are unchanged.
- Verifier and CI: measure statement coverage of the real server under the integration scenarios with the pinned FLOSS coverlet console tool. `tools/PostgreSqlMcp.Verify -- coverage [--output dir] [--threshold n]` instruments the three shipped assemblies, runs `integration --no-build` (new flag that skips the build step) against the disposable Docker fixture, prints per-assembly line, branch and method coverage with the files and methods the scenarios never reach, and writes a Cobertura report. The CI `verify` job runs it with a gate of 80% total line coverage and uploads the report as the `coverage` artifact. The first measurement was 89.41% lines, 84.84% branches and 98.38% methods in total, excluding only compiler-generated `[GeneratedRegex]` code. It is a test-infrastructure change: no shipped runtime behavior changes.
- README troubleshooting: a new row explains that `npx` (and an MCP client's npx entry) hides install-script output, so a failed setup, such as a missing platform package, exits without printing anything (exit code 127 on Linux), and shows how to reveal the message with `--foreground-scripts`. The missing-platform row now also names Windows on Arm among the platforms without a platform package. The main package's `postinstall` stays a bare command; CONTRIBUTING explains why a shell fallback message is not used.

## 0.5.0
Expand Down
Loading
Loading