@forge/react
11.9.011.9.1-next.0
out/hooks/usePermissions.jsout/hooks/usePermissions.js+29−30
Index: package/out/hooks/usePermissions.js
===================================================================
--- package/out/hooks/usePermissions.js
+++ package/out/hooks/usePermissions.js
@@ -2,32 +2,35 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.usePermissions = void 0;
const react_1 = require("react");
const bridge_1 = require("@forge/bridge");
-const minimatch_1 = require("minimatch");
+const egress_1 = require("@forge/egress");
/**
* https://ecosystem-platform.atlassian.net/browse/DEPLOY-1411
- * reuse logic from @forge/api
+ * Uses @forge/egress for URL matching (same logic as @forge/api)
*/
/**
+ * Helper function to extract URL string from external URL permissions.
+ * Matches the implementation in @forge/api for consistency.
+ */
+function extractUrlString(url) {
+ if (typeof url === 'string') {
+ return url;
+ }
+ if ('address' in url && url.address) {
+ return url.address;
+ }
+ return url.remote || '';
+}
+/**
* Resource types that can be loaded externally
*/
const RESOURCE_TYPES = ['fonts', 'styles', 'frames', 'images', 'media', 'scripts'];
/**
* Fetch types for external requests
*/
const FETCH_TYPES = ['backend', 'client'];
/**
- * Helper function to check if a URL matches any of the allowed patterns
- * Uses minimatch for robust pattern matching with wildcards
- */
-const matchesAllowedUrl = (url, allowedUrls) => {
- return allowedUrls.some((allowedUrl) => {
- // Use minimatch for pattern matching
- return (0, minimatch_1.minimatch)(url, allowedUrl);
- });
-};
-/**
* Hook for checking permissions in Forge apps
*
* @param requiredPermissions - The permissions required for the component
* @returns Object containing permission state, loading status, and error information
@@ -95,31 +98,27 @@
canFetchFrom: (type, url) => {
const fetchUrls = external.fetch?.[type];
if (!fetchUrls?.length)
return false;
- // Extract string URLs from fetch URLs array
- const allowedUrls = fetchUrls
- .map((item) => {
- // If item is already a string, use it directly
- if (typeof item === 'string') {
- return item;
- }
- // If item has an address property, use that
- if ('address' in item && item.address) {
- return item.address;
- }
- // Otherwise, use the remote property (if it exists)
- return item.remote;
- })
- .filter((url) => typeof url === 'string');
- return matchesAllowedUrl(url, allowedUrls);
+ // Extract URLs and create egress filter
+ const allowList = fetchUrls.map(extractUrlString).filter((u) => u.length > 0);
+ if (allowList.length === 0)
+ return false;
+ const egressFilter = new egress_1.EgressFilteringService(allowList);
+ // Backend: hostname-only matching, Client: CSP validation (includes paths)
+ return type === 'client' ? egressFilter.isValidUrlCSP(url) : egressFilter.isValidUrl(url);
},
canLoadResource: (type, url) => {
const resourceUrls = external[type];
if (!resourceUrls?.length)
return false;
- const stringUrls = resourceUrls.filter((item) => typeof item === 'string');
- return matchesAllowedUrl(url, stringUrls);
+ // Extract URLs and create egress filter
+ const allowList = resourceUrls.map(extractUrlString).filter((u) => u.length > 0);
+ if (allowList.length === 0)
+ return false;
+ const egressFilter = new egress_1.EgressFilteringService(allowList);
+ // All resources use CSP validation (checks protocol + hostname + paths)
+ return egressFilter.isValidUrlCSP(url);
},
getScopes: () => scopeArray,
getExternalPermissions: () => external,
hasAnyPermissions: () => scopeArray.length > 0 || Object.keys(external).length > 0