Appearance
Cloud-sync protection, claim by claim
WIN-DLP-EPIC-018 asks to recognise managed synchronisation directories and treat them correctly, including placeholders, because a synced library is where the documents are, and it is where a naive agent does the most damage.
The placeholder half is done, and done with unusual care - it is the clearest example in this product of declining to do something because doing it would cause the harm.
The recognition half is implemented by DLP-189: Windows reads the providers' own configuration and combines it with administrator-declared roots. The capability matrix reports the channel as monitor-and-warn, never as prevention.
1. Placeholders: the damage a naive agent does
A file in a OneDrive or SharePoint-synced library may be a placeholder: the metadata is local and the content is not. Opening it downloads it.
So an agent that scans a synced library the obvious way hydrates every placeholder in it - megabytes to gigabytes of download, on somebody's laptop, on a metered connection, caused by the security tool. And each download is itself a data transfer the product would then report.
| Claim | Proved by |
|---|---|
| A dehydrated placeholder is reported, never read | monitor::tests::a_dehydrated_placeholder_is_reported_not_read |
| Each recall attribute has its own sentence | monitor::tests::each_recall_attribute_has_its_own_sentence |
| The most specific attribute wins when several are set | monitor::tests::the_most_specific_attribute_wins_when_several_are_set |
| An ordinary file is not mistaken for a placeholder | monitor::tests::an_ordinary_file_is_not_a_placeholder |
| A real local file is read normally | monitor::tests::a_real_local_file_is_read_normally |
The reporting is what makes it defensible: a placeholder is not skipped silently, it is reported with the reason - "cloud placeholder: the content is not on this machine, and reading it would download it" - so an operator sees a file that was not scanned rather than a file that looked clean.
The last two rows matter as much as the first. A check this conservative is worth nothing if it also refuses ordinary local files, and both directions are tested.
2. Recognition: configured evidence, never a folder-name guess
agent/core/src/cloud_sync.rs recognises OneDrive roots from the provider's registry accounts (including loaded user hives under HKEY_USERS when the service runs as LocalSystem), Dropbox roots from info.json, and any root an administrator declares. A directory merely called OneDrive is not enough: the provider or the administrator has to identify it.
agent/core/src/runner.rs resolves each file path against those roots before the event is built. A matching write is emitted as Channel::CloudSyncFolder, so a policy scoped to that channel can fire; the event names the provider and, only where the provider publishes it, whether the account is personal or business. It never guesses an account name, address or tenant.
Whether the destination is sanctioned (DLP-1210). A declared root is trusted and a personal account is not. A OneDrive business account is trusted when the tenant OneDrive recorded for it (ConfiguredTenantId) is on the administrator's sanctioned_cloud_tenants list in agent.toml. It is untrusted when the list exists and leaves that tenant out. With no list, or no recorded tenant, it is undecided. The tenant id is read from the same registry text and used only for that decision on the endpoint; the event carries the provider, the account kind and the answer, never the tenant (PRIVACY_MODEL.md §44). A business Dropbox publishes no team identifier, so it stays undecided. The registry value is parsed and tested from recorded reg.exe text; it has not been read from a real OneDrive account on a Windows host.
This is the correction delivered by DLP-189. DLP-132 remains the regression history: before recognition existed the matrix claimed a channel no event could emit. The matrix now names the evidence behind the row and the tests pin both sides:
| Claim | Proved by |
|---|---|
| A folder name alone proves nothing | cloud_sync::tests::a_folder_that_merely_looks_like_a_sync_root_is_not_one |
| OneDrive personal and business roots remain distinct | cloud_sync::tests::a_personal_and_a_business_account_are_both_read_and_told_apart |
| An unpublished account kind stays absent | cloud_sync::tests::an_account_kind_the_provider_did_not_publish_is_absent |
| A business account's tenant is read, and only a business account's | cloud_sync::tests::a_business_account_carries_the_tenant_onedrive_recorded_whatever_the_value_order |
| The sanctioned list decides a business account, and no list decides nothing | cloud_sync::tests::the_sanctioned_list_decides_a_business_account_and_nothing_else_does |
| A list entry that is not a tenant id is refused at start | config::tests::a_sanctioned_tenant_that_is_not_a_tenant_id_is_refused |
| The most specific nested root wins | cloud_sync::tests::the_most_specific_root_wins |
| A provider this build does not know is carried, not discarded | cloud_sync::tests::a_provider_this_build_has_not_heard_of_is_carried_rather_than_dropped |
The test that would have caught it
platform::tests::a_channel_is_claimed_only_where_something_produces_it now asserts that every channel claimed as monitored appears in the list of channels the agent can actually emit.
It is a hand-maintained list, which is a weakness, and it is still better than a claim nobody checks: somebody adding a channel must edit it, and the assertion makes that edit necessary rather than polite.
It caught a second thing immediately. The first version of the list was built from a grep of runner.rs, and the test failed on mobile_device - which is emitted on Linux, through DeviceClass::channel(), because a phone over MTP is a FUSE mount and a write to it is an ordinary file event on a real path. A channel reaches an event two ways and the second is easy to miss. The list now says which is which.
3. What is still not seen
| Gap | State |
|---|---|
| Recognising a sync root | Implemented. Provider configuration or an administrator declaration; never a path-name heuristic (DLP-189) |
| The upload itself | Absent, and not achievable from the filesystem. The local write is seen; what the sync client then does with it over TLS is not. DLP-1178 is scanning the cloud store server-side instead |
| A placeholder's content | Deliberately not read. §1 |
The second row is the honest limit of this whole channel: an endpoint agent watching a directory sees a file appear in it. Whether the sync client subsequently uploaded that file, to which tenant, under whose account, is not visible from the filesystem - and DLP-1178's answer is to ask the cloud provider rather than the endpoint.
4. What is not covered
- Nothing here has been run against a real OneDrive client. The placeholder tests synthesise the file attributes; no test has a real dehydrated file, because that needs a Windows host with a signed-in account.
DLP-3. - No test has exercised provider discovery against a real signed-in OneDrive or Dropbox client. Parsing and path resolution are unit-tested, and the Windows end-to-end runner does not carry a provider account. This is part of the real-fleet gap recorded as
DLP-449.