The short answer: a file’s access comes from its links, and a link with ShareType = 'I' inherits permission from whatever record it points at. So a file becomes visible to everyone who can see any record it is linked to, and there is nothing on the file itself that tells you which records those are. That is the access nobody expects.
The three ShareType values
V, Viewer. The linked record’s users can view and download. They cannot edit or delete the file.C, Collaborator. They can also edit the file, upload new versions, and change its sharing.I, Inferred. Permission follows the linked record: edit access on the record means collaborator access on the file, read access means viewer.
Inferred is the default for files uploaded through the Files related list on a record, which means most files in an org are on inferred sharing without anyone having chosen it.
Visibility narrows it further
Visibility is a second dimension, and both apply:
AllUsers— internal and external users with access to the linked record.InternalUsers— internal only, even if a community user can see the record.SharedUsers— only users the file has been explicitly shared with, ignoring the record.
For anything sensitive on a record that community or portal users can reach, InternalUsers is the setting you want and it is not the default.
Why you cannot audit this easily
ContentDocumentLink cannot be queried without a filter on ContentDocumentId or LinkedEntityId. This is enforced by the platform:
SELECT Id FROM ContentDocumentLink
// Implementation restriction: ContentDocumentLink requires a filter by a
// single Id on ContentDocumentId or LinkedEntityId
So there is no “show me every file link in the org” query. An audit means iterating over records and querying links per record, in batch, which is slow and is the only route available.
for (List<Account> chunk : [SELECT Id FROM Account]) {
Set<Id> ids = new Map<Id, Account>(chunk).keySet();
for (ContentDocumentLink l : [
SELECT ContentDocumentId, LinkedEntityId, ShareType, Visibility
FROM ContentDocumentLink WHERE LinkedEntityId IN :ids
]) { ... }
}
Note that IN with a set is permitted even though an unfiltered query is not.
The exposure pattern
A support agent uploads a customer’s document to a case. The case is linked to an account. Later, someone links the same file to an opportunity so the sales team can see it. The opportunity is visible to the whole sales organisation through the role hierarchy, and the customer document now is too.
Nothing in this sequence is a misconfiguration. Each step is a reasonable action, and the file’s access is the union of every link. That union is the thing to reason about, and it is why files carrying anything sensitive should be linked to exactly one record and set to SharedUsers or InternalUsers.
Deleting a link is not deleting the file
Removing a ContentDocumentLink removes the file from that record. The ContentDocument still exists, still belongs to whoever uploaded it, and is still in their personal Files. If the intent is that the file is gone, delete the ContentDocument, and note that it goes to the recycle bin with the usual 15 days.
Guest users
Guest user access to files is deliberately restrictive. A guest user generally cannot see a file through an inferred link, and making a file publicly available means a ContentDistribution record, which generates a public URL with optional password and expiry. That URL bypasses Salesforce login entirely, so treat generating one as a publishing decision.
Frequently asked
Why can a user see a file they were never given? Query the links for that ContentDocumentId. One of the linked records is visible to them.
Can I change ShareType after the fact? Yes, update the ContentDocumentLink. It is an ordinary record.
Does file access respect field-level security? Not meaningfully. A file is not a field. Access is the link model described here and nothing else.
