Agent skill
solid-security
SolidStart security: XSS prevention, CSP headers, CORS configuration, CSRF protection, input validation, server-side validation, environment variable security.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/solid-security
Metadata
Additional technical details for this skill
- globs
-
[ "**/middleware/**", "**/*security*" ]
SKILL.md
SolidStart Security
Complete guide to securing SolidStart applications. Protect against XSS, CSRF, and other common attacks.
XSS Prevention
Solid automatically escapes values in JSX to prevent XSS attacks.
Automatic Escaping
// ✅ Safe - automatically escaped
function Component() {
const userInput = "<script>alert('xss')</script>";
return <div>{userInput}</div>; // Rendered as text
}
Dangerous: innerHTML
// ❌ DANGEROUS - not escaped
function Component() {
const userInput = "<script>alert('xss')</script>";
return <div innerHTML={userInput} />; // Executes script!
}
// ✅ SAFE - sanitize first
import DOMPurify from "dompurify";
function Component() {
const userInput = "<script>alert('xss')</script>";
const sanitized = DOMPurify.sanitize(userInput);
return <div innerHTML={sanitized} />;
}
Best Practices
- Avoid innerHTML when possible
- Sanitize user input with DOMPurify
- Validate URLs from user input:
tsx
function validateUrl(url: string) { const parsed = new URL(url); if (parsed.protocol === "javascript:") { throw new Error("Invalid URL"); } return parsed; } - Sanitize noscript content
Content Security Policy (CSP)
Configure CSP headers via middleware to restrict resource loading.
With Nonce (Recommended)
// src/middleware/index.ts
import { createMiddleware } from "@solidjs/start/middleware";
import { randomBytes } from "crypto";
export default createMiddleware({
onRequest: (event) => {
const nonce = randomBytes(16).toString("base64");
event.locals.nonce = nonce;
const csp = `
default-src 'self';
script-src 'nonce-${nonce}' 'strict-dynamic' 'unsafe-eval';
object-src 'none';
base-uri 'none';
frame-ancestors 'none';
form-action 'self';
`.replace(/\s+/g, " ");
event.response.headers.set("Content-Security-Policy", csp);
},
});
// src/entry-server.tsx
export default createHandler(
() => <StartServer document={...} />,
(event) => ({ nonce: event.locals.nonce })
);
Without Nonce
import { createMiddleware } from "@solidjs/start/middleware";
export default createMiddleware({
onBeforeResponse: (event) => {
const csp = `
default-src 'self';
font-src 'self';
object-src 'none';
base-uri 'none';
frame-ancestors 'none';
form-action 'self';
`.replace(/\s+/g, " ");
event.response.headers.set("Content-Security-Policy", csp);
},
});
CORS Configuration
Configure CORS headers for API endpoints.
import { createMiddleware } from "@solidjs/start/middleware";
import { json } from "@solidjs/router";
const TRUSTED_ORIGINS = ["https://my-app.com", "https://another-app.com"];
export default createMiddleware({
onBeforeResponse: (event) => {
const { request, response } = event;
response.headers.append("Vary", "Origin, Access-Control-Request-Method");
const origin = request.headers.get("Origin");
const requestUrl = new URL(request.url);
const isApiRequest = requestUrl.pathname.startsWith("/api");
if (isApiRequest && origin && TRUSTED_ORIGINS.includes(origin)) {
// Handle preflight
if (
request.method === "OPTIONS" &&
request.headers.get("Access-Control-Request-Method")
) {
return json(null, {
headers: {
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Methods": "OPTIONS, POST, PUT, PATCH, DELETE",
"Access-Control-Allow-Headers": "Authorization, Content-Type",
},
});
}
// Handle normal requests
response.headers.set("Access-Control-Allow-Origin", origin);
}
},
});
CSRF Protection
Protect against Cross-Site Request Forgery attacks.
import { createMiddleware } from "@solidjs/start/middleware";
import { json } from "@solidjs/router";
const SAFE_METHODS = ["GET", "HEAD", "OPTIONS", "TRACE"];
const TRUSTED_ORIGINS = ["https://another-app.com"];
export default createMiddleware({
onRequest: (event) => {
const { request } = event;
if (!SAFE_METHODS.includes(request.method)) {
const requestUrl = new URL(request.url);
const origin = request.headers.get("Origin");
// Check Origin header
if (origin) {
const parsedOrigin = new URL(origin);
if (
parsedOrigin.origin !== requestUrl.origin &&
!TRUSTED_ORIGINS.includes(parsedOrigin.host)
) {
return json({ error: "origin invalid" }, { status: 403 });
}
}
// Check Referer for HTTPS
if (!origin && requestUrl.protocol === "https:") {
const referer = request.headers.get("Referer");
if (!referer) {
return json({ error: "referer not supplied" }, { status: 403 });
}
const parsedReferer = new URL(referer);
if (parsedReferer.protocol !== "https:") {
return json({ error: "referer invalid" }, { status: 403 });
}
if (
parsedReferer.host !== requestUrl.host &&
!TRUSTED_ORIGINS.includes(parsedReferer.host)
) {
return json({ error: "referer invalid" }, { status: 403 });
}
}
}
},
});
Input Validation
Server-Side Validation
"use server";
import { action } from "@solidjs/router";
import { z } from "zod";
const userSchema = z.object({
email: z.string().email(),
name: z.string().min(1).max(100),
});
export const createUser = action(async (formData: FormData) => {
const data = {
email: formData.get("email"),
name: formData.get("name"),
};
// Validate
const result = userSchema.safeParse(data);
if (!result.success) {
return { ok: false, errors: result.error.errors };
}
// Process validated data
// ...
}, "createUser");
Client-Side Validation
import { createSignal } from "solid-js";
function Form() {
const [email, setEmail] = createSignal("");
const [error, setError] = createSignal("");
const validateEmail = (value: string) => {
if (!value.includes("@")) {
setError("Invalid email");
return false;
}
setError("");
return true;
};
return (
<form>
<input
value={email()}
onInput={(e) => {
setEmail(e.currentTarget.value);
validateEmail(e.currentTarget.value);
}}
/>
<Show when={error()}>
<span class="error">{error()}</span>
</Show>
</form>
);
}
Environment Variable Security
Never Expose Secrets
# ❌ WRONG - exposed to client
VITE_DB_PASSWORD=secret123
# ✅ CORRECT - server-only
DB_PASSWORD=secret123
Access Control
// ✅ Server-only access
"use server";
export async function getData() {
const secret = process.env.API_SECRET_KEY; // Server-only
// Use secret
}
Best Practices
- Always validate input on server
- Sanitize user content before rendering
- Use CSP headers to restrict resources
- Configure CORS for API endpoints
- Protect against CSRF for state-changing operations
- Never expose secrets in client code
- Use HTTPS in production
- Keep dependencies updated
Summary
- XSS: Avoid innerHTML, sanitize input
- CSP: Configure via middleware with nonces
- CORS: Restrict to trusted origins
- CSRF: Validate Origin/Referer headers
- Validation: Server-side is mandatory
- Secrets: Never use VITE_ prefix
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?