BG-003: Opaque object pass
An unprojected fetch or database result is sent whole to the client.
Severity: warning
The raw, unprojected result of a fetch or database call is handed whole to the client: as a Client Component prop, as a Server Action's return value, or as a route handler's response body.
Why it fires
Nobody picked fields, so whatever the backend returns gets serialized to the browser. Today, and again after its next deploy. The set of fields that cross is not decided by your code; it is decided by whoever changes that endpoint.
That is why it is a warning rather than an error: there may be nothing
sensitive in the response today. The finding is about the shape of the decision,
not about a value that is known to be dangerous.
It is not a type error, and it is not about keywords
TypeScript does not save you here. Types are erased at compile time, so even
when user is declared as interface PublicUser { name: string }, React Server
Components serialize the actual runtime object across the React Flight protocol.
If the query returned fifteen columns, all fifteen travel in the payload and are
readable in the browser's network tab.
It is also not the keyword list that catches this. BG-001
fires on named sensitive fields such as password_hash, token, or secret.
BG-003 fires on unprojected ingress data whether or not any field is sensitive
today, which is what protects you when a later migration adds a column nobody
thought to project.
Fixing it
Project the fields explicitly
Pass the specific fields the client component needs, in a new object literal:
// Passes every database column to the browser
<StudentProfileScreen user={studentUser} />
// Only these fields are serialized
<StudentProfileScreen
user={{
id: studentUser.id,
firstName: studentUser.first_name,
lastName: studentUser.last_name,
experiences: studentUser.experiences,
}}
/>
Parse it through a validator or DTO
A schema parse or a mapping function does the same job and keeps the projection
in one place. Declare the function under sanitizers so the analysis knows it
clears the value:
const safeUser = publicStudentSchema.parse(studentUser);
return <StudentProfileScreen user={safeUser} />;
Suppress it when you have checked the query
If you have read the query and know that every column is safe and intended for the browser, record that at the crossing:
return (
<main>
{/* antra-disable-next-line BG-003 reason="public profile DTO verified" */}
<StudentProfileScreen user={studentUser} />
</main>
);