Research 19 min read Sep 8, 2026 9

Path Containment Is Not Ownership: Testing Cross-Tenant Artifact References

Owning a report does not grant access to every artifact it references. We examine how stored references can cross tenant boundaries even when paths and target permissions appear valid.

ApplicationSecurity APISecurity BOLA MultiTenantSecurity Authorization ArtifactSecurity AutonomousPentesting
S
SecureHup Team
Security Researcher · SecureHup
Path Containment Is Not Ownership: Testing Cross-Tenant Artifact References

A user in tenant Beta creates a report object it legitimately owns and sets its artifact reference to /runs/alpha/artifacts/result.bin. The path is valid and contained beneath run Alpha's artifact directory. The create endpoint checks permission on the new Beta report but not read authority on the Alpha source. A later download follows the stored reference under Beta's valid target permission.

No traversal or malformed identifier is required. The authorization failure is relational: the operation joins a source object and a target object, then protects only one end. Copy, alias, snapshot, preview, export, cache, and background-worker paths can all repeat that mistake.

Testing needs a valid two-tenant object graph. Vary source permission and target permission independently, store references through every supported route, and follow each consumer to the eventual file effect. Path containment proves location; it cannot prove ownership or an authorized transfer.

Key Takeaways

  • Path containment proves a filesystem or URI relationship; it does not prove principal, tenant, or object-level authorization.
  • Creating a reference requires authorization over both the target object and the source artifact, plus permission for the specific linking or copying action.
  • A later read-through endpoint must preserve source authorization or rely on a deliberate, auditable transfer of ownership; target permission alone cannot silently launder access.
  • Reference, alias, copy, snapshot, import, and derived artifact have different revocation and ownership semantics that must be defined explicitly.
  • Autonomous pentesting should use valid objects in two synthetic tenants, build cross-object references, and inspect actual artifact bytes and audit state rather than testing malformed paths only.
  • Durable remediation models artifact identity and ownership as structured relationships, centralizes authorization, and replays indirect access paths in CI.

Two Different Questions: Location and Authority

Path containment answers a syntactic or structural question:

Does candidate path P resolve beneath allowed root R?

Authorization answers a relationship question:

May principal U perform action A on source object S
within tenant T under current policy and context C?

Both are necessary. Without containment, a caller may escape an artifact root. Without authorization, a caller may select another user's valid root. Combining the checks into one function obscures which property has actually been proven.

A Valid Source ID Can Still Be Foreign

Applications often accept both a source identifier and source path:

POST /derived-objects
{
  "name": "my-target",
  "source_container_id": "run-A",
  "source": "/runs/run-A/artifacts"
}

The server loads run-A, confirms the path is inside its artifact directory, and stores the reference. If it does not use an authorization-aware source lookup, knowing or guessing a valid source ID can be enough. The path check makes the request internally consistent while leaving ownership untested.

The safer sequence is:

  1. Authenticate the principal.
  2. Authorize creation or management of the target object.
  3. Load the source through a permission-enforcing service.
  4. Authorize the requested source action: read, link, copy, derive, export, or transfer.
  5. Verify source and target tenant/policy compatibility.
  6. Resolve and contain paths within the authorized source.
  7. Persist a typed reference with immutable source identity and policy semantics.
  8. Reauthorize or enforce the transfer decision on every later read.

Containment belongs after source authorization, not instead of it.

How Permission Laundering Works

The attack is an object-graph sequence:

Victim tenant owns source container S and artifact F
Attacker may manage target container D
Attacker cannot read S or F directly

1. Attacker creates D with a reference to S/F
2. Create endpoint checks D permission and path containment
3. Missing check: attacker may read/link S/F
4. D stores the foreign source reference
5. Attacker calls a download/read endpoint authorized on D
6. Endpoint follows D -> S/F and returns F

The target object launders authority. Permission to manage D becomes indirect permission to read F. This resembles broken object-level authorization, but the vulnerable object is a relationship rather than one direct URL parameter.

