first commit
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# eval-safe
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Provides `serializeEvalSafe` and `deserializeEvalSafe` as an explicit opt-in adapter for scenarios where the standard tagged JSON or MessagePack codecs cannot be used — for example, when the payload must include JavaScript `Function` values, `RegExp` literals with complex syntax, or other types that cannot be expressed in a JSON-safe intermediate. The adapter wraps the `serialize-javascript` library, which is purpose-built for this use case (e.g. server-side Redux initial state injection into HTML).
|
||||
|
||||
This module lives under the `./unsafe` subpath export and must **never** appear in the main `@suaveplan/serialize` bundle. Importing it is an explicit acknowledgment of the security trade-off.
|
||||
|
||||
## Features
|
||||
|
||||
- `serializeEvalSafe` — produces a JavaScript expression string via `serialize-javascript` that, when eval'd, reconstructs the original value including Functions and RegExps
|
||||
- `deserializeEvalSafe` — evaluates the produced string via `(0, eval)(...)` (indirect eval) to reconstruct the value
|
||||
- Security warning emitted on every call unless `{ ignoreSecurityWarning: true }` is passed
|
||||
- `EvalSafeOptions` interface for suppressing warnings in controlled environments
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```typescript
|
||||
import { serializeEvalSafe, deserializeEvalSafe } from "@suaveplan/serialize/unsafe";
|
||||
|
||||
// Suppress warnings in a trusted server-side context
|
||||
const str = serializeEvalSafe({ fn: () => 42 }, { ignoreSecurityWarning: true });
|
||||
const restored = deserializeEvalSafe(str, { ignoreSecurityWarning: true });
|
||||
// (restored as any).fn() === 42
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```typescript
|
||||
import { serializeEvalSafe } from "@suaveplan/serialize/unsafe";
|
||||
|
||||
// Inject Redux initial state into an HTML page server-side.
|
||||
// This is the canonical use case for serialize-javascript.
|
||||
const html = `
|
||||
<script>
|
||||
window.__INITIAL_STATE__ = ${serializeEvalSafe(reduxState, { ignoreSecurityWarning: true })};
|
||||
</script>
|
||||
`;
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `serializeEvalSafe(value: unknown, opts?: EvalSafeOptions): string`
|
||||
|
||||
Serialize `value` using `serialize-javascript`. The output is a JavaScript expression string, not JSON.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `value` — any JavaScript value, including Functions and RegExps
|
||||
- `opts.ignoreSecurityWarning` — pass `true` to suppress the `console.warn`
|
||||
|
||||
**Returns:** a JavaScript expression string.
|
||||
|
||||
### `deserializeEvalSafe(json: string, opts?: EvalSafeOptions): unknown`
|
||||
|
||||
Evaluate `json` as a JavaScript expression and return the result.
|
||||
|
||||
**Parameters:**
|
||||
|
||||
- `json` — a string produced by `serializeEvalSafe`; MUST be from a trusted source
|
||||
- `opts.ignoreSecurityWarning` — pass `true` to suppress the `console.warn`
|
||||
|
||||
**Returns:** the reconstructed value.
|
||||
|
||||
**Security:** Uses indirect `eval` (`(0, eval)(...)`), which runs in the global scope rather than the local closure scope, slightly reducing the attack surface compared to direct `eval`.
|
||||
|
||||
### `EvalSafeOptions`
|
||||
|
||||
```typescript
|
||||
interface EvalSafeOptions {
|
||||
ignoreSecurityWarning?: boolean;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- The `(0, eval)(...)` pattern (indirect eval) is used intentionally: it evaluates in the global scope rather than capturing local variables, which slightly reduces the blast radius if a malicious string is accidentally evaluated.
|
||||
- `serialize-javascript` is listed as a regular dependency (not a dev dependency) because it is shipped as part of the `/unsafe` subpath. The main bundle's Rollup config marks it as `external`, so it is never bundled into `dist/index.js`.
|
||||
- The biome `lint/security/noEval` rule is suppressed with a comment at the call site to document that the eval usage is intentional, reviewed, and isolated to this specific adapter.
|
||||
Reference in New Issue
Block a user