@forge/lint 6.0.0-next.11 → 6.0.0-next.12

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.
Files changed (19) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/out/lint/lint.d.ts.map +1 -1
  3. package/out/lint/lint.js +2 -0
  4. package/out/lint/linters/app-managed-permissions-sdk-linter/app-managed-permissions-sdk-linter.d.ts +10 -0
  5. package/out/lint/linters/app-managed-permissions-sdk-linter/app-managed-permissions-sdk-linter.d.ts.map +1 -0
  6. package/out/lint/linters/app-managed-permissions-sdk-linter/app-managed-permissions-sdk-linter.js +43 -0
  7. package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permission-sensitive-usage.d.ts +3 -0
  8. package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permission-sensitive-usage.d.ts.map +1 -0
  9. package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permission-sensitive-usage.js +32 -0
  10. package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.d.ts +3 -0
  11. package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.d.ts.map +1 -0
  12. package/out/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.js +162 -0
  13. package/out/lint/linters/app-managed-permissions-sdk-linter/manifest-declarative-permission-handling.d.ts +3 -0
  14. package/out/lint/linters/app-managed-permissions-sdk-linter/manifest-declarative-permission-handling.d.ts.map +1 -0
  15. package/out/lint/linters/app-managed-permissions-sdk-linter/manifest-declarative-permission-handling.js +57 -0
  16. package/out/lint/text/messages.d.ts +4 -0
  17. package/out/lint/text/messages.d.ts.map +1 -1
  18. package/out/lint/text/messages.js +4 -0
  19. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @forge/lint
2
2
 