Why Direct Endpoint Tests Miss It

A security test may call the canonical source download endpoint with the foreign source ID and receive 403. That looks correct. The bypass lives in another domain endpoint that dereferences the stored link. Testing each endpoint independently misses the chain.

Code-aware analysis helps identify where a source URI is accepted, persisted, transformed, and later consumed. Runtime tests prove whether the indirect consumer invokes the same authorization service as the direct source endpoint.

Artifact References Need Explicit Semantics

Not every cross-object relationship should behave the same way.

Relationship Data behavior Authorization expectation Revocation question
Live reference Target reads current source bytes Reauthorize source on each access or use a deliberate delegation Does source permission removal immediately block target?
Alias Alternate name for same object Exact same policy as source identity Can alias outlive source or tenant membership?
Copy New bytes stored under target ownership Caller must read/export source and create target; transfer policy applies Does later source revocation affect copied bytes?
Snapshot Immutable point-in-time copy/reference Source read plus snapshot permission; immutable identity recorded Who owns retention and deletion?
Derived artifact New output computed from source Source-use permission, target creation, and data-classification policy Can output reveal source after source access is revoked?
Import External or cross-domain artifact becomes local Import policy, provenance, scanning, and target ownership Is original identity retained for audit?
Shared link Delegated access to source Explicit audience, scope, expiry, revocation Can recipient convert it into permanent ownership?
Export bundle Collection crosses boundary Every included object authorized and classified Are late-added files included without recheck?

Product requirements may deliberately allow a copy to survive later source revocation. That is not automatically a bug if the transfer was authorized, visible, and governed when the copy was created. A live reference should usually not become an implicit permanent copy merely because one download endpoint caches it.

Define Ownership Transfer, Do Not Infer It

If attaching an artifact to a target transfers ownership, say so in the domain model and require a transfer capability. Record source owner, target owner, actor, purpose, time, classification, retention, and approval. Do not let a generic source string cause the transfer as a side effect.

Object Identity Must Be Structured

Raw paths and URIs are poor authorization handles. They combine storage location, transport, and identity in strings that different components parse differently. Prefer a typed reference:

ArtifactReference {
  artifact_id
  source_container_id
  source_tenant_id
  immutable_digest_or_version
  relationship_type
  authorization_mode
  created_by_principal
  policy_version
  created_at
  expiry_or_retention
}

The storage layer resolves the path from authoritative metadata after authorization. Clients do not supply absolute server paths. If an external URI is supported, it receives a separate fetch, provenance, and network policy rather than masquerading as an internal artifact ID.

Avoid Trusting Duplicated Ownership Fields

An artifact record may include tenant_id, while its parent run, experiment, project, or repository also has ownership. If these values can drift, different endpoints may authorize against different fields. Define one authoritative relationship and enforce consistency transactionally.

Denormalized tenant IDs can improve query performance, but they need integrity constraints and tests. They must not become client-controlled shortcuts around parent ownership.

Authorization Must Cover Both Ends of the Edge

A relationship creation operation involves at least:

  • Acting principal.
  • Source object and source tenant.
  • Target object and target tenant.
  • Relationship type.
  • Intended action and purpose.
  • Data classification and residency.
  • Current roles and delegated grants.
  • Retention, sharing, and revocation policy.

A useful invariant is:

create_edge(P, S, D, R) only if
  P may perform R on S,
  P may create/manage D,
  S and D satisfy tenant and data-transfer policy,
  and later consumers enforce R's declared authorization semantics.

Checking only mayManage(D) proves half the operation. Checking only mayRead(S) ignores whether the caller may publish or transfer it into D. Read permission does not always include export or cross-tenant sharing.

Service Accounts Do Not Remove User Authorization

Background workers may read artifact storage through a global service identity. That is an implementation detail, not permission for the initiating user. The job must carry immutable actor, source, target, tenant, and authorized relationship context. At execution, it should confirm that the grant remains valid or follow the documented snapshot/transfer decision.

