# Plugin Security and Privacy

> How to write a Broadcast plugin that is safe for your users and their subscribers: permissions, secrets, personal data and input, in plain words.

Source: https://sendbroadcast.net/docs/plugins-security

> Coming in Broadcast 3.0. Until then, this page applies only to the Upcoming (edge) build.

Your plugin works with real people's data. This page lists the rules, and the reason for each. Broadcast enforces many of them for you, and you must follow the rest.

## What Broadcast does for you

- Your code runs in its own process. It cannot reach the database, files or server settings.
- Each call to Broadcast is checked: the plugin, the version, the channel and the permission.
- Your code sees only the channel it runs in. It cannot ask for another one.
- The network gate lets your code reach only declared hosts. It refuses private and internal addresses.
- Secrets are stored encrypted, and a page never sees them.
- A plugin page has no access to the admin's session.
- The core adds the unsubscribe link and checks consent **after** all plugin hooks. No plugin can change that.
- With no `subscribers` permission, a plugin gets no personal data in events.
- No plugin can write a tag or custom field that holds a topic, because a topic is consent.
- A plugin event reaches a webhook only if an admin turns it on. Broadcast builds that webhook, with no email address.
- Email addresses are hidden in logs.
- An admin reviews each version. Only an admin can deploy.

## What you must do

### Ask for the least

Ask only for what the feature needs. Use `read` if you do not write. Use an exact host, such as `api.example.com`, and not `*.example.com`.

A short list is also good for you, because the admin reads it before deciding.

### Keep secrets secret

- Mark API keys, tokens and webhook URLs `"secret": true`.
- Read them with `await ctx.settings.secret("name")`, in the call that needs them.
- Never log them, never store them, and never return them from a hook.
- Send a secret only to the service it is for.

Do not put a key in your code. An admin reads the code, and the code is stored in plain text.

### Treat personal data with care

Emails, names and custom fields are personal data.

- Read only the fields you need.
- Store personal data with `ctx.store.forSubscriber(id)`. It is deleted with the person.
- If you keep it anywhere else, add the [Subscriber Deleted Hook](https://sendbroadcast.net/docs/plugins-hook-subscriber-deleted) and delete it there.
- Never write personal data to the log. Log the subscriber ID.
- Send it only to the hosts the manifest declares, and only for the reason the admin expects.

If you send data to another service, say so in your plugin's description. An admin may need to name that service in a privacy notice.

### Never trust input

Input is anything that comes from outside your code:

- The `value` of a [public action](https://sendbroadcast.net/docs/plugins-hook-public-actions). It is not signed.
- The `body` of an [API endpoint](https://sendbroadcast.net/docs/plugins-hook-api-endpoints).
- The `input` of `pageData` and of an [AI tool](https://sendbroadcast.net/docs/plugins-hook-ai-tools).
- `signup_ref` on an event, and anything in `custom_data`.
- Everything a remote service answers.

Check the type and the length, and compare it to a list of allowed values when you can.

```javascript
const ALLOWED = ["good", "bad"];
if (!ALLOWED.includes(value)) return { title: "Sorry", message: "We do not know that answer." };
```

Be careful with a key built from input. `new Map()` and `Object.hasOwn` are safer than a plain object lookup, because `__proto__` is a valid string.

### Write safe pages

- Put text in the page with `textContent`, never `innerHTML`.
- Do not ask for a Broadcast password.
- Do not make a page that looks like a Broadcast sign-in page.

### Be a good guest

- Handle repeats, so that a retry does no harm.
- Use bulk calls for many items.
- Do not loop forever. A slow call is stopped, and the failures count.

## Permissions change trust

Each new permission asks for more trust. When a new version asks for other permissions or hooks, the admin sees the new ones marked **New**.

Changes to your events, rules and settings should only add things. If you rename a setting, its saved value is lost. If you rename a rule, saved segments break. Use a new name and keep the old one.

## If you find a problem

If your plugin has a security bug, tell the people who run it, and release a fixed version as soon as you can. An admin can turn off a plugin in a channel at once, or stop all plugins with safe mode. See [safe mode](https://sendbroadcast.net/docs/plugins-install#safe-mode).
