Skip to content

Commit 62f9e45

Browse files
committed
Added way to request font permissions in iframe
1 parent 9728662 commit 62f9e45

6 files changed

Lines changed: 290 additions & 253 deletions

File tree

src/app/package.json

Lines changed: 126 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,128 @@
11
{
2-
"name": "gSender",
3-
"private": true,
4-
"type": "module",
5-
"scripts": {
6-
"dev": "node server",
7-
"build": "npm run build:client",
8-
"build:client": "vite build --outDir ../../dist/gsender/app --emptyOutDir",
9-
"preview": "cross-env NODE_ENV=production node server",
10-
"test": "../../node_modules/.bin/jest.cmd --config ../../jest.config.js --testPathPatterns=src/app"
11-
},
12-
"dependencies": {
13-
"@electron/remote": "^2.1.3",
14-
"@sentry/electron": "^7.7.1",
15-
"@sentry/react": "^9.28.0",
16-
"@serialport/parser-byte-length": "^13.0.0",
17-
"@serialport/parser-readline": "^13.0.0",
18-
"@sienci/avrgirl-arduino": "^5.0.3",
19-
"acorn": "^8.18.0",
20-
"acorn-walk": "^8.3.5",
21-
"axios": "^1.2.4",
22-
"basic-ftp": "^5.1.0",
23-
"bcrypt-nodejs": "0.0.3",
24-
"buffer-chunks": "^0.1.1",
25-
"chalk": "~2.4.2",
26-
"commander": "^10.0.0",
27-
"compression": "^1.8.1",
28-
"connect-restreamer": "~1.0.3",
29-
"consolidate": "~0.15.1",
30-
"cookie-parser": "~1.4.4",
31-
"cors": "^2.8.5",
32-
"crc-full": "^1.1.0",
33-
"debug": "^4.4.3",
34-
"deep-keys": "~0.5.0",
35-
"electron-log": "^5.4.3",
36-
"electron-store": "^11.0.2",
37-
"electron-updater": "^6.7.3",
38-
"ensure-array": "~1.0.0",
39-
"errorhandler": "~1.5.0",
40-
"escodegen": "~1.11.1",
41-
"esprima": "~4.0.1",
42-
"expand-tilde": "~2.0.2",
43-
"express": "~4.16.4",
44-
"express-jwt": "~5.3.1",
45-
"express-session": "^1.19.0",
46-
"gcode-interpreter": "~2.1.0",
47-
"gcode-parser": "~1.3.6",
48-
"hogan.js": "~3.0.2",
49-
"http-proxy": "~1.18.1",
50-
"i18next": "~15.0.9",
51-
"i18next-fs-backend": "^1.1.4",
52-
"i18next-http-middleware": "^3.2.1",
53-
"is-electron": "^2.2.2",
54-
"is-online": "^9.0.1",
55-
"jsonfile": "^6.2.0",
56-
"jsonwebtoken": "~8.5.1",
57-
"lodash": "~4.17.11",
58-
"method-override": "~3.0.0",
59-
"minimatch": "~3.0.4",
60-
"mkdirp": "~0.5.1",
61-
"morgan": "^1.10.1",
62-
"multer": "^1.4.5-lts.1",
63-
"nrf-intel-hex": "^1.3.0",
64-
"range_check": "~1.4.0",
65-
"react": "^18.2.0",
66-
"react-dom": "^18.2.0",
67-
"registry-auth-token": "~3.4.0",
68-
"registry-url": "~5.1.0",
69-
"rimraf": "~2.6.3",
70-
"serialport": "^13.0.0",
71-
"serve-favicon": "~2.5.0",
72-
"serve-static": "~1.13.2",
73-
"session-file-store": "^1.5.0",
74-
"shortid": "~2.2.14",
75-
"sirv": "^2.0.4",
76-
"socket.io": "^4.1.3",
77-
"spawn-default-shell": "~2.0.0",
78-
"superagent": "^6.1.0",
79-
"usb": "^2.17.0",
80-
"uuid": "~3.3.2",
81-
"watch": "~1.0.2",
82-
"webappengine": "~1.2.0",
83-
"winreg": "^1.2.5",
84-
"winston": "^3.19.0"
85-
},
86-
"devDependencies": {
87-
"@tanstack/router-devtools": "^1.46.7",
88-
"@tanstack/router-plugin": "^1.47.0",
89-
"@testing-library/jest-dom": "^6.9.1",
90-
"@testing-library/react": "^16.3.2",
91-
"@types/classnames": "^2.3.4",
92-
"@types/cli-color": "^2.0.6",
93-
"@types/express": "^4.17.21",
94-
"@types/jest": "^30.0.0",
95-
"@types/node": "^20.12.7",
96-
"@types/react": "^18.2.79",
97-
"@types/react-beautiful-dnd": "^13.1.8",
98-
"@types/react-dom": "^18.3.0",
99-
"@types/react-select": "^5.0.1",
100-
"@types/react-syntax-highlighter": "^15.5.13",
101-
"@types/redux-action": "^1.2.8",
102-
"@types/redux-batched-subscribe": "^0.1.7",
103-
"@types/redux-logger": "^3.0.13",
104-
"@types/superagent": "^8.1.9",
105-
"@types/uuid": "^10.0.0",
106-
"@vitejs/plugin-react": "^4.2.1",
107-
"autoprefixer": "^10.4.20",
108-
"cross-env": "^7.0.3",
109-
"jest": "^30.2.0",
110-
"jest-environment-jsdom": "^30.2.0",
111-
"postcss": "^8.4.47",
112-
"tailwindcss": "^3.4.13",
113-
"ts-jest": "^29.4.6",
114-
"typescript": "^5.4.5",
115-
"vite": "^5.4.2",
116-
"vite-tsconfig-paths": "^5.0.1"
117-
},
118-
"author": {
119-
"name": "Sienci Labs <hi@sienci.com>",
120-
"email": "hi@sienci.com"
121-
},
122-
"license": "MIT",
123-
"repository": {
124-
"type": "git",
125-
"url": "https://github.com/Sienci-Labs/gsender"
126-
},
127-
"version": "1.7.0-Edge-1"
2+
"name": "gSender",
3+
"private": true,
4+
"type": "module",
5+
"scripts": {
6+
"dev": "node server",
7+
"build": "npm run build:client",
8+
"build:client": "vite build --outDir ../../dist/gsender/app --emptyOutDir",
9+
"preview": "cross-env NODE_ENV=production node server",
10+
"test": "../../node_modules/.bin/jest.cmd --config ../../jest.config.js --testPathPatterns=src/app"
11+
},
12+
"dependencies": {
13+
"@electron/remote": "^2.1.3",
14+
"@sentry/electron": "^7.7.1",
15+
"@sentry/react": "^9.28.0",
16+
"@serialport/parser-byte-length": "^13.0.0",
17+
"@serialport/parser-readline": "^13.0.0",
18+
"@sienci/avrgirl-arduino": "^5.0.3",
19+
"acorn": "^8.18.0",
20+
"acorn-walk": "^8.3.5",
21+
"axios": "^1.2.4",
22+
"basic-ftp": "^5.1.0",
23+
"bcrypt-nodejs": "0.0.3",
24+
"buffer-chunks": "^0.1.1",
25+
"chalk": "~2.4.2",
26+
"commander": "^10.0.0",
27+
"compression": "^1.8.1",
28+
"connect-restreamer": "~1.0.3",
29+
"consolidate": "~0.15.1",
30+
"cookie-parser": "~1.4.4",
31+
"cors": "^2.8.5",
32+
"crc-full": "^1.1.0",
33+
"debug": "^4.4.3",
34+
"deep-keys": "~0.5.0",
35+
"electron-log": "^5.4.3",
36+
"electron-store": "^11.0.2",
37+
"electron-updater": "^6.7.3",
38+
"ensure-array": "~1.0.0",
39+
"errorhandler": "~1.5.0",
40+
"escodegen": "~1.11.1",
41+
"esprima": "~4.0.1",
42+
"expand-tilde": "~2.0.2",
43+
"express": "~4.16.4",
44+
"express-jwt": "~5.3.1",
45+
"express-session": "^1.19.0",
46+
"gcode-interpreter": "~2.1.0",
47+
"gcode-parser": "~1.3.6",
48+
"hogan.js": "~3.0.2",
49+
"http-proxy": "~1.18.1",
50+
"i18next": "~15.0.9",
51+
"i18next-fs-backend": "^1.1.4",
52+
"i18next-http-middleware": "^3.2.1",
53+
"is-electron": "^2.2.2",
54+
"is-online": "^9.0.1",
55+
"jsonfile": "^6.2.0",
56+
"jsonwebtoken": "~8.5.1",
57+
"lodash": "~4.17.11",
58+
"method-override": "~3.0.0",
59+
"minimatch": "~3.0.4",
60+
"mkdirp": "~0.5.1",
61+
"morgan": "^1.10.1",
62+
"multer": "^1.4.5-lts.1",
63+
"nrf-intel-hex": "^1.3.0",
64+
"range_check": "~1.4.0",
65+
"react": "^18.2.0",
66+
"react-dom": "^18.2.0",
67+
"registry-auth-token": "~3.4.0",
68+
"registry-url": "~5.1.0",
69+
"rimraf": "~2.6.3",
70+
"serialport": "^13.0.0",
71+
"serve-favicon": "~2.5.0",
72+
"serve-static": "~1.13.2",
73+
"session-file-store": "^1.5.0",
74+
"shortid": "~2.2.14",
75+
"sirv": "^2.0.4",
76+
"socket.io": "^4.1.3",
77+
"spawn-default-shell": "~2.0.0",
78+
"superagent": "^6.1.0",
79+
"usb": "^2.17.0",
80+
"uuid": "~3.3.2",
81+
"watch": "~1.0.2",
82+
"webappengine": "~1.2.0",
83+
"winreg": "^1.2.5",
84+
"winston": "^3.19.0"
85+
},
86+
"devDependencies": {
87+
"@tanstack/router-devtools": "^1.46.7",
88+
"@tanstack/router-plugin": "^1.47.0",
89+
"@testing-library/jest-dom": "^6.9.1",
90+
"@testing-library/react": "^16.3.2",
91+
"@types/classnames": "^2.3.4",
92+
"@types/cli-color": "^2.0.6",
93+
"@types/express": "^4.17.21",
94+
"@types/jest": "^30.0.0",
95+
"@types/node": "^20.12.7",
96+
"@types/react": "^18.2.79",
97+
"@types/react-beautiful-dnd": "^13.1.8",
98+
"@types/react-dom": "^18.3.0",
99+
"@types/react-select": "^5.0.1",
100+
"@types/react-syntax-highlighter": "^15.5.13",
101+
"@types/redux-action": "^1.2.8",
102+
"@types/redux-batched-subscribe": "^0.1.7",
103+
"@types/redux-logger": "^3.0.13",
104+
"@types/superagent": "^8.1.9",
105+
"@types/uuid": "^10.0.0",
106+
"@vitejs/plugin-react": "^4.2.1",
107+
"autoprefixer": "^10.4.20",
108+
"cross-env": "^7.0.3",
109+
"jest": "^30.2.0",
110+
"jest-environment-jsdom": "^30.2.0",
111+
"postcss": "^8.4.47",
112+
"tailwindcss": "^3.4.13",
113+
"ts-jest": "^29.4.6",
114+
"typescript": "^5.4.5",
115+
"vite": "^5.4.2",
116+
"vite-tsconfig-paths": "^5.0.1"
117+
},
118+
"author": {
119+
"name": "Sienci Labs <hi@sienci.com>",
120+
"email": "hi@sienci.com"
121+
},
122+
"license": "MIT",
123+
"repository": {
124+
"type": "git",
125+
"url": "https://github.com/Sienci-Labs/gsender"
126+
},
127+
"version": "1.7.0-Edge-1"
128128
}