Do not serialize only a raw source URI into the queue. The worker cannot reconstruct why it was authorized.

Recheck Authorization at the Right Time

There are two broad patterns.

Live Authorization

The target remains a reference. Every read resolves the current source and verifies the caller's source permission. This supports immediate revocation but can make shared or published objects unavailable when source relationships change.

Authorized Materialization

At creation, the system verifies export/copy permission and creates target-owned immutable bytes. Later reads authorize against the target because a deliberate transfer occurred. Audit, classification, retention, and deletion policies must reflect the copied data.

The unsafe middle is storing a live foreign reference but authorizing later reads only against the target, without an explicit transfer. That gives copy-like authority with reference-like implementation and no transfer decision.

Time-of-Check and Revocation

For asynchronous copy or derivation, source permission can be removed after job creation but before execution. Decide whether authorization is evaluated at request time, execution time, or both. High-risk cross-tenant transfers should usually recheck at execution. If a signed approval intentionally authorizes later execution, bind it to source digest, target, actor, purpose, and expiry.

Threat Model for Artifact Relationships

Layer Unsafe assumption Failure Strong invariant
API schema Valid IDs imply valid relationship Foreign source ID accepted Source and target are independently authorized
Path check Contained path is owned path Foreign artifact passes containment Location and authority checks stay separate
Parent lookup Generic get is sufficient Authorization-aware lookup bypassed Protected objects load through policy service
Relationship create Target management implies source use Target launders access Edge action authorizes both ends
Persistence Raw URI preserves meaning Ownership and relationship semantics disappear Typed immutable reference stored
Read-through Target permission covers resolved bytes Foreign source downloaded Live source reauthorization or explicit transfer
Worker Service credential is authorized Job reads globally for unauthorized actor Actor/tenant/grant context follows job
Cache Cached bytes inherit target policy Foreign data becomes permanent Cache key and policy reflect source/transfer
Revocation Permission changes affect every path Indirect link survives removal Lifecycle behavior is explicit and tested
Audit Target log is enough Original source/actor disappears Source, target, edge, actor, and effect recorded

How Autonomous Pentesting Should Test References

The test needs valid objects and relationships, not malformed paths.

Create Synthetic Tenants and Objects

Create tenant A and tenant B. In each, create an owner, administrator, ordinary member, source container, target container, and uniquely marked harmless artifact. Ensure users receive realistic but narrow permissions.

Establish Direct Baselines

Confirm user A can read artifact A and cannot read artifact B through the canonical source endpoint. Confirm user A can manage a target object in tenant A. Record audit and storage state.

Enumerate Edge-Creation Features

Search create-version, attach, import, clone, copy, snapshot, derive, export, bundle, alias, restore, preview, converter, and background-job APIs. Code analysis should find fields such as source, run_id, artifact_uri, parent, input, and origin that later reach storage or download services.

Build Valid Cross-Tenant Edges

Use tenant A's authorized target and tenant B's valid source. Keep the source path correctly contained under B's root. Vary only the ownership relationship. Try direct create, asynchronous job, bulk API, legacy endpoint, and client SDK.

Follow Every Consumer

After a reference is created or partially accepted, attempt download, preview, serving, conversion, export, archive, clone, cache warmup, and API retrieval. A create response alone does not prove disclosure; actual foreign artifact bytes or derived information do.

Exercise Revocation and Mutation

Remove source permission, change tenant membership, delete the source, replace artifact bytes under a mutable label, and change target ownership. Observe whether live references, copies, snapshots, queues, and caches follow their documented semantics.

For SecureHup users, this object-graph approach can turn a suspicious missing check into proof of whether valid cross-tenant references actually expose protected artifact content. Users gain clearer business impact, less time on path-traversal false positives, a source-to-consumer trace developers can fix, and a replayable regression across indirect endpoints. The value lies in verified authorization behavior rather than product-internal claims.

Cross-Tenant Reference Test Cases

