Web Security Audit
A read-only security pass for any web project — built so it works the same on a
Next.js app, a SvelteKit site, an Express API, or a completely different stack,
and so a non-security person can trust the result.
How this skill works (read this first)
You (Claude) are the auditor. Do the 21 checks below by reading the code and
applying judgment. Do not rely on a tool's raw regex output as the verdict —
that is exactly what produces false positives (a regex sees KEY in a variable
name and panics, even when the key is public by design and the value is empty).
You read the code, understand the context, and decide.
The bundled security-audit.sh (run.ps1 on Windows) is an optional
accelerator for large codebases. It is never required. Treat its output as a
list of candidates to confirm, not findings. If the harness blocks running it,
or you can't trust the project enough to execute its scripts, just do the checks
by hand with Grep/Read — that path always works.
Ground rules
- Read-only by default. The audit never edits files, installs packages,
runs
audit fix, or modifies dependencies. Reporting is the deliverable.
- Never execute untrusted project code. Dependency-audit commands do not run
project code, but they do use the network; obtain permission first.
tsc / eslint / build steps execute the project's own config and plugins —
only run them on a project the user already trusts, and never let a tool
auto-install a missing binary (no npx <pkg> that hits the network).
- Confirm before you flag. Every candidate must be opened and read. A match
in a test fixture, an
.env.example template, a comment, or a public-by-design
key is not a finding.
- Adapt to the repo. Monorepo? Run the checks per app (
apps/web,
packages/*) — auto-detection that only looks at the repo root will miss
nested apps. Non-JS project (Python, Go…)? Say so and scope to what applies.
Production-safe audit boundary
Start with source, configuration, tests and dependency manifests. Against a live
site, stay passive and low-volume: normally one request per page/header you must
verify. Never brute-force credentials, fuzz production endpoints, enumerate at
scale, bypass bot protection, place orders, trigger email/SMS/AI generation, run
load/DoS tests, or probe infrastructure owned by a third party. If static evidence
is enough, make zero production requests.
Dependency audits are network operations. Run them only when the user permits
network access; otherwise report them as not run. Never print a discovered secret,
token, email address, TOTP or raw provider error into the report—use file:line and
[REDACTED].
The 21 checks — what to look for and how to read the result
| # |
Check |
Severity |
| 1 |
No database/ORM imports in client code |
Error |
| 2 |
Auth on every API route handler |
Warning |
| 3 |
Premium/role gating enforced server-side |
Warning |
| 4 |
No real secrets in client-exposed env vars |
Error |
| 5 |
Price/score/business math is server-side |
Warning |
| 6 |
Input validation on API routes |
Warning |
| 7 |
Rate limiting on expensive endpoints |
Warning |
| 8 |
No secrets/PII in logs |
Error |
| 9 |
Dependency vulnerabilities (audit) |
Error/Warn |
| 10 |
TypeScript typecheck (trusted projects only) |
Error |
| 11 |
Lint (trusted projects only) |
Warning |
| 12 |
No hardcoded secrets |
Error |
| 13 |
No wildcard/permissive CORS |
Warning |
| 14 |
No SQL injection (raw queries + interpolation) |
Warning |
| 15 |
No XSS sinks (dangerouslySetInnerHTML, v-html, innerHTML, {@html}) |
Warning |
| 16 |
CSP and browser/deployment security headers |
Warning |
| 17 |
External URLs/navigation sinks allow only http:/https: |
Warning |
| 18 |
Cheap bot/auth/rate/quota gates run before metered calls |
Error |
| 19 |
Installers/updates authenticate artifacts before execution |
Error |
| 20 |
External payloads are bounded, schema-validated and fail closed |
Error |
| 21 |
Containers use least privilege where practical |
Warning |
For each one:
- Find — grep the patterns across source files (skip
node_modules, build
output: .next .nuxt .svelte-kit dist build .output coverage).
- Open & read — confirm it's real in context.
- Decide — real issue, accepted-with-reason, or false positive.
Details where reading-with-judgment matters most:
- #1 client DB access — flag an ORM/DB client import (
@prisma/client,
drizzle-orm, mongoose, pg, mysql2, @supabase/supabase-js used as a
service-role client, …) reachable from browser code: a React "use client"
file, a .vue/.svelte component, or anything bundled to the client. Server
components, route handlers, server actions, and *.server.ts are fine.
- #4 secrets in public env vars — this is the #1 source of false positives.
See the allowlist below. Only flag when a real, non-empty secret value is
assigned to a client-exposed variable. Empty templates and public-by-design
keys are PASS.
- Static encrypted pages — client-side AES-GCM can protect a published blob,
but it is not server authentication: attackers can copy the ciphertext and
guess passwords offline, there is no per-user revocation, and any same-origin
XSS can steal a derived key held in browser storage. Require a high-entropy
password, strong KDF, authenticated encryption,
noindex/no-store, CSP and
an explicit acceptance of the shared-password model; use server-side access
control when individual identity or revocation matters.
- #7 rate limiting — only expensive routes need it: AI generation, payments,
email/SMS, anything that costs money or compute per call. A plain CRUD GET does
not need a rate limiter to pass.
- #8 sensitive logs — include PII and transient credentials: email/account
identifiers, auth headers, session IDs, OTP/TOTP codes and raw upstream error
bodies. Log a stable category/request ID and redact values.
- #9 dependency audit — with network permission, run
npm audit / pnpm audit / yarn audit (no project code executes). Report high/critical as
errors, low/moderate as warnings. Do not run audit fix --force.
- #10/#11 typecheck & lint — these execute project config. Run only on a
trusted project, using the locally installed binary
(
node_modules/.bin/tsc, node_modules/.bin/eslint). If not installed, skip
and say so — never auto-install.
- #15 XSS — an
innerHTML match is a candidate, not an automatic finding.
Trace every interpolated value. Constants and correctly escaped values can be
safe; decrypted/generated HTML needs an explicit trust boundary and a CSP.
- #17 URL sinks — validate stored, generated and provider-supplied URLs at
the final sink (
href, window.open, redirects). Reject javascript:,
data:, embedded credentials and non-HTTP(S) schemes.
- #18 cost ordering — for AI, image/logo enrichment, payment, email and SMS,
prove the order in code: cheap validation → bot/auth check → rate limit →
entitlement/quota/credit reservation → paid call. A limiter after the provider
call does not protect the bill.
- #19 update authenticity — HTTPS and encryption are not artifact
authentication. Before
tar, docker compose, bash or replacing code,
verify a signature or encrypt-then-MAC tag, cap size, extract to a scratch
directory, validate required files, then swap. Root installers must use
mktemp, not predictable /tmp/name files. Shared-key HMAC does not protect
against a malicious holder of that key; prefer public-key signatures when that
threat matters.
- #20 fail closed — cap response/body/ciphertext sizes and KDF work factors
before expensive processing. Validate decrypted JSON types, enums, uniqueness,
numeric finiteness/ranges and totals. Missing action lists must not default to
[] when empty means delete, sell or revoke. Validate mode enums before
selecting production/live defaults.
- #21 containers — review
read_only, cap_drop, no-new-privileges,
non-root users, secret mounts and network exposure per service. Absence is a
hardening opportunity, not automatically exploitable.
Public-by-design — do NOT flag these as leaks
These are meant to live in the browser. Finding them in client code or in a
NEXT_PUBLIC_* / VITE_* / PUBLIC_* variable is expected and correct. Verify
the intended protection exists, but do not report them as secret leaks:
- Supabase anon key (
NEXT_PUBLIC_SUPABASE_ANON_KEY) — public; protected by
Row Level Security policies. Confirm RLS is on; the key itself is fine.
- Stripe publishable key (
pk_live_…, pk_test_…,
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY) — designed to be exposed. Only the
secret key (sk_live_… / sk_test_…) is a real leak.
- Firebase web config (
apiKey, authDomain, projectId, …) — public by
design; protected by Firebase Security Rules + allowed-domains.
- Clerk publishable key (
pk_…, NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY) — public.
- Analytics / monitoring tokens — PostHog (
NEXT_PUBLIC_POSTHOG_KEY), Sentry
DSN (NEXT_PUBLIC_SENTRY_DSN), GA/GTM IDs, Amplitude, Mixpanel browser tokens —
all client-side by design.
- Map / search browser tokens — Mapbox
pk.…, Google Maps browser key,
Algolia search-only key — public, ideally restricted by domain/referrer.
- Empty or placeholder values —
.env.example, .env.sample, .env.template
with empty values or placeholders (your-key-here, xxx, changeme,
sk_test_xxx, <...>) are templates, not secrets. Only a real value
committed to a real .env is a leak.
When you skip one of these, say why in the report (e.g. "anon key is public,
RLS confirmed in supabase/") so the user learns the reasoning, not just the verdict.
Always treat as a real secret (Error)
A literal value matching any of these committed to the repo is a genuine leak —
rotate it immediately:
- OpenAI / Anthropic
sk-… ; Stripe secret sk_live_… / sk_test_…
- AWS
AKIA… access keys ; Google server key AIza… ; Google OAuth ya29.…
- GitHub
ghp_… / gho_… / github_pat_… ; GitLab glpat-…
- Slack
xoxb-… / xoxp-… ; SendGrid SG.…
- JWTs hardcoded in source ; private keys (
-----BEGIN … PRIVATE KEY-----)
- DB connection strings with credentials
(
postgres://user:pass@…, mongodb+srv://user:pass@…, mysql://…, redis://…)
Also confirm .env, .env.* (except .env.example) are git-ignored.
Optional: the bash accelerator
For big repos you may run the bundled script to generate candidates faster.
# macOS / Linux / CI — from the project root
bash <skill-dir>/security-audit.sh # offline/local, warnings don't fail
bash <skill-dir>/security-audit.sh --network # opt in to registry dependency audit
bash <skill-dir>/security-audit.sh --ci # exit 1 on any error (for CI)
# Windows (delegates to Git Bash automatically)
& "<skill-dir>\run.ps1"
& "<skill-dir>\run.ps1" --ci
<skill-dir> is wherever this skill is installed (e.g.
~/.claude/skills/web-security-audit).
The script is read-only except --fix. Do not use --fix by default; it
modifies files (eslint --fix, non-forced npm audit fix). Only run it on
explicit user request, and review the git diff afterward. Whatever the script
prints, still confirm each candidate yourself per the allowlist above.
Report template
After the pass, give the user a single table plus a plain-language verdict:
| # | Check | Result | Detail |
|---|-------|--------|--------|
| 1 | Client DB access | PASS / FAIL | file:line or "none" |
| 2 | API auth | … | … |
…
| 15| XSS sinks | … | … |
…
| 21| Container least privilege | … | … |
Then, per real finding:
| ID |
Severity |
File:line |
What |
Fix |
Finish with a one-line verdict in plain words ("No real security holes on the
static checks. One thing to double-check: …"), and for any item you skipped as a
false positive, one line on why it's safe. End with what you did not run
(e.g. "didn't execute typecheck/lint — say the word and I'll run them on this
trusted repo").
The 9 principles behind the checks
- Don't talk to the database directly from the client.
- Gatekeep every action (auth on every endpoint).
- Don't hide, withhold (enforce premium server-side).
- Keep secrets off the browser (public-by-design keys excepted).
- Don't do math on the phone (price/score server-side).
- Sanitize everything (validate inputs with a schema).
- Rate limit expensive endpoints.
- Don't log sensitive stuff.
- Audit with a second pair of eyes (a different model catches different blind spots).
Source
Upstream: https://github.com/buffalodebile/vibecoding-security-audit
Author: Burak Eregar (Mr Black AI). MIT licensed.
1---2name: web-security-audit3description: Read-only, production-safe security audit for web apps and vibecoded projects. Use for security audits, OWASP reviews, pentests, secret-leak checks, GDPR/RGPD reviews and pre-deploy verification. Covers 21 checks: client/server trust boundaries, auth and authorization, input validation, expensive-call ordering, rate limits, secrets/PII logs, dependencies, CORS, SQL injection, XSS, security headers, URL sinks, authenticated installers/updates, resource bounds, fail-closed parsing and container least privilege. Framework-agnostic across JavaScript/TypeScript and mixed stacks. Apply judgment to avoid false positives; the optional offline-first script only produces candidates and never replaces code review. Do not load-test, brute-force, fuzz production, trigger metered providers or run untrusted code.4---56# Web Security Audit78A read-only security pass for any web project — built so it works the same on a9Next.js app, a SvelteKit site, an Express API, or a completely different stack,10and so a non-security person can trust the result.1112## How this skill works (read this first)1314**You (Claude) are the auditor.** Do the 21 checks below by reading the code and15applying judgment. Do **not** rely on a tool's raw regex output as the verdict —16that is exactly what produces false positives (a regex sees `KEY` in a variable17name and panics, even when the key is public by design and the value is empty).18You read the code, understand the context, and decide.1920The bundled `security-audit.sh` (`run.ps1` on Windows) is an **optional21accelerator** for large codebases. It is never required. Treat its output as a22list of *candidates to confirm*, not findings. If the harness blocks running it,23or you can't trust the project enough to execute its scripts, just do the checks24by hand with Grep/Read — that path always works.2526### Ground rules27281. **Read-only by default.** The audit never edits files, installs packages,29 runs `audit fix`, or modifies dependencies. Reporting is the deliverable.302. **Never execute untrusted project code.** Dependency-audit commands do not run31 project code, but they do use the network; obtain permission first.32 `tsc` / `eslint` / build steps execute the project's own config and plugins —33 only run them on a project the user already trusts, and never let a tool34 auto-install a missing binary (no `npx <pkg>` that hits the network).353. **Confirm before you flag.** Every candidate must be opened and read. A match36 in a test fixture, an `.env.example` template, a comment, or a public-by-design37 key is **not** a finding.384. **Adapt to the repo.** Monorepo? Run the checks per app (`apps/web`,39 `packages/*`) — auto-detection that only looks at the repo root will miss40 nested apps. Non-JS project (Python, Go…)? Say so and scope to what applies.4142## Production-safe audit boundary4344Start with source, configuration, tests and dependency manifests. Against a live45site, stay passive and low-volume: normally one request per page/header you must46verify. Never brute-force credentials, fuzz production endpoints, enumerate at47scale, bypass bot protection, place orders, trigger email/SMS/AI generation, run48load/DoS tests, or probe infrastructure owned by a third party. If static evidence49is enough, make zero production requests.5051Dependency audits are network operations. Run them only when the user permits52network access; otherwise report them as not run. Never print a discovered secret,53token, email address, TOTP or raw provider error into the report—use file:line and54`[REDACTED]`.5556## The 21 checks — what to look for and how to read the result5758| # | Check | Severity |59|---|-------|----------|60| 1 | No database/ORM imports in client code | Error |61| 2 | Auth on every API route handler | Warning |62| 3 | Premium/role gating enforced server-side | Warning |63| 4 | No **real** secrets in client-exposed env vars | Error |64| 5 | Price/score/business math is server-side | Warning |65| 6 | Input validation on API routes | Warning |66| 7 | Rate limiting on expensive endpoints | Warning |67| 8 | No secrets/PII in logs | Error |68| 9 | Dependency vulnerabilities (`audit`) | Error/Warn |69| 10 | TypeScript typecheck (trusted projects only) | Error |70| 11 | Lint (trusted projects only) | Warning |71| 12 | No hardcoded secrets | Error |72| 13 | No wildcard/permissive CORS | Warning |73| 14 | No SQL injection (raw queries + interpolation) | Warning |74| 15 | No XSS sinks (`dangerouslySetInnerHTML`, `v-html`, `innerHTML`, `{@html}`) | Warning |75| 16 | CSP and browser/deployment security headers | Warning |76| 17 | External URLs/navigation sinks allow only `http:`/`https:` | Warning |77| 18 | Cheap bot/auth/rate/quota gates run before metered calls | Error |78| 19 | Installers/updates authenticate artifacts before execution | Error |79| 20 | External payloads are bounded, schema-validated and fail closed | Error |80| 21 | Containers use least privilege where practical | Warning |8182For each one:83841. **Find** — grep the patterns across source files (skip `node_modules`, build85 output: `.next` `.nuxt` `.svelte-kit` `dist` `build` `.output` `coverage`).862. **Open & read** — confirm it's real in context.873. **Decide** — real issue, accepted-with-reason, or false positive.8889Details where reading-with-judgment matters most:9091- **#1 client DB access** — flag an ORM/DB client import (`@prisma/client`,92 `drizzle-orm`, `mongoose`, `pg`, `mysql2`, `@supabase/supabase-js` used as a93 *service-role* client, …) reachable from browser code: a React `"use client"`94 file, a `.vue`/`.svelte` component, or anything bundled to the client. Server95 components, route handlers, server actions, and `*.server.ts` are fine.96- **#4 secrets in public env vars** — this is the #1 source of false positives.97 See the allowlist below. Only flag when a **real, non-empty secret value** is98 assigned to a client-exposed variable. Empty templates and public-by-design99 keys are **PASS**.100- **Static encrypted pages** — client-side AES-GCM can protect a published blob,101 but it is not server authentication: attackers can copy the ciphertext and102 guess passwords offline, there is no per-user revocation, and any same-origin103 XSS can steal a derived key held in browser storage. Require a high-entropy104 password, strong KDF, authenticated encryption, `noindex`/`no-store`, CSP and105 an explicit acceptance of the shared-password model; use server-side access106 control when individual identity or revocation matters.107- **#7 rate limiting** — only expensive routes need it: AI generation, payments,108 email/SMS, anything that costs money or compute per call. A plain CRUD GET does109 not need a rate limiter to pass.110- **#8 sensitive logs** — include PII and transient credentials: email/account111 identifiers, auth headers, session IDs, OTP/TOTP codes and raw upstream error112 bodies. Log a stable category/request ID and redact values.113- **#9 dependency audit** — with network permission, run `npm audit` / `pnpm114 audit` / `yarn audit` (no project code executes). Report high/critical as115 errors, low/moderate as warnings. Do **not** run `audit fix --force`.116- **#10/#11 typecheck & lint** — these execute project config. Run only on a117 trusted project, using the **locally installed** binary118 (`node_modules/.bin/tsc`, `node_modules/.bin/eslint`). If not installed, skip119 and say so — never auto-install.120- **#15 XSS** — an `innerHTML` match is a candidate, not an automatic finding.121 Trace every interpolated value. Constants and correctly escaped values can be122 safe; decrypted/generated HTML needs an explicit trust boundary and a CSP.123- **#17 URL sinks** — validate stored, generated and provider-supplied URLs at124 the final sink (`href`, `window.open`, redirects). Reject `javascript:`,125 `data:`, embedded credentials and non-HTTP(S) schemes.126- **#18 cost ordering** — for AI, image/logo enrichment, payment, email and SMS,127 prove the order in code: cheap validation → bot/auth check → rate limit →128 entitlement/quota/credit reservation → paid call. A limiter after the provider129 call does not protect the bill.130- **#19 update authenticity** — HTTPS and encryption are not artifact131 authentication. Before `tar`, `docker compose`, `bash` or replacing code,132 verify a signature or encrypt-then-MAC tag, cap size, extract to a scratch133 directory, validate required files, then swap. Root installers must use134 `mktemp`, not predictable `/tmp/name` files. Shared-key HMAC does not protect135 against a malicious holder of that key; prefer public-key signatures when that136 threat matters.137- **#20 fail closed** — cap response/body/ciphertext sizes and KDF work factors138 before expensive processing. Validate decrypted JSON types, enums, uniqueness,139 numeric finiteness/ranges and totals. Missing action lists must not default to140 `[]` when empty means delete, sell or revoke. Validate mode enums before141 selecting production/live defaults.142- **#21 containers** — review `read_only`, `cap_drop`, `no-new-privileges`,143 non-root users, secret mounts and network exposure per service. Absence is a144 hardening opportunity, not automatically exploitable.145146## Public-by-design — do NOT flag these as leaks147148These are **meant** to live in the browser. Finding them in client code or in a149`NEXT_PUBLIC_*` / `VITE_*` / `PUBLIC_*` variable is expected and correct. Verify150the intended protection exists, but do not report them as secret leaks:151152- **Supabase anon key** (`NEXT_PUBLIC_SUPABASE_ANON_KEY`) — public; protected by153 Row Level Security policies. Confirm RLS is on; the key itself is fine.154- **Stripe publishable key** (`pk_live_…`, `pk_test_…`,155 `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY`) — designed to be exposed. Only the156 **secret** key (`sk_live_…` / `sk_test_…`) is a real leak.157- **Firebase web config** (`apiKey`, `authDomain`, `projectId`, …) — public by158 design; protected by Firebase Security Rules + allowed-domains.159- **Clerk publishable key** (`pk_…`, `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`) — public.160- **Analytics / monitoring tokens** — PostHog (`NEXT_PUBLIC_POSTHOG_KEY`), Sentry161 DSN (`NEXT_PUBLIC_SENTRY_DSN`), GA/GTM IDs, Amplitude, Mixpanel browser tokens —162 all client-side by design.163- **Map / search browser tokens** — Mapbox `pk.…`, Google Maps browser key,164 Algolia **search-only** key — public, ideally restricted by domain/referrer.165- **Empty or placeholder values** — `.env.example`, `.env.sample`, `.env.template`166 with empty values or placeholders (`your-key-here`, `xxx`, `changeme`,167 `sk_test_xxx`, `<...>`) are templates, **not** secrets. Only a real value168 committed to a real `.env` is a leak.169170When you skip one of these, say *why* in the report (e.g. "anon key is public,171RLS confirmed in `supabase/`") so the user learns the reasoning, not just the verdict.172173## Always treat as a real secret (Error)174175A literal value matching any of these committed to the repo is a genuine leak —176rotate it immediately:177178- OpenAI / Anthropic `sk-…` ; Stripe **secret** `sk_live_…` / `sk_test_…`179- AWS `AKIA…` access keys ; Google server key `AIza…` ; Google OAuth `ya29.…`180- GitHub `ghp_…` / `gho_…` / `github_pat_…` ; GitLab `glpat-…`181- Slack `xoxb-…` / `xoxp-…` ; SendGrid `SG.…`182- JWTs hardcoded in source ; private keys (`-----BEGIN … PRIVATE KEY-----`)183- DB connection strings **with credentials**184 (`postgres://user:pass@…`, `mongodb+srv://user:pass@…`, `mysql://…`, `redis://…`)185186Also confirm `.env`, `.env.*` (except `.env.example`) are git-ignored.187188## Optional: the bash accelerator189190For big repos you may run the bundled script to generate candidates faster.191192```bash193# macOS / Linux / CI — from the project root194bash <skill-dir>/security-audit.sh # offline/local, warnings don't fail195bash <skill-dir>/security-audit.sh --network # opt in to registry dependency audit196bash <skill-dir>/security-audit.sh --ci # exit 1 on any error (for CI)197```198199```powershell200# Windows (delegates to Git Bash automatically)201& "<skill-dir>\run.ps1"202& "<skill-dir>\run.ps1" --ci203```204205`<skill-dir>` is wherever this skill is installed (e.g.206`~/.claude/skills/web-security-audit`).207208The script is read-only **except** `--fix`. Do **not** use `--fix` by default; it209modifies files (`eslint --fix`, non-forced `npm audit fix`). Only run it on210explicit user request, and review the `git diff` afterward. Whatever the script211prints, still confirm each candidate yourself per the allowlist above.212213## Report template214215After the pass, give the user a single table plus a plain-language verdict:216217```218| # | Check | Result | Detail |219|---|-------|--------|--------|220| 1 | Client DB access | PASS / FAIL | file:line or "none" |221| 2 | API auth | … | … |222…223| 15| XSS sinks | … | … |224…225| 21| Container least privilege | … | … |226```227228Then, per real finding:229230| ID | Severity | File:line | What | Fix |231|----|----------|-----------|------|-----|232233Finish with a one-line verdict in plain words ("No real security holes on the234static checks. One thing to double-check: …"), and for any item you skipped as a235false positive, one line on **why** it's safe. End with what you did *not* run236(e.g. "didn't execute typecheck/lint — say the word and I'll run them on this237trusted repo").238239## The 9 principles behind the checks2402411. Don't talk to the database directly from the client.2422. Gatekeep every action (auth on every endpoint).2433. Don't hide, withhold (enforce premium server-side).2444. Keep secrets off the browser (public-by-design keys excepted).2455. Don't do math on the phone (price/score server-side).2466. Sanitize everything (validate inputs with a schema).2477. Rate limit expensive endpoints.2488. Don't log sensitive stuff.2499. Audit with a second pair of eyes (a different model catches different blind spots).250251## Source252253Upstream: https://github.com/buffalodebile/vibecoding-security-audit254Author: Burak Eregar (Mr Black AI). MIT licensed.