Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 54 additions & 3 deletions src/content/docs/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ These attributes control the fundamental behavior of the widget.
| `data-label` | `"Feedback"` | Text label displayed next to the button icon |
| `data-category-labels` | built-in labels | Self-hosted JSON mapping from built-in categories to GitHub labels |
| `data-button` | `"true"` | Show the floating button: `"true"` or `"false"` |
| `data-auth-token-provider` | none | Name of a global function that returns a self-hosted auth token |
| `data-welcome` | `"Report a bug or request a feature"` | Welcome message displayed at the top of the form |
| `data-show-issue-link` | `"public"` | Show the GitHub issue link after submission: `"public"`, `"always"`, or `"never"` |

Expand Down Expand Up @@ -199,6 +200,37 @@ Self-hosted deployments can also let pages set labels via the `data-category-lab

Only enable this when your worker's `ALLOWED_ORIGINS` is locked down to trusted domains.

## Auth Token Provider

Self-hosted deployments can require short-lived host-app auth tokens before BugDrop accepts feedback. This is useful when the widget is only meant for authenticated users in your product.

First, define a global function that fetches a token from your own backend:

```html
<script>
window.getBugDropToken = async function getBugDropToken() {
const response = await fetch('/api/bugdrop-token', { credentials: 'include' });
if (!response.ok) throw new Error('Unable to create BugDrop token');
const { token } = await response.json();
return token;
};
</script>
```

Then reference that function on the BugDrop script tag:

```html
<script
src="https://bugdrop.yourcompany.com/widget.js"
data-repo="owner/repo"
data-auth-token-provider="getBugDropToken"
></script>
```

The provider may return either a raw token or a value already prefixed with `Bearer `. BugDrop calls it before installation checks and feedback submissions, then sends the token in the `Authorization` header.

This attribute only adds the client-side header. Your self-hosted worker must also set `AUTH_TOKEN_SECRET` to enforce the token gate. The worker secret and the host app's token-signing secret must be byte-for-byte identical; if a valid-looking `bd1` token is rejected, check for trailing newlines or quoting differences. See the [Self-Hosting guide](https://github.com/mean-weasel/bugdrop/blob/main/SELF_HOSTING.md) for the worker environment variables, secret hygiene notes, and token claim format.

### Rules and behavior

- Recognized keys: `bug`, `feature`, `question`. Other keys are ignored with a warning.
Expand Down Expand Up @@ -297,9 +329,28 @@ Supported attributes:
- `data-bugdrop-redacted`
- `data-bugdrop-mask`

This is best-effort visual masking, not a data-loss-prevention or security boundary. BugDrop can only mask content it can measure in the page DOM. Unmarked sensitive information can still appear, and browser rendering limits can apply to canvas, image, video, iframe, SVG, shadow DOM, pseudo-element, and highly custom control content.

Selected-area screenshots are rendered to the selected dimensions and masks are translated into that crop, but the capture still runs client-side against the page DOM. Avoid screenshots entirely for pages where client-side screenshot rendering is unacceptable.
This is visual masking, not a data-loss-prevention or security boundary.
BugDrop can only mask content it can measure in the page DOM. Unmarked
sensitive information can still appear, and browser rendering limits can apply
to canvas, image, video, iframe, SVG, shadow DOM, pseudo-element, and highly
custom control content.

Masking works in full-page, selected-element, selected-area, and automatic
screenshot modes when BugDrop renders the page through its DOM screenshot path.
For selected-area captures, mask rectangles are translated into the selected
crop. For selected-element captures with surrounding context enabled, masks are
computed relative to the captured context element.

| Capture path | Automatic DOM masks | User preview | Manual redact tool | Limitation warning | Notes |
| --- | --- | --- | --- | --- | --- |
| Full page | Yes | Yes | Yes | Yes | Uses DOM screenshot rendering unless the page is too complex |
| Selected element | Yes | Yes | Yes | Yes | Masks are relative to the captured element or surrounding context |
| Selected area | Yes | Yes | Yes | Yes, scoped to the crop | Masks outside the selected crop are ignored |
| Automatic screenshot | Yes | No | No | No in the current UI | Use only when marked sensitive regions are stable and supported |
| Native viewport fallback | No | Yes | Yes | Existing unavailable-mask warning | Used for very complex pages; users must review manually |

Disable screenshots or use `data-screenshot="optional"` on pages where sensitive
pixels cannot be reliably enclosed by DOM boxes.

```html
<!-- Automatically attach a full-page screenshot -->
Expand Down
7 changes: 3 additions & 4 deletions src/content/docs/faq.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -208,16 +208,15 @@ If the widget appears but submissions fail:

1. **Check the GitHub App** -- Make sure it is installed on the correct repository with Issues and Contents permissions
2. **Check rate limits** -- You may have hit the per-IP or per-repository rate limit
3. **Check CSP** -- If your site uses a strict Content Security Policy, make sure `https://bugdrop.neonwatty.workers.dev` is allowed in `connect-src` so the browser can submit feedback to the Worker API
4. **Check the console** -- Look for error messages in the browser developer console
5. **Check the network tab** -- Inspect the API request to see the response status and body
3. **Check the console** -- Look for error messages in the browser developer console
4. **Check the network tab** -- Inspect the API request to see the response status and body

### Screenshots are not being uploaded

If issues are created but without screenshots:

1. **Check branch protection** -- The GitHub App needs permission to push to the `bugdrop-screenshots` branch. See the [Installation](/docs/installation) page for details on branch protection configuration.
2. **Check CSP** -- Make sure your Content Security Policy allows the BugDrop widget script in `script-src` and feedback submission in `connect-src`.
2. **Check CSP** -- Make sure your Content Security Policy allows the BugDrop widget script.
3. **Cross-origin content** -- Browser screenshot rendering cannot capture cross-origin iframes or images loaded without CORS headers. These elements may appear blank in the screenshot.

### Can I use BugDrop on a static site?
Expand Down
8 changes: 3 additions & 5 deletions src/content/docs/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ BugDrop to visually cover in supported screenshot modes, mark those elements wit

