@flamingo-stack/openframe-frontend-core 0.0.442 → 0.0.443
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-J5NO7PAG.cjs → chunk-2CJ734N2.cjs} +3 -3
- package/dist/{chunk-J5NO7PAG.cjs.map → chunk-2CJ734N2.cjs.map} +1 -1
- package/dist/{chunk-UFBUQO7Z.cjs → chunk-323TCNF3.cjs} +18 -18
- package/dist/{chunk-UFBUQO7Z.cjs.map → chunk-323TCNF3.cjs.map} +1 -1
- package/dist/{chunk-63TSTDME.js → chunk-3ZE55IH6.js} +5 -5
- package/dist/{chunk-Y3NFB6HR.js → chunk-5U6OZ5FD.js} +4 -4
- package/dist/{chunk-IAZ5AN2E.js → chunk-6IPNXMB7.js} +6 -6
- package/dist/{chunk-XMCVUYKC.cjs → chunk-6NO4YYU7.cjs} +12 -12
- package/dist/{chunk-XMCVUYKC.cjs.map → chunk-6NO4YYU7.cjs.map} +1 -1
- package/dist/{chunk-QO6MCQYE.cjs → chunk-7CZ4MJSI.cjs} +13 -10
- package/dist/chunk-7CZ4MJSI.cjs.map +1 -0
- package/dist/{chunk-FPE3SQ5D.js → chunk-A7MG4SBL.js} +3 -3
- package/dist/{chunk-SM26C2L7.js → chunk-AZQOX2RS.js} +2 -2
- package/dist/{chunk-JYI4R25Q.js → chunk-DOVBNECK.js} +3 -3
- package/dist/{chunk-7KDR5P23.js → chunk-FQASXKBK.js} +2 -2
- package/dist/{chunk-LFWPWIAC.cjs → chunk-H4W3NCR2.cjs} +7 -7
- package/dist/{chunk-LFWPWIAC.cjs.map → chunk-H4W3NCR2.cjs.map} +1 -1
- package/dist/{chunk-DUQQEQ7G.cjs → chunk-IBIGXER5.cjs} +191 -153
- package/dist/chunk-IBIGXER5.cjs.map +1 -0
- package/dist/{chunk-IZ6A6K6N.cjs → chunk-KNNSS3CZ.cjs} +15 -15
- package/dist/{chunk-IZ6A6K6N.cjs.map → chunk-KNNSS3CZ.cjs.map} +1 -1
- package/dist/{chunk-AGV26PUR.js → chunk-LGMRNYTX.js} +3 -3
- package/dist/{chunk-YZJUHEPZ.js → chunk-LYYVTI34.js} +5 -5
- package/dist/{chunk-TAQMYVO6.cjs → chunk-M6XUXQ5Q.cjs} +40 -40
- package/dist/{chunk-TAQMYVO6.cjs.map → chunk-M6XUXQ5Q.cjs.map} +1 -1
- package/dist/{chunk-2BTDPDME.js → chunk-MG4AMIFX.js} +4 -1
- package/dist/chunk-MG4AMIFX.js.map +1 -0
- package/dist/{chunk-ZQYFDYWV.js → chunk-NQODKJ2F.js} +121 -83
- package/dist/{chunk-ZQYFDYWV.js.map → chunk-NQODKJ2F.js.map} +1 -1
- package/dist/{chunk-KUXGQ2FQ.cjs → chunk-OHKKI6EF.cjs} +32 -32
- package/dist/{chunk-KUXGQ2FQ.cjs.map → chunk-OHKKI6EF.cjs.map} +1 -1
- package/dist/{chunk-JOH5D76X.cjs → chunk-PRJFSF4W.cjs} +73 -73
- package/dist/{chunk-JOH5D76X.cjs.map → chunk-PRJFSF4W.cjs.map} +1 -1
- package/dist/{chunk-W3JZXLJ2.js → chunk-PVMPBWKX.js} +6 -6
- package/dist/{chunk-7KNOHGMI.js → chunk-RQVR7I73.js} +2 -2
- package/dist/{chunk-7QP65IE4.cjs → chunk-RYKANQE6.cjs} +5 -5
- package/dist/{chunk-7QP65IE4.cjs.map → chunk-RYKANQE6.cjs.map} +1 -1
- package/dist/{chunk-QAG357PE.js → chunk-UXJ47OJF.js} +2 -2
- package/dist/{chunk-JNC4LVD6.cjs → chunk-VACXEUGF.cjs} +29 -29
- package/dist/{chunk-JNC4LVD6.cjs.map → chunk-VACXEUGF.cjs.map} +1 -1
- package/dist/{chunk-LUKRPHEW.cjs → chunk-YPFQ2UIH.cjs} +73 -73
- package/dist/{chunk-LUKRPHEW.cjs.map → chunk-YPFQ2UIH.cjs.map} +1 -1
- package/dist/{chunk-IPEIOR77.cjs → chunk-ZVAAIP5Y.cjs} +40 -40
- package/dist/{chunk-IPEIOR77.cjs.map → chunk-ZVAAIP5Y.cjs.map} +1 -1
- package/dist/components/case-studies/index.cjs +9 -9
- package/dist/components/case-studies/index.js +3 -3
- package/dist/components/chat/index.cjs +3 -3
- package/dist/components/chat/index.js +2 -2
- package/dist/components/contact/index.cjs +4 -4
- package/dist/components/contact/index.js +3 -3
- package/dist/components/docs/index.cjs +6 -6
- package/dist/components/docs/index.js +5 -5
- package/dist/components/embeds/index.cjs +4 -4
- package/dist/components/embeds/index.js +3 -3
- package/dist/components/faq/index.cjs +5 -5
- package/dist/components/faq/index.js +4 -4
- package/dist/components/features/index.cjs +3 -3
- package/dist/components/features/index.js +2 -2
- package/dist/components/features/policy-configuration-panel.d.ts +11 -0
- package/dist/components/features/policy-configuration-panel.d.ts.map +1 -1
- package/dist/components/help-center-pages/index.cjs +22 -22
- package/dist/components/help-center-pages/index.js +13 -13
- package/dist/components/index.cjs +135 -135
- package/dist/components/index.js +11 -11
- package/dist/components/navigation/index.cjs +3 -3
- package/dist/components/navigation/index.js +2 -2
- package/dist/components/onboarding-guides/index.cjs +7 -7
- package/dist/components/onboarding-guides/index.js +6 -6
- package/dist/components/related-content/index.cjs +5 -5
- package/dist/components/related-content/index.js +4 -4
- package/dist/components/tickets/index.cjs +6 -6
- package/dist/components/tickets/index.js +5 -5
- package/dist/components/ui/index.cjs +3 -3
- package/dist/components/ui/index.js +2 -2
- package/dist/hooks/index.cjs +2 -2
- package/dist/hooks/index.js +1 -1
- package/dist/index.cjs +3 -3
- package/dist/index.js +2 -2
- package/dist/types/permissions.d.ts +9 -6
- package/dist/types/permissions.d.ts.map +1 -1
- package/dist/utils/embed-authed-fetch.d.ts +19 -3
- package/dist/utils/embed-authed-fetch.d.ts.map +1 -1
- package/dist/utils/index.cjs +3 -0
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/features/policy-configuration-panel.tsx +185 -122
- package/src/stories/PolicyConfigurationPanel.stories.tsx +254 -0
- package/src/types/permissions.ts +10 -7
- package/src/utils/__tests__/embed-authed-fetch.test.ts +39 -0
- package/src/utils/embed-authed-fetch.ts +27 -3
- package/dist/chunk-2BTDPDME.js.map +0 -1
- package/dist/chunk-DUQQEQ7G.cjs.map +0 -1
- package/dist/chunk-QO6MCQYE.cjs.map +0 -1
- /package/dist/{chunk-63TSTDME.js.map → chunk-3ZE55IH6.js.map} +0 -0
- /package/dist/{chunk-Y3NFB6HR.js.map → chunk-5U6OZ5FD.js.map} +0 -0
- /package/dist/{chunk-IAZ5AN2E.js.map → chunk-6IPNXMB7.js.map} +0 -0
- /package/dist/{chunk-FPE3SQ5D.js.map → chunk-A7MG4SBL.js.map} +0 -0
- /package/dist/{chunk-SM26C2L7.js.map → chunk-AZQOX2RS.js.map} +0 -0
- /package/dist/{chunk-JYI4R25Q.js.map → chunk-DOVBNECK.js.map} +0 -0
- /package/dist/{chunk-7KDR5P23.js.map → chunk-FQASXKBK.js.map} +0 -0
- /package/dist/{chunk-AGV26PUR.js.map → chunk-LGMRNYTX.js.map} +0 -0
- /package/dist/{chunk-YZJUHEPZ.js.map → chunk-LYYVTI34.js.map} +0 -0
- /package/dist/{chunk-W3JZXLJ2.js.map → chunk-PVMPBWKX.js.map} +0 -0
- /package/dist/{chunk-7KNOHGMI.js.map → chunk-RQVR7I73.js.map} +0 -0
- /package/dist/{chunk-QAG357PE.js.map → chunk-UXJ47OJF.js.map} +0 -0
package/src/types/permissions.ts
CHANGED
|
@@ -11,21 +11,24 @@ export interface PermissionPolicy {
|
|
|
11
11
|
approvalLevel: ApprovalLevel;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Pure data describing one permission category. Presentation state (expansion,
|
|
16
|
+
* bulk-selection display) is owned by PolicyConfigurationPanel itself.
|
|
17
|
+
*/
|
|
14
18
|
export interface PermissionCategory {
|
|
15
19
|
id: string;
|
|
16
20
|
name: string;
|
|
17
|
-
icon
|
|
18
|
-
|
|
19
|
-
globalPermission?: ApprovalLevel;
|
|
20
|
-
isExpanded: boolean;
|
|
21
|
+
/** Optional leading icon; the panel falls back to a shield. */
|
|
22
|
+
icon?: React.ReactNode;
|
|
21
23
|
policies: PermissionPolicy[];
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
export interface PolicyConfigurationPanelProps {
|
|
25
27
|
categories: PermissionCategory[];
|
|
26
28
|
editMode: boolean;
|
|
27
|
-
|
|
28
|
-
onGlobalPermissionChange: (categoryId: string, level: ApprovalLevel | undefined) => void;
|
|
29
|
+
/** Change a single policy's approval level. `policyId` is the policy's `id`. */
|
|
29
30
|
onPolicyPermissionChange: (categoryId: string, policyId: string, level: ApprovalLevel) => void;
|
|
31
|
+
/** Bulk-apply an approval level to every policy in a category. */
|
|
32
|
+
onCategoryPermissionChange: (categoryId: string, level: ApprovalLevel) => void;
|
|
30
33
|
className?: string;
|
|
31
|
-
}
|
|
34
|
+
}
|
|
@@ -166,3 +166,42 @@ describe('embedAuthedFetch 401 self-heal (adapter.refresh)', () => {
|
|
|
166
166
|
expect(fetchSpy).toHaveBeenCalledTimes(1)
|
|
167
167
|
})
|
|
168
168
|
})
|
|
169
|
+
|
|
170
|
+
describe('embedAuthedFetch adapter.allowedOrigins (native-shell cross-origin sanction)', () => {
|
|
171
|
+
const originalFetch = globalThis.fetch
|
|
172
|
+
|
|
173
|
+
beforeEach(() => {
|
|
174
|
+
globalThis.fetch = vi.fn(async () => new Response('ok')) as typeof fetch
|
|
175
|
+
// Production mode so these tests exercise the real guard, not the
|
|
176
|
+
// dev-mode warn-and-allow hatch.
|
|
177
|
+
vi.stubEnv('NODE_ENV', 'production')
|
|
178
|
+
})
|
|
179
|
+
afterEach(() => {
|
|
180
|
+
globalThis.fetch = originalFetch
|
|
181
|
+
setEmbedAuthAdapter(null)
|
|
182
|
+
vi.unstubAllEnvs()
|
|
183
|
+
vi.restoreAllMocks()
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
it('allows a cross-origin URL whose exact origin the adapter sanctions', async () => {
|
|
187
|
+
setEmbedAuthAdapter({ allowedOrigins: ['https://gateway.example'] })
|
|
188
|
+
await expect(embedAuthedFetch('https://gateway.example/content/api/faqs')).resolves.toBeDefined()
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
it('still rejects origins outside the allowlist', () => {
|
|
192
|
+
setEmbedAuthAdapter({ allowedOrigins: ['https://gateway.example'] })
|
|
193
|
+
expect(() => embedAuthedFetch('https://evil.com/x')).toThrow(/cross-origin/i)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it('does not treat an allowlisted origin as a prefix (suffix-domain spoof rejected)', () => {
|
|
197
|
+
setEmbedAuthAdapter({ allowedOrigins: ['https://gateway.example'] })
|
|
198
|
+
expect(() => embedAuthedFetch('https://gateway.example.evil.com/x')).toThrow(/cross-origin/i)
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('cannot allowlist non-http(s) schemes (protocol check runs first)', () => {
|
|
202
|
+
// `javascript:` URLs have origin 'null'; even a (misguided) 'null' entry
|
|
203
|
+
// must not get past the protocol gate.
|
|
204
|
+
setEmbedAuthAdapter({ allowedOrigins: ['null'] })
|
|
205
|
+
expect(() => embedAuthedFetch('javascript:alert(1)')).toThrow(/non-http\(s\)/i)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
@@ -86,6 +86,19 @@ export interface EmbedAuthAdapter {
|
|
|
86
86
|
* host's own auth layer, not to this fetch wrapper.
|
|
87
87
|
*/
|
|
88
88
|
refresh?: () => Promise<boolean>
|
|
89
|
+
/**
|
|
90
|
+
* Exact extra origins (`scheme://host[:port]`, as produced by `URL.origin`)
|
|
91
|
+
* the same-origin guard additionally accepts. The guard normally rejects
|
|
92
|
+
* every cross-origin URL in production — but native-shell hosts serve the
|
|
93
|
+
* page from a local pseudo-origin (`capacitor://localhost`,
|
|
94
|
+
* `tauri://localhost`) with NO server behind it, so the gateway their
|
|
95
|
+
* bearer already belongs to is unavoidably cross-origin. Listing it here
|
|
96
|
+
* is the host's explicit sanction to send the adapter's credentials there.
|
|
97
|
+
* Compared AFTER the http(s)-protocol check, so non-http(s) schemes can
|
|
98
|
+
* never be allowlisted. Omit (or leave empty) everywhere else — same-origin
|
|
99
|
+
* remains the rule, this is the narrow exception.
|
|
100
|
+
*/
|
|
101
|
+
allowedOrigins?: string[]
|
|
89
102
|
}
|
|
90
103
|
|
|
91
104
|
/**
|
|
@@ -158,9 +171,12 @@ export function hasEmbedAuthAdapter(): boolean {
|
|
|
158
171
|
*
|
|
159
172
|
* **Cross-origin defense:** the wrapper assumes a same-origin `/api/…`
|
|
160
173
|
* relative URL. Absolute URLs are accepted only when their origin matches
|
|
161
|
-
* the current window's origin
|
|
162
|
-
*
|
|
163
|
-
*
|
|
174
|
+
* the current window's origin — or appears in the registered adapter's
|
|
175
|
+
* `allowedOrigins` (the native-shell hatch; see that field's doc) — and
|
|
176
|
+
* cross-origin URLs otherwise throw before the bearer leaves the page.
|
|
177
|
+
* This is a defense-in-depth guard for future call sites — outside the
|
|
178
|
+
* allowlisted-shell case there is no legitimate cross-origin use of this
|
|
179
|
+
* fetch wrapper.
|
|
164
180
|
*
|
|
165
181
|
* **401 self-heal:** when a registered adapter supplies `refresh`, a `401`
|
|
166
182
|
* response triggers a single token refresh + retry of the same request
|
|
@@ -352,6 +368,14 @@ function assertSameOrigin(url: string): void {
|
|
|
352
368
|
)
|
|
353
369
|
}
|
|
354
370
|
if (target.origin !== pageOrigin) {
|
|
371
|
+
// Host-sanctioned cross-origin target (`EmbedAuthAdapter.allowedOrigins`).
|
|
372
|
+
// Native shells serve the page from a local pseudo-origin with no server
|
|
373
|
+
// behind it, so their gateway — the same backend the host already trusts
|
|
374
|
+
// with this bearer — is unavoidably cross-origin. Exact `URL.origin`
|
|
375
|
+
// match, and only reachable for http(s) targets (checked above).
|
|
376
|
+
if (getRegisteredAuthAdapter()?.allowedOrigins?.includes(target.origin)) {
|
|
377
|
+
return
|
|
378
|
+
}
|
|
355
379
|
// Dev-mode escape hatch — embedded apps (e.g. openframe-frontend)
|
|
356
380
|
// run on a different origin from their gateway during local dev,
|
|
357
381
|
// and forcing a Next.js `rewrites()` workaround is more error-prone
|