@forge/lint
6.0.0-next.116.0.0-next.12
out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.js+
out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.jsNew file+162
Index: package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.js
===================================================================
--- package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.js
+++ package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.js
@@ -0,0 +1,162 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.doesAstUsePermissionsSdk = void 0;
+const typescript_estree_1 = require("@typescript-eslint/typescript-estree");
+const FORGE_API_MODULE = '@forge/api';
+const FORGE_BRIDGE_MODULE = '@forge/bridge';
+const FORGE_REACT_MODULE = '@forge/react';
+const PERMISSIONS_SDK_METHODS = new Set(['hasPermission', 'hasScope', 'canFetchFrom', 'canLoadResource']);
+const BRIDGE_SDK_IMPORTS = new Set(['checkPermissions', 'createPermissionUtils']);
+const getImportSource = (node) => {
+ if (node.source.type === typescript_estree_1.AST_NODE_TYPES.Literal && typeof node.source.value === 'string') {
+ return node.source.value;
+ }
+ return null;
+};
+const collectImportBindings = (program) => {
+ const forgeApiPermissions = new Set();
+ const forgeApiNamespaces = new Set();
+ const forgeBridgeSdk = new Set();
+ const forgeBridgeNamespaces = new Set();
+ const forgeReactUsePermissions = new Set();
+ const forgeReactNamespaces = new Set();
+ for (const stmt of program.body) {
+ if (stmt.type !== typescript_estree_1.AST_NODE_TYPES.ImportDeclaration)
+ continue;
+ const source = getImportSource(stmt);
+ if (!source)
+ continue;
+ if (source === FORGE_API_MODULE) {
+ for (const spec of stmt.specifiers) {
+ if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportSpecifier) {
+ if (spec.imported.type === typescript_estree_1.AST_NODE_TYPES.Identifier && spec.imported.name === 'permissions') {
+ forgeApiPermissions.add(spec.local.name);
+ }
+ }
+ else if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
+ forgeApiNamespaces.add(spec.local.name);
+ }
+ }
+ }
+ else if (source === FORGE_BRIDGE_MODULE) {
+ for (const spec of stmt.specifiers) {
+ if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
+ forgeBridgeNamespaces.add(spec.local.name);
+ }
+ else if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportSpecifier && spec.imported.type === typescript_estree_1.AST_NODE_TYPES.Identifier) {
+ if (BRIDGE_SDK_IMPORTS.has(spec.imported.name)) {
+ forgeBridgeSdk.add(spec.local.name);
+ }
+ }
+ }
+ }
+ else if (source === FORGE_REACT_MODULE) {
+ for (const spec of stmt.specifiers) {
+ if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
+ forgeReactNamespaces.add(spec.local.name);
+ }
+ else if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportSpecifier && spec.imported.type === typescript_estree_1.AST_NODE_TYPES.Identifier) {
+ if (spec.imported.name === 'usePermissions') {
+ forgeReactUsePermissions.add(spec.local.name);
+ }
+ }
+ }
+ }
+ }
+ return {
+ forgeApiPermissions,
+ forgeApiNamespaces,
+ forgeBridgeSdk,
+ forgeBridgeNamespaces,
+ forgeReactUsePermissions,
+ forgeReactNamespaces
+ };
+};
+const flattenMemberExpression = (expr) => {
+ const chain = [];
+ let current = expr;
+ while (current.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression) {
+ if (current.property.type !== typescript_estree_1.AST_NODE_TYPES.Identifier) {
+ return undefined;
+ }
+ chain.unshift(current.property.name);
+ current = current.object;
+ }
+ return { root: current, chain };
+};
+const isForgeApiPermissionsCall = (callee, bindings) => {
+ const flat = flattenMemberExpression(callee);
+ if (!flat)
+ return false;
+ const { root, chain } = flat;
+ if (root.type !== typescript_estree_1.AST_NODE_TYPES.Identifier) {
+ return false;
+ }
+ if (chain.length === 1 && PERMISSIONS_SDK_METHODS.has(chain[0])) {
+ return bindings.forgeApiPermissions.has(root.name);
+ }
+ if (chain.length === 2 &&
+ chain[0] === 'permissions' &&
+ PERMISSIONS_SDK_METHODS.has(chain[1]) &&
+ bindings.forgeApiNamespaces.has(root.name)) {
+ return true;
+ }
+ return false;
+};
+const isForgeBridgeSdkCall = (callee, bindings) => {
+ const flat = flattenMemberExpression(callee);
+ if (!flat)
+ return false;
+ const { root, chain } = flat;
+ if (root.type !== typescript_estree_1.AST_NODE_TYPES.Identifier)
+ return false;
+ if (chain.length !== 1 || !BRIDGE_SDK_IMPORTS.has(chain[0]))
+ return false;
+ return bindings.forgeBridgeNamespaces.has(root.name);
+};
+const isForgeReactUsePermissionsCall = (callee, bindings) => {
+ const flat = flattenMemberExpression(callee);
+ if (!flat)
+ return false;
+ const { root, chain } = flat;
+ if (root.type !== typescript_estree_1.AST_NODE_TYPES.Identifier)
+ return false;
+ if (chain.length !== 1 || chain[0] !== 'usePermissions')
+ return false;
+ return bindings.forgeReactNamespaces.has(root.name);
+};
+const doesAstUsePermissionsSdk = (ast) => {
+ const program = ast.parsed;
+ if (!program)
+ return false;
+ const bindings = collectImportBindings(program);
+ let found = false;
+ ast.traverse({
+ enter: (node) => {
+ if (found || node.type !== typescript_estree_1.AST_NODE_TYPES.CallExpression)
+ return;
+ const callee = node.callee;
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.Identifier && bindings.forgeBridgeSdk.has(callee.name)) {
+ found = true;
+ return;
+ }
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.Identifier && bindings.forgeReactUsePermissions.has(callee.name)) {
+ found = true;
+ return;
+ }
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression && isForgeApiPermissionsCall(callee, bindings)) {
+ found = true;
+ return;
+ }
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression && isForgeBridgeSdkCall(callee, bindings)) {
+ found = true;
+ return;
+ }
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression && isForgeReactUsePermissionsCall(callee, bindings)) {
+ found = true;
+ }
+ }
+ });
+ return found;
+};
+exports.doesAstUsePermissionsSdk = doesAstUsePermissionsSdk;