src/app/src/features/Plugins/components/PluginPanel.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ type PluginPanelProps = {
1515
title?: string;
1616
};
1717

18+
// Maps a manifest permission to the Permissions-Policy feature it delegates
19+
// to the plugin iframe. Only permissions with a browser-feature counterpart
20+
// belong here — most permissions (workspace:read, etc.) gate the message
21+
// bridge instead and have no iframe-level equivalent.
22+
const IFRAME_ALLOW_MAP: Partial<Record<string, string>> = {
23+
"local-fonts": "local-fonts",
24+
};
25+
26+
const buildIframeAllow = (permissions: PluginRecord["permissions"]): string =>
27+
(permissions ?? [])
28+
.map((permission) => IFRAME_ALLOW_MAP[permission])
29+
.filter((feature): feature is string => Boolean(feature))
30+
.join("; ");
31+
1832
const PluginPanel = ({ plugin, className = "", title }: PluginPanelProps) => {
1933
// Bumped on dev live-reload to force the iframe to re-fetch its content.
2034
const [reloadToken, setReloadToken] = useState(0);
@@ -66,6 +80,7 @@ const PluginPanel = ({ plugin, className = "", title }: PluginPanelProps) => {
6680
src={iframeSrc}
6781
className="flex-1 w-full min-h-[320px] border border-gray-200 rounded-md dark:border-outline"
6882
sandbox="allow-scripts allow-forms allow-same-origin"
83+
allow={buildIframeAllow(plugin.permissions)}
6984
/>
7085
</div>
7186
);

src/app/src/features/Plugins/types.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export type PluginRecord = {
3131
description: string;
3232
engine: string | null;
3333
capabilities: PluginCapabilitiesWire;
34+
permissions: PluginPermissionsType[];
3435
enabled: boolean;
3536
valid: boolean;
3637
errors: string[];
@@ -45,12 +46,13 @@ export type PluginsResponse = {
4546
plugins: PluginRecord[];
4647
};
4748

48-
export type PluginPermissionsType =
49+
export type PluginPermissionsType =
4950
"machine:read"
5051
| "machine:write"
5152
| "visualizer:load"
5253
| "workspace:read"
53-
| "redux:read";
54+
| "redux:read"
55+
| "local-fonts";
5456

5557
export type PluginTopicsType = "workspace" | "redux"
5658

src/main.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,19 @@ const main = () => {
205205
try {
206206
await session.defaultSession.clearCache();
207207

208+
// Plugin iframes are same-origin with the app, so a single app-wide
209+
// grant is enough — per-plugin scoping happens at the iframe's
210+
// `allow="local-fonts"` attribute (see PluginPanel.tsx), which is only
211+
// set for plugins that declare "local-fonts" in their manifest.
212+
session.defaultSession.setPermissionCheckHandler(
213+
(_webContents, permission) => permission === "local-fonts",
214+
);
215+
session.defaultSession.setPermissionRequestHandler(
216+
(_webContents, permission, callback) => {
217+
callback(permission === "local-fonts");
218+
},
219+
);
220+
208221
windowManager = new WindowManager();
209222
// Create and show splash before server starts
210223
const splashScreen = windowManager.createSplashScreen({

0 commit comments

Comments
 (0)