Repository navigation
fix(security): apply service connection visibility to MCP and ingestion clients - #36312
mohityadav766 wants to merge 4 commits into
Conversation
…on clients MCP get_entity and patch_entity drop a service's connection for callers without EditAll on it, through the ServiceConnectionAccess check that ServiceEntityResource now shares; editors still get it masked. Ingestion copes with a connection that does not come back: dbt takes the lineage SQL dialect from the service's serviceType instead of failing lineage for every model, and other workflows and the data quality SDK fail with an error naming the token as the cause. Amends the service-connection visibility and MCP credential-masking decision records.
❌ PR checklist incompleteThis PR cannot be merged until the following are addressed on its linked issue:
The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically. Maintainers can bypass this check by adding the |
…age dialect basedpyright reports the optional serviceConnection config and serviceName as new errors.
jsonpatch 1.38 types JsonPatch.patch as a Sequence, so basedpyright rejects the append that adds array-field replace operations and fails the Python static checks. The patches built there always hold a list, so the append is unchanged at runtime.
…ponse PATCH, follow and unfollow, restore and the timestamp history now pass the service through decryptOrNullify, as GET and list do. A stored version that cannot be decrypted is returned without its connection, and a JSON Patch that copies or moves from another path needs EditAll on the service.
| private void decryptOrNullifyVersion(SecurityContext securityContext, T version) { | ||
| try { | ||
| decryptOrNullify(securityContext, version); | ||
| } catch (Exception e) { | ||
| nullifyConnection(version); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Quality: Version decrypt failure is swallowed silently via catch (Exception)
decryptOrNullifyVersion catches every Exception and quietly removes the connection. That covers the case the comment describes (a ciphertext that can't be decrypted), but it also hides secrets-manager outages, misconfiguration and authorization lookup errors. In those cases a bot reading /history silently gets versions with no connection, and nothing is logged. The repo rules forbid catch (Exception e) and require at least a log line. Catch the specific exception that decryption or masking throws (for example InvalidServiceConnectionException, or the secrets-manager exception type) and log the version id and number at WARN before removing the connection.
Catch the specific failure and log it before removing the connection:
private void decryptOrNullifyVersion(SecurityContext securityContext, T version) {
try {
decryptOrNullify(securityContext, version);
} catch (InvalidServiceConnectionException e) {
LOG.warn(
"Version {} of service {} could not be decrypted; returning it without connection",
version.getVersion(),
version.getId(),
e);
nullifyConnection(version);
}
}
- Apply fix
Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎
Code Review 👍 Approved with suggestions 0 closed / 1 findings🔴 High risk · The new EditAll gate controls service-connection visibility across REST and MCP; a misapplied check could expose stored credentials. Applies the service connection visibility rule to MCP get_entity and patch_entity, REST PATCH, follow, restore and history responses, and dbt lineage dialect fallback. Approved with suggestions: 💡 Quality: Version decrypt failure is swallowed silently via catch (Exception)
Catch the specific failure and log it before removing the connection🤖 Prompt for agentsVerificationRules No rules evaluated Functional validation Not enabledAuto-approval Not enabledFor more verification details, comment OptionsDisplay: compact → Showing compact verification results. Comment with these commands to change the behavior for this request:
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source |
|



Describe your changes:
Follow-up to #36219, which returns a service connection only to bots and, masked, to callers who can edit the service.
get_entityandpatch_entitynow apply the same rule. A caller withoutEditAllon the service gets it withoutconnection; editors still get it masked. The check moved toServiceConnectionAccess, whichServiceEntityResourcenow shares./historynow pass the service throughdecryptOrNullify, as GET and list do. A stored version that can't be decrypted comes back without its connection, and a JSON Patchcopy/moveon a service needs EditAll.serviceType. Before, lineage failed for every model.patch_request.pygets a type-onlycast. jsonpatch 1.38 (released 2026-10-26) typesJsonPatch.patchas aSequence, so basedpyright fails the Python static checks, and with thempy-tests-status, on every PR. The patch there always holds a list, so runtime behavior is unchanged.2026-10-26-service-connection-visibility-follows-edit-permissionand2026-10-20-mcp-responses-mask-stored-credentials(v2 each).Type of change:
Tests:
MetadataServiceResourceIT: patch, follow,/history,copyand restore responses per role (they fail before this change);ServiceEntityResourceTest.ServiceConnectionAccessTest;McpResponseUtilsTest,GetEntityToolTest,PatchEntityToolTest: a viewer gets no connection, an editor gets it masked.test_dbt.py::TestLineageDialectWithoutConnection,test_base_workflow.py(missing connection, dbt),test_workflow_config_builder.py(no connection).🤖 Generated with Claude Code