microsoft / microsoft/multiclouddb-sdk-for-java
π Deep Repository Review: 16 Critical, 34 High, 48 Medium findings
@allenkim0129 is already working on this.
Since Jul 8, 2026.
- Dominant language
- Java
- Stars
- 7
- Forks
- 7
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 1
Description
π Deep Repository Review: microsoft/multiclouddb-sdk-for-java
Date: 2026-04-27
Repo: https://github.com/microsoft/multiclouddb-sdk-for-java
Reviewer: Copilot deep-review pipeline (7 parallel agents)
π Aggregate Finding Counts
| Severity | API/Arch | Cosmos DB | DynamoDB | Spanner | Query DSL | CI/CD | Tests | Total |
|---|---|---|---|---|---|---|---|---|
| π΄ CRITICAL | 3 | 2 | 0 | 3 | 2 | 3 | 3 | 16 |
| π HIGH | 7 | 4 | 4 | 4 | 5 | 6 | 4 | 34 |
| π‘ MEDIUM | 8 | 6 | 7 | 8 | 6 | 7 | 6 | 48 |
| π’ LOW | 6 | 4 | 7 | 6 | 5 | 5 | 3 | 36 |
| Total | 24 | 16 | 18 | 21 | 18 | 21 | 16 | 134 |
π¨ CRITICAL Findings (Must Fix Before GA)
C1. Spanner Provider Storage Model Is Fundamentally Broken
Source: SpannerProviderClient.java, SpannerConstants.java
DDL creates tables with 3 columns (partitionKey, sortKey, data STRING(MAX)), but writeMutationFields() writes each document field as an individual Spanner column. The data column is never written to. Every write on a provisioned table fails.
Fix: Serialize docs to JSON in data column, or generate DDL matching the document schema.
C2. SQL Injection via Unquoted Table Names in Spanner
Source: SpannerExpressionTranslator.java, SpannerProviderClient.java
Table names injected via String.format() without backtick-quoting. "users; DROP TABLE users--" achieves full injection. Cosmos (fixed c alias) and DynamoDB (double-quotes) are safe.
Fix: Backtick-quote all identifiers + validate names against [a-zA-Z_][a-zA-Z0-9_]*.
C3. String Literal Injection in Expression Translators
Source: All three translator files
Inline string literals bypass parameterization. Spanner translator also missing backslash escaping.
C4. Spanner ABORTED Mapped to CONFLICT Instead of Retry
Source: SpannerErrorMapper.java
Should be TRANSIENT_FAILURE per GCP docs.
C5. No Maven Central Publishing in Release Pipeline
Source: .github/workflows/release.yml
No GPG signing, no Sonatype, no distributionManagement. Consumers can't <dependency> on this.
C6. No Spanner Emulator Integration Tests in CI
CI covers Cosmos + DynamoDB but not Spanner. Regressions ship undetected.
C7. Mutable Exception β Thread Safety Violation
Source: MulticloudDbException.java
withDiagnostics() mutates non-final field in-place. Data race across threads.
C8. Dead Conformance Tests β Never Execute
ResultSetControlConformanceTest and TtlAndMetadataConformanceTest are abstract with zero subclasses. Spanner missing from SortKeyOrderingConformanceTest.
C9. No Document Size Validation in Cosmos Provider
399KB limit mentioned in README but not enforced in provider write path.
π HIGH Findings
API & Architecture
| ID | Finding |
|---|---|
| A-H1 | Jackson ObjectNode leaked in public API β DocumentResult returns ObjectNode, writes use Map |
| A-H2 | Inconsistent document type across read/write/query |
| A-H3 | read() returns null instead of Optional<DocumentResult> |
| A-H4 | Missing TIMEOUT and PRECONDITION_FAILED error categories |
| A-H5 | expressionTranslator volatile + setter β race condition window |
| A-H6 | QueryPage deep-copies all items on construction |
| A-H7 | No module-info.java β internal package accessible |
Cosmos DB Provider
| ID | Finding | Best Practice |
|---|---|---|
| Co-H1 | No retry-after for 429 throttling | MS 429 docs |
| Co-H2 | Default Gateway mode (not Direct) β latency penalty | MS Direct vs Gateway |
| Co-H3 | RU charge missing from point-op diagnostics | |
| Co-H4 | Consistency level hardcoded β not configurable |
DynamoDB Provider
| ID | Finding | Best Practice |
|---|---|---|
| Dy-H1 | Client-side sort on scan mixes partitions | Scan has no ordering |
| Dy-H2 | No Binary/Binary Set support β data silently dropped | AWS Data Types |
| Dy-H3 | toAttributeValue() can't handle List/Map params |
|
| Dy-H4 | update() bypasses DynamoErrorMapper |
Spanner Provider
| ID | Finding | Best Practice |
|---|---|---|
| Sp-H1 | OFFSET pagination is O(nΒ²) | GCP keyset pagination |
| Sp-H2 | TRANSACTIONS_CAP declared but unimplemented |
GCP R/W Transactions |
| Sp-H3 | Spanner client leak on construction failure | |
| Sp-H4 | ORDER BY fields not sanitized β injection |
Query DSL
| ID | Finding |
|---|---|
| Q-H1 | No recursion depth limit β StackOverflow |
| Q-H2 | No expression length limit β OOM |
| Q-H3 | BetweenExpression/InExpression accept unvalidated Objects |
| Q-H4 | Silent no-op on unknown Expression types |
| Q-H5 | DynamoDB reserved words not quoted (status, name, countβ¦) |
CI/CD
| ID | Finding |
|---|---|
| CI-H1 | No static analysis (Checkstyle/SpotBugs/PMD) |
| CI-H2 | No JaCoCo coverage |
| CI-H3 | No Dependabot/Renovate |
| CI-H4 | No OWASP dependency-check |
| CI-H5 | Release has no integration test gate |
| CI-H6 | junit-jupiter missing <scope>test</scope> in conformance POM |
Tests
| ID | Finding |
|---|---|
| T-H1 | No thread-safety tests |
| T-H2 | No Testcontainers β manual emulator setup |
| T-H3 | Spanner has only 2 unit tests |
| T-H4 | Inconsistent @Tag usage |
π‘ MEDIUM Findings (48 total β summarized by area)
API (8): Missing equals/hashCode on error types, no timeout validation, capability naming collision, unbounded thread pool in provisionSchema, missing toString fields, no @Nullable annotations
Cosmos (6): No throughput/indexing in provisioning, sync-only API, query results leak system fields, setMaxBufferedItemCount misuse, capability docs wrong on TTL field name, no projection support
DynamoDB (7): No 400KB size check, binary keys break pagination, strong consistency not configurable, incompatible token formats, reserved words unquoted, number precision loss, TTL not enabled on table
Spanner (8): read() uses SQL not readRow(), translator doesn't quote tables, BATCH/CHANGE_FEED caps unimplemented, toString() for complex objects, fragile WHERE detection, brittle DDL error check, missing type handling
Query DSL (6): Negative number ambiguity, dead isAtBetweenAnd(), no function arity validation, Cosmos ignores container param, Literal accepts arbitrary Objects, DynamoDB drops missing params
CI/CD (7): Javadoc lint disabled, PAT for docs deploy, test modules not excluded from deploy, no dependency convergence, no BOM, stale plugins, no permissions block
Tests (6): Size test only on DynamoDB, fragile @Order cleanup, new client per test, no edge cases, user-agent test only on DynamoDB
β What's Done Well
| Area | Assessment |
|---|---|
| API Design | Clean builders, good Javadoc, solid SPI, extensible value objects |
| Error Model | Comprehensive categories with retryability flags |
| Query DSL | Sealed interfaces + records, correct precedence, parameterized queries |
| DynamoDB | Excellent error mapping, correct Query vs Scan routing, PAY_PER_REQUEST default |
| Cosmos | Singleton client, session consistency, point-read optimization, user-agent |
| Module Structure | Clean multi-module layout, independent versioning, centralized deps |
| Community | Complete CONTRIBUTING, SECURITY, CLA, CODE_OF_CONDUCT, PR/issue templates |
| Tests | 281+ tests, good parser/validator/translator coverage |
Provider Best Practice Cross-Check
Azure Cosmos DB
| Practice | Status |
|---|---|
| Singleton client | β |
| Direct mode default | β οΈ Defaults to Gateway |
| Session consistency | β |
| Retry-after on 429 | β |
| RU tracking | β οΈ Queries only |
| Point read via readItem | β |
| User-agent tagging | β |
Amazon DynamoDB
| Practice | Status |
|---|---|
| AWS SDK v2 | β |
| PAY_PER_REQUEST | β |
| Query vs Scan routing | β |
| GetItem for reads | β |
| ReturnConsumedCapacity | β (except PartiQL) |
| Reserved word quoting | β |
| Binary support | β Silently dropped |
| TTL enablement | β |
Google Cloud Spanner
| Practice | Status |
|---|---|
| readRow for point reads | β Uses SQL |
| Keyset pagination | β Uses OFFSET |
| R/W transactions | β Bare mutations |
| ABORTED retry | β Mapped to CONFLICT |
| Identifier quoting | β Injection risk |
| JSON document storage | β DDL mismatch |
| Emulator support | β |
π― Top 10 Priority Actions
| # | Action | Impact |
|---|---|---|
| 1 | Fix Spanner storage model (serialize to data JSON column) |
Provider non-functional |
| 2 | Fix SQL injection (backtick-quote Spanner identifiers) | Security |
| 3 | Fix Spanner ABORTED β TRANSIENT_FAILURE | Retry handling |
| 4 | Add parser recursion/length limits | DoS prevention |
| 5 | Unify document type to Map<String, Object> |
API consistency |
| 6 | Add Spanner CI tests + fill conformance gaps | Coverage |
| 7 | Quote DynamoDB field names in PartiQL | Correctness |
| 8 | Enable TTL on DynamoDB tables during provisioning | Silent failure |
| 9 | Add Dependabot + OWASP + SpotBugs | Security/quality |
| 10 | Cosmos: default Direct mode + configurable consistency | Performance |
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up β it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.