3
+ ## 6.0.0-next.12
4
+
5
+ ### Minor Changes
6
+
7
+ - fbf8219: Adds a lint warning when you use app-managed permissions and the linter still sees API usage that usually needs scopes, but no permission checks in your scanned code or manifest yet.
8
+
3
9
  ## 6.0.0-next.11
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/lint/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AAiBxG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,iBAAiB,GAAI,QAAQ,UAAU,EAAE,aAAa,UAAU,EAAE,EAAE,qBAAkB,KAAG,IA+CrG,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,aAAa,UAAU,EAAE,KAAG,YAQxD,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,UAAU,MAAM,EAChB,QAAQ,OAAO,QAAQ,EACvB,kBAAiB,OAAO,EAAE,CAAC,QAAQ,CAAC,aAAsC,KACzE,OAAO,CAAC,SAAS,CAOnB,CAAC;AAEF,eAAO,MAAM,IAAI,GACf,aAAa,MAAM,EAAE,EACrB,UAAU,QAAQ,EAClB,aAAa,MAAM,EACnB,QAAQ,UAAU,EAClB,gBAAgB,cAAc,EAC9B,2BAlBU,MAAM,UACR,OAAO,QAAQ,oBACN,OAAO,EAAE,CAAC,QAAQ,CAAC,aAAa,KAChD,OAAO,CAAC,SAAS,CAea,EAC/B,UAAS,eAAe,EAevB,KACA,OAAO,CAAC,UAAU,EAAE,CAyCtB,CAAC;AAwBF,eAAO,MAAM,QAAQ,GACnB,QAAQ,UAAU,EAClB,gBAAgB,cAAc,EAC9B,SAAQ,eAAiE,KACxE,OAAO,CAAC,UAAU,EAAE,CAGtB,CAAC"}
1
+ {"version":3,"file":"lint.d.ts","sourceRoot":"","sources":["../../src/lint/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,cAAc,EACf,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,OAAO,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAkB,MAAM,oBAAoB,CAAC;AAkBxG,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,iBAAiB,GAAI,QAAQ,UAAU,EAAE,aAAa,UAAU,EAAE,EAAE,qBAAkB,KAAG,IA+CrG,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,aAAa,UAAU,EAAE,KAAG,YAQxD,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,UAAU,MAAM,EAChB,QAAQ,OAAO,QAAQ,EACvB,kBAAiB,OAAO,EAAE,CAAC,QAAQ,CAAC,aAAsC,KACzE,OAAO,CAAC,SAAS,CAOnB,CAAC;AAEF,eAAO,MAAM,IAAI,GACf,aAAa,MAAM,EAAE,EACrB,UAAU,QAAQ,EAClB,aAAa,MAAM,EACnB,QAAQ,UAAU,EAClB,gBAAgB,cAAc,EAC9B,2BAlBU,MAAM,UACR,OAAO,QAAQ,oBACN,OAAO,EAAE,CAAC,QAAQ,CAAC,aAAa,KAChD,OAAO,CAAC,SAAS,CAea,EAC/B,UAAS,eAAe,EAgBvB,KACA,OAAO,CAAC,UAAU,EAAE,CAyCtB,CAAC;AAwBF,eAAO,MAAM,QAAQ,GACnB,QAAQ,UAAU,EAClB,gBAAgB,cAAc,EAC9B,SAAQ,eAAiE,KACxE,OAAO,CAAC,UAAU,EAAE,CAGtB,CAAC"}
package/out/lint/lint.js CHANGED
@@ -21,6 +21,7 @@ const frame_component_linter_1 = require("./linters/frame-component-linter/frame
21
21
  const deprecated_egress_permissions_manifest_linter_1 = require("./linters/manifest-linter/deprecated-egress-permissions-manifest-linter");
22
22
  const llm_module_linter_1 = require("./linters/llm-module-linter/llm-module-linter");
23
23
  const deprecated_api_module_linter_1 = require("./linters/deprecated-api-module-linter/deprecated-api-module-linter");
24
+ const app_managed_permissions_sdk_linter_1 = require("./linters/app-managed-permissions-sdk-linter/app-managed-permissions-sdk-linter");
24
25
  const function_timeout_linter_1 = require("./linters/function-timeout-linter/function-timeout-linter");
25
26
  const reportLintResults = (logger, lintResults, showSummary = true) => {
26
27
  let numErrors = 0, numWarnings = 0;
@@ -97,6 +98,7 @@ const lint = async (filesToLint, manifest, environment, logger, statsigService,
97
98
  new llm_module_linter_1.LlmModuleLinter(environment, manifest, logger),
98
99
  new deprecated_api_module_linter_1.DeprecatedApiModuleLinter(environment, manifest, logger),
99
100
  new deprecated_egress_permissions_manifest_linter_1.DeprecatedEgressPermissionsManifestLinter(logger, statsigService),
101
+ new app_managed_permissions_sdk_linter_1.AppManagedPermissionsSdkLinter(manifest, logger),
100
102
  new function_timeout_linter_1.FunctionTimeoutLinter(manifest, logger)
101
103
  ]) => {
102
104
  const { include, exclude } = await (0, cli_shared_1.listTSConfigIncludeExclude)(new cli_shared_1.FileSystemReader());
@@ -0,0 +1,10 @@
1
+ import { ManifestSchema as Manifest } from '@forge/manifest';
2
+ import { AbstractLinter } from '../../abstract-linter';
3
+ import { LintInput, LintLogger, LintResult } from '../../linter-interface';
4
+ export declare class AppManagedPermissionsSdkLinter extends AbstractLinter {
5
+ private readonly manifest;
6
+ constructor(manifest: Manifest, logger: LintLogger);
7
+ bootstrap(): Promise<void>;
8
+ batchExecuteImpl(inputs?: LintInput[]): Promise<LintResult[]>;
9
+ }
10
+ //# sourceMappingURL=app-managed-permissions-sdk-linter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-managed-permissions-sdk-linter.d.ts","sourceRoot":"","sources":["../../../../src/lint/linters/app-managed-permissions-sdk-linter/app-managed-permissions-sdk-linter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,IAAI,QAAQ,EAAiB,MAAM,iBAAiB,CAAC;AAE5E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAa,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAOtF,qBAAa,8BAA+B,SAAQ,cAAc;IAE9D,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,QAAQ,EACnC,MAAM,EAAE,UAAU;IAKd,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1B,gBAAgB,CAAC,MAAM,GAAE,SAAS,EAAO,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;CA8BxE"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AppManagedPermissionsSdkLinter = void 0;
4
+ const manifest_1 = require("@forge/manifest");
5
+ const abstract_linter_1 = require("../../abstract-linter");
6
+ const linter_interface_1 = require("../../linter-interface");
7
+ const text_1 = require("../../text");
8
+ const detect_permission_sensitive_usage_1 = require("./detect-permission-sensitive-usage");
9
+ const detect_permissions_sdk_usage_1 = require("./detect-permissions-sdk-usage");
10
+ const manifest_declarative_permission_handling_1 = require("./manifest-declarative-permission-handling");
11
+ class AppManagedPermissionsSdkLinter extends abstract_linter_1.AbstractLinter {
12
+ manifest;
13
+ constructor(manifest, logger) {
14
+ super(logger);
15
+ this.manifest = manifest;
16
+ }
17
+ async bootstrap() {
18
+ }
19
+ async batchExecuteImpl(inputs = []) {
20
+ if (this.manifest.permissions?.enforcement !== 'app-managed') {
21
+ return [];
22
+ }
23
+ const declaresHandling = (0, manifest_declarative_permission_handling_1.manifestDeclaresDeclarativePermissionHandling)(this.manifest) ||
24
+ inputs.some(({ ast }) => ast !== null && (0, detect_permissions_sdk_usage_1.doesAstUsePermissionsSdk)(ast));
25
+ if (declaresHandling) {
26
+ return [];
27
+ }
28
+ const hasPermissionSensitiveUsage = inputs.some(({ ast }) => (0, detect_permission_sensitive_usage_1.doesAstIndicatePermissionSensitiveUsage)(ast));
29
+ if (!hasPermissionSensitiveUsage) {
30
+ return [];
31
+ }
32
+ const lintResult = new linter_interface_1.LintResult(manifest_1.MANIFEST_FILE);
33
+ lintResult.add({
34
+ class: linter_interface_1.LintClass.Warning,
35
+ message: text_1.messages.verifiers.appManagedPermissionsSdk.message(),
36
+ reference: text_1.messages.verifiers.appManagedPermissionsSdk.reference,
37
+ line: 0,
38
+ column: 0
39
+ });
40
+ return [lintResult];
41
+ }
42
+ }
43
+ exports.AppManagedPermissionsSdkLinter = AppManagedPermissionsSdkLinter;
@@ -0,0 +1,3 @@
1
+ import type { ASTParseResult } from '../../../parse';
2
+ export declare const doesAstIndicatePermissionSensitiveUsage: (ast: ASTParseResult | null) => boolean;
3
+ //# sourceMappingURL=detect-permission-sensitive-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-permission-sensitive-usage.d.ts","sourceRoot":"","sources":["../../../../src/lint/linters/app-managed-permissions-sdk-linter/detect-permission-sensitive-usage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAyBrD,eAAO,MAAM,uCAAuC,GAAI,KAAK,cAAc,GAAG,IAAI,KAAG,OAoBpF,CAAC"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doesAstIndicatePermissionSensitiveUsage = void 0;
4
+ const visitors_1 = require("../permission-linter/visitors");
5
+ const PERMISSION_SENSITIVE_VISITORS = [
6
+ new visitors_1.ProductNodeVisitor(),
7
+ new visitors_1.ExternalApiCallVisitor(),
8
+ new visitors_1.StorageAPINodeVisitor(),
9
+ new visitors_1.UIHookNodeVisitor(),
10
+ new visitors_1.NotificationAPINodeVisitor(),
11
+ new visitors_1.ImageUrlVisitor()
12
+ ];
13
+ const doesAstIndicatePermissionSensitiveUsage = (ast) => {
14
+ if (!ast?.parsed) {
15
+ return false;
16
+ }
17
+ let found = false;
18
+ ast.traverse({
19
+ enter: (node, parent) => {
20
+ if (found) {
21
+ return;
22
+ }
23
+ for (const visitor of PERMISSION_SENSITIVE_VISITORS) {
24
+ visitor.visit(node, parent, (_match) => {
25
+ found = true;
26
+ });
27
+ }
28
+ }
29
+ });
30
+ return found;
31
+ };
32
+ exports.doesAstIndicatePermissionSensitiveUsage = doesAstIndicatePermissionSensitiveUsage;
@@ -0,0 +1,3 @@
1
+ import type { ASTParseResult } from '../../../parse';
2
+ export declare const doesAstUsePermissionsSdk: (ast: ASTParseResult) => boolean;
3
+ //# sourceMappingURL=detect-permissions-sdk-usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect-permissions-sdk-usage.d.ts","sourceRoot":"","sources":["../../../../src/lint/linters/app-managed-permissions-sdk-linter/detect-permissions-sdk-usage.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AA4JrD,eAAO,MAAM,wBAAwB,GAAI,KAAK,cAAc,KAAG,OAwC9D,CAAC"}
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doesAstUsePermissionsSdk = void 0;
4
+ const typescript_estree_1 = require("@typescript-eslint/typescript-estree");
5
+ const FORGE_API_MODULE = '@forge/api';
6
+ const FORGE_BRIDGE_MODULE = '@forge/bridge';
7
+ const FORGE_REACT_MODULE = '@forge/react';
8
+ const PERMISSIONS_SDK_METHODS = new Set(['hasPermission', 'hasScope', 'canFetchFrom', 'canLoadResource']);
9
+ const BRIDGE_SDK_IMPORTS = new Set(['checkPermissions', 'createPermissionUtils']);
10
+ const getImportSource = (node) => {
11
+ if (node.source.type === typescript_estree_1.AST_NODE_TYPES.Literal && typeof node.source.value === 'string') {
12
+ return node.source.value;
13
+ }
14
+ return null;
15
+ };
16
+ const collectImportBindings = (program) => {
17
+ const forgeApiPermissions = new Set();
18
+ const forgeApiNamespaces = new Set();
19
+ const forgeBridgeSdk = new Set();
20
+ const forgeBridgeNamespaces = new Set();
21
+ const forgeReactUsePermissions = new Set();
22
+ const forgeReactNamespaces = new Set();
23
+ for (const stmt of program.body) {
24
+ if (stmt.type !== typescript_estree_1.AST_NODE_TYPES.ImportDeclaration)
25
+ continue;
26
+ const source = getImportSource(stmt);
27
+ if (!source)
28
+ continue;
29
+ if (source === FORGE_API_MODULE) {
30
+ for (const spec of stmt.specifiers) {
31
+ if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportSpecifier) {
32
+ if (spec.imported.type === typescript_estree_1.AST_NODE_TYPES.Identifier && spec.imported.name === 'permissions') {
33
+ forgeApiPermissions.add(spec.local.name);
34
+ }
35
+ }
36
+ else if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
37
+ forgeApiNamespaces.add(spec.local.name);
38
+ }
39
+ }
40
+ }
41
+ else if (source === FORGE_BRIDGE_MODULE) {
42
+ for (const spec of stmt.specifiers) {
43
+ if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
44
+ forgeBridgeNamespaces.add(spec.local.name);
45
+ }
46
+ else if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportSpecifier && spec.imported.type === typescript_estree_1.AST_NODE_TYPES.Identifier) {
47
+ if (BRIDGE_SDK_IMPORTS.has(spec.imported.name)) {
48
+ forgeBridgeSdk.add(spec.local.name);
49
+ }
50
+ }
51
+ }
52
+ }
53
+ else if (source === FORGE_REACT_MODULE) {
54
+ for (const spec of stmt.specifiers) {
55
+ if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportNamespaceSpecifier) {
56
+ forgeReactNamespaces.add(spec.local.name);
57
+ }
58
+ else if (spec.type === typescript_estree_1.AST_NODE_TYPES.ImportSpecifier && spec.imported.type === typescript_estree_1.AST_NODE_TYPES.Identifier) {
59
+ if (spec.imported.name === 'usePermissions') {
60
+ forgeReactUsePermissions.add(spec.local.name);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+ return {
67
+ forgeApiPermissions,
68
+ forgeApiNamespaces,
69
+ forgeBridgeSdk,
70
+ forgeBridgeNamespaces,
71
+ forgeReactUsePermissions,
72
+ forgeReactNamespaces
73
+ };
74
+ };
75
+ const flattenMemberExpression = (expr) => {
76
+ const chain = [];
77
+ let current = expr;
78
+ while (current.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression) {
79
+ if (current.property.type !== typescript_estree_1.AST_NODE_TYPES.Identifier) {
80
+ return undefined;
81
+ }
82
+ chain.unshift(current.property.name);
83
+ current = current.object;
84
+ }
85
+ return { root: current, chain };
86
+ };
87
+ const isForgeApiPermissionsCall = (callee, bindings) => {
88
+ const flat = flattenMemberExpression(callee);
89
+ if (!flat)
90
+ return false;
91
+ const { root, chain } = flat;
92
+ if (root.type !== typescript_estree_1.AST_NODE_TYPES.Identifier) {
93
+ return false;
94
+ }
95
+ if (chain.length === 1 && PERMISSIONS_SDK_METHODS.has(chain[0])) {
96
+ return bindings.forgeApiPermissions.has(root.name);
97
+ }
98
+ if (chain.length === 2 &&
99
+ chain[0] === 'permissions' &&
100
+ PERMISSIONS_SDK_METHODS.has(chain[1]) &&
101
+ bindings.forgeApiNamespaces.has(root.name)) {
102
+ return true;
103
+ }
104
+ return false;
105
+ };
106
+ const isForgeBridgeSdkCall = (callee, bindings) => {
107
+ const flat = flattenMemberExpression(callee);
108
+ if (!flat)
109
+ return false;
110
+ const { root, chain } = flat;
111
+ if (root.type !== typescript_estree_1.AST_NODE_TYPES.Identifier)
112
+ return false;
113
+ if (chain.length !== 1 || !BRIDGE_SDK_IMPORTS.has(chain[0]))
114
+ return false;
115
+ return bindings.forgeBridgeNamespaces.has(root.name);
116
+ };
117
+ const isForgeReactUsePermissionsCall = (callee, bindings) => {
118
+ const flat = flattenMemberExpression(callee);
119
+ if (!flat)
120
+ return false;
121
+ const { root, chain } = flat;
122
+ if (root.type !== typescript_estree_1.AST_NODE_TYPES.Identifier)
123
+ return false;
124
+ if (chain.length !== 1 || chain[0] !== 'usePermissions')
125
+ return false;
126
+ return bindings.forgeReactNamespaces.has(root.name);
127
+ };
128
+ const doesAstUsePermissionsSdk = (ast) => {
129
+ const program = ast.parsed;
130
+ if (!program)
131
+ return false;
132
+ const bindings = collectImportBindings(program);
133
+ let found = false;
134
+ ast.traverse({
135
+ enter: (node) => {
136
+ if (found || node.type !== typescript_estree_1.AST_NODE_TYPES.CallExpression)
137
+ return;
138
+ const callee = node.callee;
139
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.Identifier && bindings.forgeBridgeSdk.has(callee.name)) {
140
+ found = true;
141
+ return;
142
+ }
143
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.Identifier && bindings.forgeReactUsePermissions.has(callee.name)) {
144
+ found = true;
145
+ return;
146
+ }
147
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression && isForgeApiPermissionsCall(callee, bindings)) {
148
+ found = true;
149
+ return;
150
+ }
151
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression && isForgeBridgeSdkCall(callee, bindings)) {
152
+ found = true;
153
+ return;
154
+ }
155
+ if (callee.type === typescript_estree_1.AST_NODE_TYPES.MemberExpression && isForgeReactUsePermissionsCall(callee, bindings)) {
156
+ found = true;
157
+ }
158
+ }
159
+ });
160
+ return found;
161
+ };
162
+ exports.doesAstUsePermissionsSdk = doesAstUsePermissionsSdk;
@@ -0,0 +1,3 @@
1
+ import type { ManifestSchema as Manifest } from '@forge/manifest';
2
+ export declare const manifestDeclaresDeclarativePermissionHandling: (manifest: Manifest) => boolean;
3
+ //# sourceMappingURL=manifest-declarative-permission-handling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-declarative-permission-handling.d.ts","sourceRoot":"","sources":["../../../../src/lint/linters/app-managed-permissions-sdk-linter/manifest-declarative-permission-handling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,IAAI,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAiDlE,eAAO,MAAM,6CAA6C,GAAI,UAAU,QAAQ,KAAG,OAuBlF,CAAC"}
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.manifestDeclaresDeclarativePermissionHandling = void 0;
4
+ const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
5
+ const moduleEntryUsesPermissionDisplayConditions = (entry) => {
6
+ if (!isRecord(entry))
7
+ return false;
8
+ const displayConditions = entry.displayConditions;
9
+ if (!isRecord(displayConditions))
10
+ return false;
11
+ return Object.prototype.hasOwnProperty.call(displayConditions, 'permissions');
12
+ };
13
+ const expressionReferencesEventPermissions = (expression) => typeof expression === 'string' && expression.includes('event.permissions');
14
+ const triggerEntryUsesEventPermissionsFilter = (entry) => {
15
+ if (!isRecord(entry))
16
+ return false;
17
+ const topFilter = entry.filter;
18
+ if (isRecord(topFilter) && expressionReferencesEventPermissions(topFilter.expression)) {
19
+ return true;
20
+ }
21
+ const events = entry.events;
22
+ if (!Array.isArray(events))
23
+ return false;
24
+ for (const ev of events) {
25
+ if (!isRecord(ev))
26
+ continue;
27
+ const filter = ev.filter;
28
+ if (isRecord(filter) && expressionReferencesEventPermissions(filter.expression)) {
29
+ return true;
30
+ }
31
+ }
32
+ return false;
33
+ };
34
+ const manifestDeclaresDeclarativePermissionHandling = (manifest) => {
35
+ const modules = manifest.modules;
36
+ if (!isRecord(modules))
37
+ return false;
38
+ for (const value of Object.values(modules)) {
39
+ if (!Array.isArray(value))
40
+ continue;
41
+ for (const entry of value) {
42
+ if (moduleEntryUsesPermissionDisplayConditions(entry)) {
43
+ return true;
44
+ }
45
+ }
46
+ }
47
+ const triggers = modules.trigger;
48
+ if (Array.isArray(triggers)) {
49
+ for (const entry of triggers) {
50
+ if (triggerEntryUsesEventPermissionsFilter(entry)) {
51
+ return true;
52
+ }
53
+ }
54
+ }
55
+ return false;
56
+ };
57
+ exports.manifestDeclaresDeclarativePermissionHandling = manifestDeclaresDeclarativePermissionHandling;
@@ -69,6 +69,10 @@ export declare const messages: {
69
69
  message: () => string;
70
70
  reference: string;
71
71
  };
72
+ appManagedPermissionsSdk: {
73
+ message: () => string;
74
+ reference: string;
75
+ };
72
76
  functionTimeout: {
73
77
  shared: {
74
78
  message: (functionKey: string, conflictingModuleKey: string, conflictingModuleType: string) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/lint/text/messages.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;2BAGA,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;2BAKf,MAAM;2BAER,MAAM;;;;2BAKJ,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;iCAKT,MAAM;;;;iCAKN,MAAM;;;;8BAKT,MAAM,OAAO,MAAM;;;;+BAKlB,MAAM,UAAU,MAAM,QAAQ,MAAM,GAAG,SAAS,SAAS,MAAM;;;;6BAKjE,MAAM;;;;+BAIJ,MAAM,QAAQ,MAAM,SAAS,MAAM;;;;8BAKpC,MAAM;;;;8BAIN,MAAM;;;;8BAIN,MAAM;;;;;;;;;oCASF,MAAM;;;;;6BAOX,MAAM,WAAW,MAAM;;;;;;;;;uCAWf,MAAM,wBAAwB,MAAM,yBAAyB,MAAM;;;;uCAKnE,MAAM,kBAAkB,MAAM,aAAa,MAAM,cAAc,MAAM;;;;;CAMnG,CAAC"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/lint/text/messages.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,QAAQ;;;2BAGA,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;2BAKf,MAAM;2BAER,MAAM;;;;2BAKJ,MAAM;2BAEN,MAAM,KAAG,MAAM;;;;iCAKT,MAAM;;;;iCAKN,MAAM;;;;8BAKT,MAAM,OAAO,MAAM;;;;+BAKlB,MAAM,UAAU,MAAM,QAAQ,MAAM,GAAG,SAAS,SAAS,MAAM;;;;6BAKjE,MAAM;;;;+BAIJ,MAAM,QAAQ,MAAM,SAAS,MAAM;;;;8BAKpC,MAAM;;;;8BAIN,MAAM;;;;8BAIN,MAAM;;;;;;;;;oCASF,MAAM;;;;;6BAOX,MAAM,WAAW,MAAM;;;;;;;;;;;;;uCAgBf,MAAM,wBAAwB,MAAM,yBAAyB,MAAM;;;;uCAKnE,MAAM,kBAAkB,MAAM,aAAa,MAAM,cAAc,MAAM;;;;;CAMnG,CAAC"}
@@ -75,6 +75,10 @@ exports.messages = {
75
75
  message: () => `The \"storage\" export from \"@forge/api\" is deprecated. Use the \"@forge/kvs\" package instead.`,
76
76
  reference: 'deprecated-api-storage'
77
77
  },
78
+ appManagedPermissionsSdk: {
79
+ message: () => 'App-managed permissions enforcement is set in the manifest. Source files include API usage that requires OAuth scopes (such as requestJira or external fetch), without checks for when those scopes are missing. Use the Permissions SDK (@forge/api, @forge/bridge, or @forge/react), displayConditions.permissions, or a trigger filter that references event.permissions.',
80
+ reference: 'https://go.atlassian.com/forge-permissions-sdk'
81
+ },
78
82
  functionTimeout: {
79
83
  shared: {
80
84
  message: (functionKey, conflictingModuleKey, conflictingModuleType) => `Function "${functionKey}" is used by a consumer and ${conflictingModuleType} "${conflictingModuleKey}". Consumer functions must not be shared with other modules. Use a separate function for each module type.`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/lint",
3
- "version": "6.0.0-next.11",
3
+ "version": "6.0.0-next.12",
4
4
  "description": "Linting for forge apps",
5
5
  "main": "out/index.js",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",