Actor Source Target Relationship/action Expected result
Tenant A owner A source A target Live reference Allowed under local policy
Tenant A member with read A source A target they cannot manage Link Denied on target permission
Tenant A target owner B private source A target Live reference Denied on source permission
Tenant A target owner B private source A target Copy/import Denied without source export/transfer grant
Explicit cross-tenant recipient Shared B source A target Expiring reference Allowed only within grant scope and lifetime
Read-only source user A source A public target Publish/export Denied if read does not include publish
Revoked user Previously linked source Existing target Read-through Current live-reference policy denies
Authorized snapshot creator A source digest v1 A target Snapshot Reads immutable v1 under target policy after deliberate copy
Worker job Authorized at request, revoked before run Any Async copy Behavior follows documented execution-time policy
Target ownership changed Live foreign reference New tenant target Download Reauthorize source/transfer; no implicit inheritance
Source label mutated Same ID, new digest Snapshot target Read Snapshot remains bound to approved digest
Cache warmed Foreign source denied A target Preview/download Cache cannot bypass source denial
Bulk operation Mixed authorized/foreign sources A target set Attach Each item receives independent decision; no partial leak
Error path Valid foreign source A target Create/read No source existence, path, or metadata leaked unnecessarily

The suite should verify allowed same-tenant and explicitly shared cases. Universal denial can hide broken authorization behind missing product functionality.

Evidence That Proves an Authorization Bypass

A strong finding records:

  • Synthetic actor, roles, tenant memberships, and current permissions.
  • Source object, artifact ID/digest, owner tenant, and direct-access baseline.
  • Target object, owner tenant, and actor's legitimate target permission.
  • Relationship type and creation endpoint.
  • Supplied source identifier/path and successful containment result.
  • Missing or inconsistent source authorization decision.
  • Persisted reference fields and background-job context.
  • Indirect consumer endpoint and actual harmless marker bytes returned.
  • Cache, preview, export, or derived outputs involved.
  • Audit events and whether source ownership remained visible.
  • Required deployment settings and permission configuration.
  • Fixed-build replay plus authorized control.

Do not use real model weights, training data, customer exports, logs, or reports as proof. Synthetic artifacts establish the path. Severity can then consider what classes of artifact a real deployment stores, which roles can create targets, whether tenants are mutually untrusted, and whether the bypass provides arbitrary files or only a narrow known artifact.

If real cross-tenant data may have been exposed, identify every relationship created under the vulnerable logic, invalidate unsafe links and caches, review download and service logs, notify data owners according to policy, and avoid relying on the target object's audit trail alone. The source system may show no direct read because the bypass used another handler.

Repair the Ownership Binding

  1. Route all source lookups through authorization-aware services.
  2. Require explicit source action permission in addition to target management.
  3. Evaluate tenant, classification, residency, export, and sharing policy for the edge.
  4. Replace raw source paths with typed artifact references and immutable identities.
  5. Define live reference, alias, copy, snapshot, import, and derived-output semantics.
  6. Reauthorize source on read-through or materialize an explicitly authorized transfer.
  7. Bind background jobs to actor, source, target, tenant, relationship, digest, and policy.
  8. Include authorization context in cache keys and invalidate unsafe cache entries.
  9. Add lifecycle handling for revocation, deletion, ownership change, and retention.
  10. Backfill or quarantine existing references whose source authorization cannot be proven.
  11. Add cross-tenant edge and indirect-consumer regressions before re-enabling affected features.

When no corrected upstream version is available or verified for a specific product, mitigate at the deployment and feature level: restrict reference creation, disable indirect artifact endpoints, isolate tenants, and enforce gateway checks. Publication and operations teams should recheck current patch status rather than inventing an upgrade target.

Release Controls for Artifact References

Reference authorization regresses when new artifact consumers, preview endpoints, caches, bulk APIs, migrations, or background workers bypass the central service.

