canvas — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited canvas (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
A canvas is a single .canvas.tsx file the IDE compiles so the user can open it beside the chat. Follow the workflow below in order.
The trigger is user intent, not response shape. Ask: would the user benefit from viewing this output as its own standalone artifact, separate from the chat? If the output is a means to an end (a drafted message, a code fix, a dashboard in another tool), skip the canvas.
Use a canvas when the agent produces new standalone analytical output:
Do NOT use a canvas when:
Location. Canvases live at /Users/<user>/.cursor/projects/<workspace>/canvases/<name>.canvas.tsx. The IDE only detects canvases written directly inside that exact directory — subfolders, alternate extensions, and other locations are not picked up. For a new canvas, always use the write file tool to create the .canvas.tsx file at that exact path; do not stop after telling the user the path or showing code in chat. Treat that managed canvases/ directory as pre-provisioned by Cursor itself: write the canvas file directly there and do not spend turns creating the directory with mkdir or checking whether it exists before writing. Listing its contents for other purposes (e.g. checking for existing canvases) is fine. If you can't determine the workspace directory from absolute paths already in your environment (terminals, transcripts, recently-viewed files), list ~/.cursor/projects/ rather than guessing. Use a descriptive kebab-case filename ending in .canvas.tsx; preserve acronym capitalization and lowercase the rest.
File rules:
.canvas.tsx file per canvas. Never create helper files, style files, or supporting modules.cursor/canvas. No relative imports, no npm packages, no Node built-ins.Component discovery: prefer built-in cursor/canvas components over hand-rolled markup. The full public surface (components, hooks, prop types, tokens) is declared in ~/.cursor/skills-cursor/canvas/sdk/index.d.ts and its sibling .d.ts files — read them when you need exact exports, prop shapes, or hook signatures rather than guessing. Referencing an export that does not exist is the most common runtime error.
Apply the Canvas generation policy below as you write, and complete its pre-delivery self-check (section 6) before returning the canvas.
Be creative. The SDK gives you expressive building blocks — use them in whatever combination best serves the content. But avoid slop: no gradients, no emojis, no box-shadows, no rainbow coloring. Cursor canvases are flat, minimal, and purposeful.
Not everything deserves equal treatment. Primary content gets more space, larger headings, and accent color. Supporting content stays compact. Squint test: blur your eyes — can you tell what matters?
Color. All colors from useHostTheme() tokens — read its JSDoc in the SDK declarations for the return shape and usage pattern. No hardcoded hex. Use accent color deliberately, not on everything.
These specific patterns produce low-quality output. If 2+ are present, redesign.
linear-gradient, radial-gradient, background-clip: text.box-shadow. Flat surfaces only.Before returning canvas code, verify:
When you create a canvas, add a short note in your chat response telling the user you created a canvas they can open beside the chat:
.canvas.tsx files exist in the workspace's canvases/ directory, include one sentence explaining what a canvas is.Both can apply at once; one or two sentences total is enough. Skip the intro for subsequent canvases.
If a canvas appears blank or missing, the most common cause is that it was not written under /Users/<user>/.cursor/projects/<workspace>/canvases/ exactly — re-save it to that path. Do not debug this by trying to create the managed directory manually; focus on correcting the file path instead. Users can click the canvas file path in the response to open it, just like any other file path in Cursor. When present, the canvas server writes a <name>.canvas.status.json sidecar after each build with status, diagnostics, or error fields you can read; the file is best-effort and may not exist, so don't block on it.
import { Divider, Grid, H1, H2, Stack, Stat, Table, Text } from 'cursor/canvas';
export default function ServiceOverview() {
return (
<Stack gap={20}>
<H1>Service Overview</H1>
<Grid columns={3} gap={16}>
<Stat value="6" label="Total Services" />
<Stat value="5" label="Healthy" tone="success" />
<Stat value="1" label="Degraded" tone="warning" />
</Grid>
<Divider />
<H2>Service Status</H2>
<Table
headers={["Service", "Status", "Uptime", "Latency"]}
rows={[
["api-gateway", "Operational", "99.99%", "12ms"],
["auth-service", "Degraded", "99.2%", "340ms"],
["billing", "Operational", "99.8%", "45ms"],
]}
rowTone={[undefined, "warning", undefined]}
/>
<Divider />
<H2>Recent Changes</H2>
<Text>Auth service latency increased after the 14:30 deploy.</Text>
<Text tone="secondary" size="small">Last checked: Apr 7, 2026 14:52 UTC</Text>
</Stack>
);
}Stats in a Grid, Table directly under H2, text sections without cards.
// BAD — every section wrapped in Card, no hierarchy, Table unnecessarily boxed
<Stack gap={12}>
<Card><CardHeader>Summary</CardHeader><CardBody><Text>6 services.</Text></CardBody></Card>
<Card><CardHeader>Status</CardHeader><CardBody><Table headers={[...]} rows={[...]} /></CardBody></Card>
<Card><CardHeader>Changes</CardHeader><CardBody><Text>Latency increased.</Text></CardBody></Card>
</Stack>~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.