Skip to content

Commit 5d625a5

Browse files
committed
fix(manifest): require http.serve for hosted styles and scripts
1 parent 9410a32 commit 5d625a5

11 files changed

Lines changed: 95 additions & 25 deletions

File tree

examples/js/scripts-demo/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/js/scripts-demo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "scripts-demo",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"private": true,
55
"scripts": {
66
"build": "owncast-plugin build"

examples/js/scripts-demo/plugin.manifest.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
"api": "1",
33
"name": "Example Scripts Demo",
44
"slug": "scripts-demo",
5-
"version": "0.2.0",
5+
"version": "0.2.1",
66
"description": "Ships a JavaScript file that pins a banner to the bottom of the viewer page (and logs to the console) so an admin can see at a glance that the plugin's script loaded, with no dependency on a live stream or chat.",
77
"permissions": [
8-
"ui.modify"
8+
"ui.modify",
9+
"http.serve"
910
],
1011
"scripts": [
1112
"client.js"

examples/js/styles-demo/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/js/styles-demo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "styles-demo",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"private": true,
55
"scripts": {
66
"build": "owncast-plugin build"

examples/js/styles-demo/plugin.manifest.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
"api": "1",
33
"name": "Example Styles Demo",
44
"slug": "styles-demo",
5-
"version": "0.2.0",
5+
"version": "0.2.1",
66
"description": "Ships a CSS file that pins a banner to the top of the viewer page so an admin can see at a glance that the plugin's stylesheet loaded, with no dependency on a live stream or chat.",
77
"permissions": [
8-
"ui.modify"
8+
"ui.modify",
9+
"http.serve"
910
],
1011
"styles": [
1112
"theme.css"

examples/js/viewer-gate/package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/js/viewer-gate/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "viewer-gate",
3-
"version": "0.2.0",
3+
"version": "0.2.1",
44
"private": true,
55
"scripts": {
66
"build": "owncast-plugin build"

examples/js/viewer-gate/plugin.manifest.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
"api": "1",
33
"name": "Example Viewer Gate",
44
"slug": "viewer-gate",
5-
"version": "0.2.0",
5+
"version": "0.2.1",
66
"description": "Shows a confirmation modal when a viewer opens the page. Yes dismisses the modal; No redirects away. Demonstrates combining manifest.styles and manifest.scripts in a single plugin.",
77
"permissions": [
8-
"ui.modify"
8+
"ui.modify",
9+
"http.serve"
910
],
1011
"styles": [
1112
"modal.css"

host-runtime/plugin/manifest.go

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,12 @@ func (m *Manifest) Validate() error {
281281
a.Icon = rewritten
282282
}
283283
// manifest.styles: CSS files the plugin contributes to the viewer
284-
// page. The host reads each file from assets/ and concatenates the
285-
// bytes into the customStyles response on /api/config, so only
286-
// ui.modify is required (the plugin paints inside Owncast's
287-
// chrome). Path rules mirror actions (relative paths auto-prefix
288-
// to /plugins/<slug>/, cross-plugin paths rejected); external
284+
// page. The host serves each file from the plugin's own URL
285+
// namespace (/plugins/<slug>/...) and injects it into the viewer,
286+
// so both ui.modify (the plugin paints inside Owncast's chrome)
287+
// and http.serve (the host serves the bytes) are required. Path
288+
// rules mirror actions (relative paths auto-prefix to
289+
// /plugins/<slug>/, cross-plugin paths rejected); external
289290
// http(s):// targets are rejected outright so admins see every
290291
// file that will land in their viewer's global CSS scope.
291292
if len(m.Styles) > 0 {
@@ -297,6 +298,11 @@ func (m *Manifest) Validate() error {
297298
"so it's visible to anyone reviewing the manifest that " +
298299
"the plugin restyles Owncast's UI")
299300
}
301+
if !hasHttpServe {
302+
return errors.New(
303+
"manifest.styles requires the \"http.serve\" permission so the " +
304+
"host can serve the bundled CSS files at /plugins/<slug>/ URLs")
305+
}
300306
for i, raw := range m.Styles {
301307
if strings.TrimSpace(raw) == "" {
302308
return fmt.Errorf("manifest.styles[%d] is empty", i)
@@ -324,8 +330,8 @@ func (m *Manifest) Validate() error {
324330
}
325331
// manifest.scripts: JS files the plugin contributes to the
326332
// viewer page. Same rules as styles, applied to .js entries;
327-
// inlined into /customjavascript by the host, so only ui.modify
328-
// is required.
333+
// the host serves them from /plugins/<slug>/ and loads them into
334+
// the viewer page, so ui.modify + http.serve are required.
329335
if len(m.Scripts) > 0 {
330336
if !hasUIModify {
331337
return errors.New(
@@ -335,6 +341,11 @@ func (m *Manifest) Validate() error {
335341
"ui.modify so it's visible to anyone reviewing the " +
336342
"manifest that the plugin runs code inside Owncast's chrome")
337343
}
344+
if !hasHttpServe {
345+
return errors.New(
346+
"manifest.scripts requires the \"http.serve\" permission so the " +
347+
"host can serve the bundled JavaScript files at /plugins/<slug>/ URLs")
348+
}
338349
for i, raw := range m.Scripts {
339350
if strings.TrimSpace(raw) == "" {
340351
return fmt.Errorf("manifest.scripts[%d] is empty", i)

0 commit comments

Comments
 (0)