diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..955f0ce --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,97 @@ +# Security Policy + +Thanks for taking the time to look at ScreenTinker's security. The project +is a one-person open-source effort, so response times reflect that — but +reports are taken seriously and handled in good faith. + +## Reporting a vulnerability + +**Primary channel — GitHub Security Advisories (preferred):** + +[github.com/screentinker/screentinker/security/advisories/new](https://github.com/screentinker/screentinker/security/advisories/new) + +GitHub's private advisory flow keeps the report off public issues, lets us +draft a fix collaboratively, and produces a CVE if appropriate. Use this +unless you have a reason not to. + +**Fallback — email:** + +`support@bytetinker.net` (the maintainer's consultancy inbox; the domain +intentionally differs from `screentinker.com` — it's the actively-monitored +business address rather than a project-domain alias that might not have +working mail delivery). + +Please include: +- A description of the issue and its impact +- Steps to reproduce (the more concrete, the better) +- The commit SHA or release tag you observed it on +- Any proof-of-concept code or payload, if you have one + +## Response timeline + +I aim to acknowledge reports within **3–5 business days** and update with a +triage assessment within **10 business days**. If you haven't heard back +in that window, please feel free to nudge — life happens, and reports +occasionally slip past. + +Fix timelines depend on severity, complexity, and whether the issue is on +the hosted instance (screentinker.com) or affects self-hosted deployments +too. Critical issues affecting the hosted instance generally get same-week +turnaround. + +## In scope + +Reports about the following are welcome and treated as security issues: + +- **Authentication / session bypass** (e.g. JWT forgery, login bypass, + privilege escalation) +- **Multi-tenancy boundary violations** (one workspace's data leaking into + another, organization-level isolation breaks) +- **XSS in widget rendering or admin UI** (e.g. unsandboxed widget content, + unescaped user input in dashboard surfaces) +- **CSRF** on state-changing endpoints +- **SQL injection** (deviations from parameterized queries are reportable) +- **Server-side request forgery** (SSRF) via widget URLs, content uploads, + webhook handlers, or similar +- **Insecure direct object reference** (accessing a resource by ID without + the proper tenancy gate) + +## Out of scope + +The following are acknowledged but not treated as in-scope security +issues for this project: + +- **Denial of service via excessive resource usage** (uploading large + files, opening many sockets, etc.) — operational concerns, not security + vulnerabilities. Rate limits exist where it matters most. +- **Social engineering** of the maintainer or other users +- **Misconfigurations of self-hosted instances** (e.g. exposing the server + to the internet without TLS, weak JWT secrets, default passwords). The + README documents recommended configuration; deviations are the operator's + responsibility. +- **Vulnerabilities in third-party dependencies** (Express, better-sqlite3, + socket.io, etc.) — please report those upstream. If a dependency CVE + affects ScreenTinker in a non-obvious way, that's worth flagging here too. +- **Reports generated by automated scanners** with no manual triage or + proof-of-concept (e.g. "your /robots.txt is missing" — not what this + project worries about) + +## Coordinated disclosure + +Please **wait until a fix has shipped to the hosted instance and +origin/main before public disclosure**. I'll keep you in the loop on +timing and confirm when it's safe to publish. For most issues that +window is a few weeks at most; if it stretches longer, that's a signal +something is more complex than expected and we'll coordinate. + +If you find a critical issue that's being actively exploited (or you +believe might be), please say so in the report — I'll prioritize +accordingly. + +## Acknowledgments + +If you'd like to be credited for a report, I'm happy to acknowledge you +by name in release notes and (when applicable) in the GitHub advisory +itself. Let me know in your report whether you'd like credit and how +you'd like to be named. Anonymous reports are also welcome — no credit +is required. diff --git a/frontend/js/views/widgets.js b/frontend/js/views/widgets.js index c7e17d2..d2e9526 100644 --- a/frontend/js/views/widgets.js +++ b/frontend/js/views/widgets.js @@ -114,7 +114,7 @@ function showPreviewModal(html) { ${t('widget.preview_title')} - + `; document.body.appendChild(overlay); // srcdoc resolves relative URLs against about:srcdoc, so inject pointing to our origin diff --git a/server/player/index.html b/server/player/index.html index 6c9fd8e..11c5815 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -1434,6 +1434,9 @@ iframe.src = `${serverUrl}/api/widgets/${item.widget_id}/render`; iframe.style.cssText = 'width:100%;height:100%;border:none;background:#000'; iframe.allow = 'autoplay; fullscreen'; + // Sandbox into a unique origin so widget scripts can't read window.parent + // state (localStorage / JWT). allow-scripts keeps inline widget code running. + iframe.setAttribute('sandbox', 'allow-scripts'); mount.appendChild(iframe); if (!isFollower) advanceTimer = setTimeout(nextItem, (item.duration_sec || 30) * 1000); } @@ -1459,6 +1462,9 @@ if (zone.zone_type === 'widget' && assignment.widget_id) { const iframe = document.createElement('iframe'); iframe.src = `${config.serverUrl}/api/widgets/${assignment.widget_id}/render`; + // Sandbox into a unique origin so widget scripts can't read window.parent + // state (localStorage / JWT). allow-scripts keeps inline widget code running. + iframe.setAttribute('sandbox', 'allow-scripts'); div.appendChild(iframe); } else if (isYoutubeZone) { createYoutubeEmbed(src, assignment, div);