marmelab/react-admin · critical
Invalid resource key: ${resource}
Error message
Invalid resource key: ${resource} What it means
checkResource() blocks resource names that could enable prototype pollution: `__proto__`, `constructor`, and `prototype`. Because the provider writes to `data[resource]` and persists to localForage keys built from the resource name, a malicious or buggy resource string with these values could corrupt Object prototypes. Any write method (update, updateMany, create, delete, deleteMany) throws this before doing anything else.
Source
Thrown at packages/ra-data-local-forage/src/index.ts:280
const indexes = params.ids.map((id: any) => {
if (!data) {
throw new Error('The dataProvider is not initialized.');
}
return data[resource].findIndex(
(record: any) => record.id === id
);
});
pullAt(data[resource], indexes);
updateLocalForage(resource);
return baseDataProvider.deleteMany(resource, params);
},
};
};
const checkResource = resource => {
if (['__proto__', 'constructor', 'prototype'].includes(resource)) {
// protection against prototype pollution
throw new Error(`Invalid resource key: ${resource}`);
}
};
export interface LocalForageDataProviderParams {
defaultData?: any;
prefixLocalForageKey?: string;
loggingEnabled?: boolean;
}
View on GitHub (pinned to 051f511bb0)
Solutions
- Audit where resource strings originate; never pass raw URL/URLSearchParams values as the resource.
- Validate resources against an allowlist of known resource names before calling dataProvider methods.
- Sanitize dynamic routes so `__proto__`/`constructor`/`prototype` segments never reach the dataProvider.
- Keep ra-data-local-forage updated so the pollution guard stays in place.
Example fix
// before: resource straight from URL
const { resource } = useParams();
await dataProvider.update(resource as string, params);
// after: allowlist check
const allowed = ['posts', 'comments'];
if (!allowed.includes(resource)) throw new Error('Unknown resource');
await dataProvider.update(resource as string, params); Defensive patterns
Strategy: validation
Validate before calling
const RESOURCE_ALLOWLIST = new Set(['posts', 'comments', 'tags']);
export function assertSafeResource(resource: string): void {
if (!RESOURCE_ALLOWLIST.has(resource)) {
throw new Error(`Unknown resource: ${resource}`);
}
if (['__proto__', 'constructor', 'prototype'].includes(resource)) {
throw new Error(`Invalid resource key: ${resource}`);
}
}
// call before every dataProvider invocation
assertSafeResource(resource); Type guard
function isSafeResource(r: string): boolean {
return !['__proto__', 'constructor', 'prototype'].includes(r);
} Try / catch
try {
await dataProvider.update(resource, params);
} catch (e) {
if (e.message.startsWith('Invalid resource key')) {
notify('Invalid resource — refusing to persist data');
return; // never retry; this indicates hostile/malformed input
}
throw e;
} Prevention
- Never derive resource names from unvalidated URL segments or API input
- Use a static allowlist of resources and validate before any dataProvider call
- Sanitize dynamic admin routes so dangerous keys can't reach the provider
- Add a unit test asserting checkResource rejects __proto__/constructor/prototype
When it happens
Trigger: Calling any mutating dataProvider method with resource set to `__proto__`, `constructor`, or `prototype` — typically from dynamic route parameters (e.g. /admin/__proto__/edit), user-controlled refs, or misconfigured resources derived from API input.
Common situations: Apps that build resource names from URL segments or remote config without validation; security scanners probing for prototype-pollution sinks; typos in resource definitions are NOT caught here (only the three dangerous keys).
Related errors
- Invalid resource key: ${resource}
- useCreate mutation requires a resource
- useCreate mutation requires a non-empty data object
- useDelete mutation requires a resource
- useDelete mutation requires a non-empty id
AI-assisted analysis of marmelab/react-admin@051f511bb0 (2026-08-30).
Data as JSON: /api/errors/c7220638e5765e6a.
Report an issue: GitHub.