Maintain:

  • Unit tests for source and target action combinations.
  • Property tests over principal-source-target-relationship tuples.
  • Same-tenant, cross-tenant, delegated-share, and revoked-grant tests.
  • Path containment tests kept separate from authorization tests.
  • Direct versus indirect endpoint parity tests.
  • Typed-reference serialization and immutable-digest tests.
  • Worker context, execution-time reauthorization, and idempotency tests.
  • Cache-key, invalidation, preview, export, and archive tests.
  • Ownership transfer, deletion, retention, and tenant-migration lifecycle tests.
  • Audit attribution and non-enumeration tests.
  • End-to-end marker-artifact tests for every edge creator and consumer.

Useful metrics include artifact endpoints with centralized authorization, raw URI reference fields, cross-tenant edge attempts, denied source/target mismatches, existing references lacking provenance, live references after grant revocation, cache entries without source policy, background jobs missing actor context, indirect read parity, and time to invalidate a compromised relationship. Path-traversal test coverage is valuable but does not measure object ownership coverage.

Artifact Ownership Review Questions

Before adding an artifact-reference feature, verify:

  • Source and target are loaded through authorization-aware services.
  • The actor has the exact required action on both ends of the relationship.
  • Tenant, sharing, export, classification, residency, and retention policy permit the edge.
  • Path containment runs only after an authorized source is established.
  • Clients submit opaque object IDs rather than server filesystem paths where possible.
  • Persisted references include immutable source identity, tenant, relationship type, and policy semantics.
  • Live references reauthorize source access on every consumer path.
  • Copies and snapshots require explicit transfer permission and record lineage.
  • Background workers carry immutable actor and authorization context.
  • Caches cannot convert denied live data into target-owned bytes accidentally.
  • Revocation, source deletion, target transfer, and tenant membership changes have defined behavior.
  • Direct download and every indirect preview/export/serve/clone path enforce equivalent policy.
  • Errors avoid foreign-object enumeration while protected logs retain reason codes.
  • Synthetic two-tenant marker tests run after relevant code changes.

Frequently Asked Questions

What is the difference between path containment and authorization?

Path containment verifies that a resolved path remains inside an expected storage root. Authorization verifies that the current principal may perform a specific action on the object represented by that path. A foreign tenant's valid path can pass containment and still be unauthorized.

Is this the same as path traversal?

No. Path traversal escapes an intended directory through path syntax or parser behavior. This class can use a correctly contained path and valid object IDs. The flaw is missing source ownership or object-level authorization.

Should artifact access be checked only when the reference is created?

It depends on relationship semantics. A live reference usually needs current source authorization on each read. A deliberate copy or snapshot can be authorized at transfer time and later governed by target ownership. The system must define and enforce one model explicitly.

Why does target permission not cover the source artifact?

Managing a destination object grants authority over that object, not arbitrary inputs from other users or tenants. Otherwise, any target the caller owns becomes a laundering mechanism for protected source data.

How can autonomous tests find this without accessing real tenant data?

Create two synthetic tenants with unique harmless marker artifacts. Confirm direct cross-tenant reads fail, create valid target objects and cross-references, then follow every indirect consumer. The returned marker and protected state prove the path without exposing customer content.

Ownership Has to Survive the Reference

Fields that create, copy, resolve, cache, preview, export, or download an artifact are all places where source authority can be lost. Typed artifact identities make that relationship more visible than raw URIs, especially when the product distinguishes ordinary references from an explicit, audited transfer of ownership or access.

In a two-tenant test, both the synchronous request and any background consumer should see the same relationship. The core property is that ownership of the destination does not manufacture read authority over an unrelated source. A perfectly legitimate object can still become a cross-tenant tunnel when only one end of the reference is authorized.

#ApplicationSecurity #APISecurity #BOLA #MultiTenantSecurity #Authorization #ArtifactSecurity #AutonomousPentesting

Was this helpful?
Share :

Find issues like this in your own codebase

SecureHup runs this exact exploit chain — discovery, enumeration and cross-tenant validation — on every deployment, automatically.