BugDrop covers each marked element with an opaque rectangle on supported captured screenshots.
Password inputs and credit-card autocomplete fields are masked automatically. See
[Screenshot masking](/docs/security#screenshot-masking) on the Security page for details.
[Screenshot masking](/security#screenshot-masking) on the Security page for details.

## Step 3: You Are Done

Expand All @@ -111,12 +111,10 @@ The BugDrop script tag should **not** include the `async` or `defer` attributes.
If your site uses a Content Security Policy, allow the BugDrop worker domain in your CSP directives:

```
Content-Security-Policy:
script-src 'self' https://bugdrop.neonwatty.workers.dev;
connect-src 'self' https://bugdrop.neonwatty.workers.dev;
Content-Security-Policy: script-src 'self' https://bugdrop.neonwatty.workers.dev;
```

`script-src` lets the widget load. `connect-src` lets the browser submit feedback to the BugDrop Worker API.
If you have a strict CSP, make sure the BugDrop worker domain is included in your `script-src` directive.

### Branch Protection and the Screenshots Branch

Expand Down
77 changes: 45 additions & 32 deletions src/content/docs/security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ BugDrop is built with a privacy-first approach:

### Screenshot masking

You can mark sensitive elements so BugDrop visually covers them in supported screenshot modes. Add `data-bugdrop-redact` or `data-bugdrop-mask` to any element you want covered:
BugDrop supports developer-configured visual masking for screenshots. Add
`data-bugdrop-redact` or `data-bugdrop-mask` to any DOM element that should be
covered:

```html
<input type="email" data-bugdrop-redact />
Expand All @@ -92,46 +94,48 @@ You can mark sensitive elements so BugDrop visually covers them in supported scr
Supported explicit attributes are `data-bugdrop-redact`, `data-bd-redact`,
`data-bugdrop-redacted`, and `data-bugdrop-mask`.

When a user submits feedback, BugDrop plans redactions from matching DOM
elements, then paints an opaque rectangle over each target's measured bounding
box on supported captured PNGs. In manual screenshot flows, the masked image is
shown in the annotator preview so the user can audit it before submitting. In
automatic screenshot mode, BugDrop applies supported masks but submits without
showing the preview step.
For supported DOM-rendered captures, when masking succeeds, BugDrop records the
geometry of matching DOM elements, renders the screenshot, then paints opaque
rectangles over those measured boxes in the PNG. The submitted image contains
the black rectangles. If masking fails, BugDrop discards the screenshot instead
of uploading it. The original page DOM is not mutated.

Masking is best-effort visual coverage, not a data-loss-prevention or security
boundary. Users should still review screenshots before submitting when the manual
screenshot flow is enabled.
Masking is visual coverage, not data-loss prevention. BugDrop does not inspect
text or pixels to discover secrets. Developers must mark the sensitive regions
they control, and users should review manual screenshots before submitting.

**Inheritance.** When an ancestor has `data-bugdrop-mask`, the entire ancestor box is
masked as a single rectangle. Descendants do not get individual rectangles — this
prevents gaps from CSS `gap` or non-masked siblings inside a masked container.

**Built-in defaults.** These are always masked, with or without an explicit attribute:
**Built-in defaults.** In supported DOM-rendered screenshot paths, BugDrop
automatically masks these with or without an explicit attribute:

- `input[type="password"]`
- Any input with `autocomplete="cc-number"`, `cc-csc`, or `cc-exp`

**Known limitations:**

- Elements inside open Shadow DOM are traversed when the browser exposes the
shadow root. Closed Shadow DOM cannot be traversed; mark the host element if
the whole custom control should be covered.
- Iframe contents are not traversed. Mark the iframe element itself if the whole
embedded frame should be visually covered.
- BugDrop does not inspect pixels or text inside canvas, image, video, plugin,
or iframe content. Mark the containing DOM element if that entire region should
be visually covered.
- Mask rectangles are collected at the start of capture. If the page reflows or reveals
sensitive elements between collection and the moment `html-to-image` finishes
rendering, the mask may not cover the final pixels. Keep masked content stable during
the brief capture window.
- **Viewport capture fallback (very complex pages):** When the page exceeds the
full-page DOM-complexity threshold, BugDrop falls back to a screen-recording
capture (via the browser's `getDisplayMedia` API). That path captures the
visible viewport directly and does not apply element masks. If you rely on
masking, ensure the user is not capturing through this fallback when sensitive
elements are visible.
These defaults do not apply to native viewport capture or skipped/failed
screenshot paths.

| Surface | Behavior | Recommended control |
| --- | --- | --- |
| Regular DOM elements | Marked element box is covered | Mark the smallest stable container that fully encloses the sensitive content |
| Password and credit-card inputs | Covered automatically | No extra attribute required, but explicit marks are fine |
| Open Shadow DOM | Traversed when the browser exposes `shadowRoot` | Mark inner controls or the host |
| Closed Shadow DOM | Not traversed | Mark the host custom element |
| Iframes | Iframe internals are not traversed | Mark the iframe element if the whole embedded frame is sensitive |
| Canvas, image, SVG, video | Internal pixels are not inspected | Mark the element or a containing wrapper |
| Pseudo-elements and highly custom controls | Generated pixels are not inspected separately | Mark a stable wrapper around the whole control |
| Native viewport capture fallback | Element masks cannot be applied | Avoid viewport fallback on pages that require masking |

If a marked region includes embedded, media, or pixel-rendered content, BugDrop
covers the marked element box and warns the user in manual screenshot review
flows that it cannot inspect the internal pixels. Automatic screenshot mode
still applies supported masks, but it submits without showing a review step.

Mask rectangles are measured at capture start. If the page reflows or reveals
sensitive elements after that measurement but before rendering finishes, a mask
can become stale. Keep sensitive marked regions stable during capture.

The only network requests BugDrop makes are:

Expand Down Expand Up @@ -211,7 +215,16 @@ If you run your own instance of BugDrop:
3. **Monitor your Cloudflare Worker logs** for unusual activity
4. **Add edge protections** such as WAF rules, CAPTCHA, bot detection, and allowlists as needed
5. **Define retention/cleanup** for the `bugdrop-screenshots` branch
6. **Keep your instance updated** with the latest version
6. **Use host-app auth tokens** when the widget should only be available to authenticated users
7. **Keep your instance updated** with the latest version

### Host-App Auth Tokens

For private or authenticated applications, self-hosted workers can require a signed token on feedback submissions. Set `AUTH_TOKEN_SECRET` on the BugDrop worker, mint short-lived tokens from your own backend after checking the user's session, and configure the widget with `data-auth-token-provider`.

This protects the feedback endpoint from direct API calls that bypass browser CORS. CORS still matters for browser isolation, but it is not an authentication boundary. The token should include the exact target repository and expire quickly, usually within 5 minutes.

If installation status is also sensitive, set `AUTH_TOKEN_REQUIRED_FOR_CHECK = "true"` so the widget's `/check/:owner/:repo` request requires the same token.

## Reporting Security Issues

Expand Down
Loading