Skip to content

importObject's opts.name override bypasses the namespace prefix and persists an unprefixed federated object #11061

Description

@os-warren

Found while working #10712 (PR #11059), which gave generateObjectDraft a
${namespace}_ prefix derived from the datasource's owning package. Filed rather than
fixed there: #10712 is scoped to the draft output, and this is a different path with
a different question behind it. Unassigned, awaiting first-touch grading.

What

ExternalDatasourceService.importObject reuses the draft pipeline, so it inherits the
prefix — but only when the caller does not name the object itself:

constname=opts.name??draft.name;

opts.name is taken verbatim. A caller passing { "name": "customers" } gets an object
persisted as customers, with no ${namespace}_ prefix, via
persistObjectmetadata.register('object', name, definition).

Reachable today

Not hypothetical, and not dependent on #10712's change. ImportObjectOpts extends GenerateDraftOpts with name?: string ("Override the auto-derived object name"), and
the import route forwards the request body as opts unfiltered
packages/rest/src/external-datasource-routes.ts:462:

constresult=awaitsvc.importObject(req.params.name,/* remote */,(req.bodyasRecord<string,unknown>)??{},);

So POST /api/v1/datasources/:name/external/tables/:remote/import with
{"name": "customers"} reaches it.

Why this is not simply "the caller owns the name"

That reading is defensible on its own, and may well be the right ruling — but the
runtime write path this lands on does not apply the check that would catch it.
validateObjectNamespacePrefix runs in two places: defineStack() (compile time) and
MetadataManager.publishPackage / the protocol's publish pre-flight (the Studio
"publish" path, NAMESPACE_PREFIX). metadata.register('object', …) is neither. So an
imported object with an explicitly unprefixed name is registered and served without any
gate having looked at the name.

Note the in-tree callers do not currently exercise this: objectui's
ImportObjectDialog posts no name, and os datasource introspect does not import at
all. The exposure is the route body.

The question a first-touch should answer

Three shapes, none of which a dev should pick unilaterally:

  • A. Apply the prefix to opts.name too (same validateObjectNamespacePrefix
    treatment as the derived name). Consistent, but silently rewrites a name the caller
    explicitly asked for.
  • B. Refuse an opts.name that fails the prefix rule, with the rule's own actionable
    message. Contract-first, and matches what the publish gate does with the same
    violation — but it turns a currently-accepted request into a 4xx.
  • C. Leave it: an explicit override is the caller's decision. Then the gap worth
    closing is arguably the broader one — that metadata.register('object', …) applies no
    namespace check at all — which is a much wider question than this seam.

Related: #10712 / PR #11059 (the draft half, where the prefix derivation lives),
#11000 (the unauthorable primaryKey key in the same generator).

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions