Fence Sensitive Dropbox Paths by Team
Fences protected Dropbox subtrees by path prefix . Dropbox addresses files and folders by a root-relative path (/Finance/2026/payroll.
- Direction
- ingress
- Rego package
dropbox.ingress.fence_sensitive_paths- App
- dropbox
- Bundles
- soc2hipaagdpr-ccpa
- Published
- Minimum gateway
- 1.0.0b24
- Schema version
- 1.0.0
- Checksum
sha256:a21a98aab34382aadfad11cf231ef36da8229ffd562ebc31200ab803061e646e
dropboxfence-sensitive-scopesingresssoc2hipaagdpr-ccpa
What this policy does
Direction: ingress (tool_pre_invoke)
Default: deny fenced targets for callers outside the mapped team, allow otherwise
Package: dropbox.ingress.fence_sensitive_paths
What it does
Fences protected Dropbox subtrees by path prefix. Dropbox addresses files
and folders by a root-relative path (/Finance/2026/payroll.xlsx), so the
policy carries a placeholder fenced_prefixes map that pairs a top-level path
prefix with the IdP team group required to touch anything under it — /HR/ →
hr, /Finance/ → finance, /Legal/ → legal, /Customers/ →
customers. At ingress it gates every path-addressed Dropbox tool and
denies, for callers who lack the mapped group:
- Reads, listings, moves, copies, writes, and deletes —
ListFolder,GetFileMetadata,GetFileContent,CreateFile,Copy,Move,Delete(official server) and their community synonyms (list_files,get_file_metadata,get_file_content,download_file,upload_file,copy_item,move_item,safe_delete_itemondbx-mcp-server;dropbox_list,dropbox_get_metadata,dropbox_download,dropbox_upload,dropbox_copy,dropbox_move,dropbox_deleteonngs) — when any path-bearing argument falls under a fenced prefix. - Search —
Search(official),search_file_db(dbx),dropbox_search(ngs). A search scoped to apath(oroptions.path) under a fenced prefix is denied for callers lacking that group. Because a search that supplies no path can surface names of files in any fenced tree (the same enumeration risk asListFolder), the fail-closed rule below denies it as well — an agent cannot enumerate restricted filenames throughListFolderorSearch.
A caller may touch a protected prefix only when their
input.subject.claims.groups include the matching team group; everyone else
is denied. Group membership is read from input.subject.claims.groups via
object.get chains and fails closed: a missing, empty, or non-array
groups claim never grants access to a fenced target.
The policy fails closed on the path too: a protected tool whose path
argument is missing, empty, or not a string (an evasion attempt) is treated as
unauthorised rather than allowed — the fence cannot verify a target it
cannot read, so the request is denied. The prefix check runs
case-insensitively on a normalised path: the path is lowercased, trimmed,
given a single leading slash, and has runs of / collapsed to one (so
//finance/x cannot dodge the /finance/ prefix) before comparison, and
matching is prefix-aware — /finance fences /finance and /finance/... but
not the sibling /finance-public. All tools this policy does not inspect pass
through untouched.
Compliance alignment
- SOC 2 C1.1 — supports identification and protection of confidential information by gating agent access to designated confidential Dropbox trees to their mapped groups; P4.1 — supports limiting personal-information use to identified purposes by keeping PI-bearing folders behind team fences on the agent channel.
- HIPAA §164.308(a)(4) — supports information access management: access to PHI-bearing Dropbox folders is authorized by IdP group on the MCP path; §164.502(b) / §164.514(d) — supports the minimum-necessary standard by fencing PHI folders to the minimal team that needs them.
- GDPR Art. 9 — supports special-category protection by fencing folders holding health, HR, or other Art. 9 data; CPRA §1798.121 — supports the right to limit use of sensitive personal information by fencing SPI paths to a minimal group; Art. 5(1)(b) — supports purpose limitation by keeping each protected tree accessible only to its owning team.
Tool name matching
Tool names are matched case-insensitively as an exact name or by -/_-
separated suffix, so the policy tolerates any gateway server-name prefix (e.g.
dropbox-GetFileContent):
- Official (
mcp.dropbox.com):listfolder,getfilemetadata,getfilecontent,createfile,copy,move,delete,search. - Community (
dbx-mcp-server):list_files,get_file_metadata,get_file_content,download_file,upload_file,copy_item,move_item,safe_delete_item,search_file_db. - Community (
ngs):dropbox_list,dropbox_get_metadata,dropbox_download,dropbox_upload,dropbox_copy,dropbox_move,dropbox_delete,dropbox_search.
Share-link and external-URL tools are intentionally not matched here — gate
those with guard-share-links-external.
Verify the exact names your gateway sends with the dump-input debug technique
before relying on this in production.
Argument shape
Dropbox does not publish MCP JSON schemas, so the argument names are
unverified and follow the Dropbox API v2 (files/*, files/search_v2) and
the community server READMEs — see Known limitations. The policy reads the path
defensively from the likely keys: path, from_path, to_path, src,
dest, source, destination, and the search scope from path /
options.path. Before shipping a policy pack, capture a live tools/list
through the gateway and pin the real argument key(s) — if the live tool
names the path under a key not in this list, that fence silently does not fire
(though a present-but-non-string path still fails closed). Dropbox id:...
handles are not resolvable to a path statelessly and are not fenced (see
Known limitations).
Examples
Allowed — unfenced path
{
"input": {
"action": "tool_pre_invoke",
"resource": { "name": "dropbox-GetFileContent", "type": "tool" },
"subject": { "sub": "google-apps|dev@example.com", "claims": { "groups": ["engineering"] } },
"payload": {
"name": "dropbox-GetFileContent",
"args": { "path": "/Projects/roadmap.pdf" } // not under a fenced prefix
}
}
}
allow = true, no reason.
Denied — fenced path, caller lacks the team group
{
"input": {
"action": "tool_pre_invoke",
"resource": { "name": "dropbox-GetFileContent", "type": "tool" },
"subject": { "sub": "google-apps|dev@example.com", "claims": { "groups": ["engineering"] } },
"payload": {
"name": "dropbox-GetFileContent",
"args": { "path": "/Finance/2026/payroll.xlsx" } // under /Finance/ -> finance
}
}
}
allow = false, reason = "Dropbox path '/Finance/2026/payroll.xlsx' is inside the protected '/finance/' folder, which is restricted to the 'finance' group. (...)".
Denied — protected tool with no readable path (fail closed)
{
"input": {
"action": "tool_pre_invoke",
"resource": { "name": "dropbox-Search", "type": "tool" },
"subject": { "sub": "google-apps|dev@example.com", "claims": { "groups": ["engineering"] } },
"payload": {
"name": "dropbox-Search",
"args": { "query": "salary" } // no path scope -> cannot verify -> deny
}
}
}
allow = false, reason = "This Dropbox call is a protected file operation but supplied no readable path (...)".
Composition
This policy is single-purpose: it fences every path-addressed read, listing, move, copy, write, delete, and search of pinned sensitive path prefixes by team. Useful companions:
guard-share-links-external— so fenced content that an authorized caller reads cannot be re-shared outward via a public link or file request.- An egress PII/PHI redaction policy on
GetFileContent/download_fileandSearchresponses — it also mops up filenames returned by an allowed listing of an unfenced ancestor (see Known limitations). - A destructive-op gate for
RestoreFolder/RestoreFileRevision, which this policy does not cover.
Known limitations
- Prefix matching only — no path canonicalization or ID resolution. The
policy matches the literal (lowercased, leading-slash-normalized, repeated
slashes collapsed) path against its prefixes. It does not resolve Dropbox
file
id:...handles,ns:<namespace_id>/...namespace-relative paths, shared-link URLs, or../.traversal tricks (Dropbox itself does not resolve../., so those literal forms do not reach the real file, but a file legitimately addressed by itsid:orns:form is not fenced). Pin the sensitive subtree's path prefixes at import time and, where your workflow usesid:/ns:addressing, pair with an egress redaction policy. - Fail-closed on missing/unparseable path denies pathless calls. A
protected tool with no readable string path — a
Searchwith only a query, aListFolderof the root, or a path supplied under an unrecognized key — is denied rather than allowed. This is deliberate: it is what stops an agent from enumerating fenced filenames through an unscopedListFolder/Search, but it means legitimate whole-drive searches must be re-scoped to a path the caller may access. - Broad operations on an unfenced ancestor can still surface fenced
descendants. A recursive
ListFolderof an unfenced parent (e.g./Projects) that happens to contain a fenced subtree enumerates items inside it. Mitigate by pinning the sensitive root prefixes (so listing the root itself is caught) and pairing with an egress redaction policy. - Argument names are unverified. Path and search-scope keys follow the
Dropbox API v2 and the community server READMEs; Dropbox publishes no MCP
schemas. If a live tool names the path differently, that fence silently does
not fire — confirm against a live
tools/listand pin the real key(s). A present-but-non-string path fails closed, so the safe failure mode holds for the keys that are checked. - Both source and destination paths are checked on move/copy. Moving a file
out of a fenced tree (fenced
from_path/src) and moving one into a fenced tree (fencedto_path/dest) both require the mapped group. This is deliberately conservative; a non-group caller relocating unrelated files into a fenced path is denied. - Placeholder configuration. Path prefixes and group names are
placeholders — replace
/hr,/finance,/legal,/customersand the group names with your real Dropbox paths and IdPgroupsvalues at import time. Group names are placeholders — replacefinancewith your IdP's group name at import time. groupsclaim must be an array of strings. A string-valued or otherwise malformed claim fails closed (fenced targets deny). If your IdP emits groups under a different claim name, updatecaller_groupsin the Rego.
Compliance note. This policy supports alignment with the cited framework controls on the MCP path only. No policy or bundle makes an organization compliant with any framework; web-UI, native-API, and in-app access are outside the gateway's reach by design. Validate against your own compliance program before relying on it.
Policy source (Rego)
package dropbox.ingress.fence_sensitive_paths
# Deny-by-default: only the explicit allow rules below permit the request.
default allow := false
# ---------------------------------------------------------------------------
# Fence configuration — PLACEHOLDERS, replace at import time.
#
# Dropbox path prefix (lowercase, leading slash, NO trailing slash) -> IdP team
# group required to read/list/move/copy/write/delete/search inside it. A prefix
# fences itself and everything under it (`/finance` fences `/finance` and
# `/finance/...`, but not the sibling `/finance-public`). Groups are compared
# case-insensitively.
fenced_prefixes := {
"/hr": "hr",
"/finance": "finance",
"/legal": "legal",
"/customers": "customers",
}
# ---------------------------------------------------------------------------
# Identity — read groups via object.get chains so a missing subject/claims/
# groups fails closed (no group -> no access to fenced targets).
caller_groups := object.get(object.get(object.get(input, "subject", {}), "claims", {}), "groups", [])
# True when the caller's groups claim (an array of strings) contains `group`.
# A malformed (non-array) claim makes the iteration fail -> fail closed.
caller_has_group(group) if {
some g in caller_groups
lower(g) == lower(group)
}
# ---------------------------------------------------------------------------
# Tool matching. The gateway prefixes tool names with the configured MCP server
# name (separator not standardized), so match the exact name or a `-`/`_`-
# separated suffix, case-insensitively.
tool_name := lower(object.get(object.get(input, "resource", {}), "name", ""))
tool_matches(suffix) if tool_name == suffix
tool_matches(suffix) if endswith(tool_name, sprintf("-%s", [suffix]))
tool_matches(suffix) if endswith(tool_name, sprintf("_%s", [suffix]))
# Every path-addressed Dropbox tool this policy gates, across all three
# dialects: reads, listings, moves, copies, writes, deletes, and search.
protected_tool_suffixes := [
# official (PascalCase, lowercased here)
"listfolder", "getfilemetadata", "getfilecontent", "createfile",
"createfolder", "copy", "move", "delete", "search",
"listfilerevisions",
# dbx-mcp-server (snake_case)
"list_files", "get_file_metadata", "get_file_content", "download_file",
"upload_file", "create_folder", "copy_item", "move_item",
"safe_delete_item", "search_file_db",
# ngs (dropbox_ prefix)
"dropbox_list", "dropbox_get_metadata", "dropbox_download",
"dropbox_upload", "dropbox_create_folder", "dropbox_copy",
"dropbox_move", "dropbox_delete", "dropbox_search",
"dropbox_get_revisions",
]
is_protected_tool if {
some s in protected_tool_suffixes
tool_matches(s)
}
# ---------------------------------------------------------------------------
# Argument extraction — object.get everywhere. Read the path defensively from
# the likely keys plus the search scope nested under `options.path`.
args := object.get(object.get(input, "payload", {}), "args", {})
path_keys := ["path", "from_path", "to_path", "src", "dest", "source", "destination"]
# Non-empty string paths supplied on the call.
requested_paths contains p if {
some k in path_keys
v := object.get(args, k, "")
is_string(v)
v != ""
p := v
}
requested_paths contains p if {
v := object.get(object.get(args, "options", {}), "path", "")
is_string(v)
v != ""
p := v
}
# A path-like argument present but not a string (an evasion of the string
# match) — fail closed.
path_malformed if {
some k in path_keys
v := object.get(args, k, null)
v != null
not is_string(v)
}
path_malformed if {
v := object.get(object.get(args, "options", {}), "path", null)
v != null
not is_string(v)
}
# ---------------------------------------------------------------------------
# Path normalization + prefix test. Case-insensitive, single leading slash.
# Collapse runs of `/` to a single slash so `//finance/x` cannot slip past the
# `/finance/` prefix test. RE2 pattern `/+` -> `/`.
normalized_path(p) := out if {
t := regex.replace(lower(trim_space(p)), `/+`, "/")
startswith(t, "/")
out := t
}
normalized_path(p) := out if {
t := regex.replace(lower(trim_space(p)), `/+`, "/")
not startswith(t, "/")
out := concat("", ["/", t])
}
# True when path `p` is at or under fenced prefix `prefix`.
path_under(p, prefix) if normalized_path(p) == prefix
path_under(p, prefix) if startswith(normalized_path(p), concat("", [prefix, "/"]))
# True when some requested path falls under a fenced prefix whose group the
# caller does not hold.
blocked_by_fence if {
some p in requested_paths
some prefix, group in fenced_prefixes
path_under(p, prefix)
not caller_has_group(group)
}
# ---------------------------------------------------------------------------
# Allow rules.
# Any tool this policy does not inspect passes through untouched.
allow if not is_protected_tool
# Protected tools: allowed only when a readable string path is present (fail
# closed on missing/unparseable path), no path argument is malformed, and no
# requested path is fenced-without-group.
allow if {
is_protected_tool
not path_malformed
count(requested_paths) > 0
not blocked_by_fence
}
# ---------------------------------------------------------------------------
# Deny reasons.
# Fenced target the caller may not touch — names the folder rule and the group.
reasons contains msg if {
is_protected_tool
some p in requested_paths
some prefix, group in fenced_prefixes
path_under(p, prefix)
not caller_has_group(group)
msg := sprintf("Dropbox path '%s' is inside the protected '%s/' folder, which is restricted to the '%s' group. Ask your Dropbox administrator to grant you the '%s' group, or contact InfoSec if this fence looks wrong.", [p, prefix, group, group])
}
# Fail closed: a protected tool with no readable string path cannot be verified.
reasons contains "This Dropbox call is a protected file operation but supplied no readable path, so the sensitive-path fence cannot verify it and the request fails closed. Re-issue with an explicit string path (for example the `path` argument scoped to a folder you may access)." if {
is_protected_tool
not path_malformed
count(requested_paths) == 0
}
# Fail closed: a path argument that is present but not a string.
reasons contains "This Dropbox call carries a malformed path argument (not a string), so the sensitive-path fence cannot evaluate it and the request fails closed. Re-issue with a string path." if {
is_protected_tool
path_malformed
}
reason := joined if {
count(reasons) > 0
reason_list := sort([r | some r in reasons])
joined := concat("; ", reason_list)
} Canonical source: policy.md on GitHub · raw · raw on this site (.md)
Related policies
Airtable: Redact PII in Record Reads
Scans the responses of the Airtable record-read tools — the calls that return row fields values — and rewrites high-confidence PII shapes to a fixed…
Asana: Redact PII in Task & Comment Reads
On the Asana MCP read path, this transform scans the free-text business fields that ride back in task, comment/story, and status-update responses — notes,…
BigQuery: Redact PII in Query Results
Scans the content returned by BigQuery's result-returning tools and rewrites high-confidence PII shapes to fixed, non-recoverable redaction tokens before the…
Block Agent Email to External Recipients
Blocks agent-initiated Microsoft 365 email sends when any recipient address falls outside a corporate-domain allowlist.
Block BigQuery Exfiltration and Cross-Project Writes
Inspects the raw GoogleSQL string carried by BigQuery SQL tools and denies any statement that moves data out of the tenant's own project — even when the call…
bigqueryguard-warehouse-exportingresssqlexfiltrationsoc2pci-dssgdpr-ccpa
Block Bulk Export & External Staging (Snowflake)
Blocks Snowflake SQL-execution tool calls whose query text moves whole tables off the Snowflake perimeter — bulk export to cloud storage or a stage, and…
snowflakeguard-warehouse-sqlexportexfiltrationingresssoc2pci-dssgdpr-ccpa