@j-schreiber/sf-cli-security-audit 0.4.1 → 0.6.0

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 (69) hide show
  1. package/README.md +20 -5
  2. package/lib/commands/org/audit/init.d.ts +2 -0
  3. package/lib/commands/org/audit/init.js +10 -0
  4. package/lib/commands/org/audit/init.js.map +1 -1
  5. package/lib/commands/org/scan/user-perms.d.ts +20 -0
  6. package/lib/commands/org/scan/user-perms.js +88 -0
  7. package/lib/commands/org/scan/user-perms.js.map +1 -0
  8. package/lib/libs/conf-init/auditConfig.d.ts +8 -0
  9. package/lib/libs/conf-init/auditConfig.js +3 -2
  10. package/lib/libs/conf-init/auditConfig.js.map +1 -1
  11. package/lib/libs/conf-init/permissionsClassification.d.ts +3 -2
  12. package/lib/libs/conf-init/permissionsClassification.js +37 -27
  13. package/lib/libs/conf-init/permissionsClassification.js.map +1 -1
  14. package/lib/libs/conf-init/presets/loose.d.ts +6 -0
  15. package/lib/libs/conf-init/presets/loose.js +35 -0
  16. package/lib/libs/conf-init/presets/loose.js.map +1 -0
  17. package/lib/libs/conf-init/presets/none.d.ts +30 -0
  18. package/lib/libs/conf-init/presets/none.js +54 -0
  19. package/lib/libs/conf-init/presets/none.js.map +1 -0
  20. package/lib/libs/conf-init/presets/strict.d.ts +4 -0
  21. package/lib/libs/conf-init/presets/strict.js +28 -0
  22. package/lib/libs/conf-init/presets/strict.js.map +1 -0
  23. package/lib/libs/conf-init/presets.d.ts +7 -0
  24. package/lib/libs/conf-init/presets.js +20 -0
  25. package/lib/libs/conf-init/presets.js.map +1 -0
  26. package/lib/libs/core/classification-types.d.ts +1 -1
  27. package/lib/libs/core/classification-types.js +1 -1
  28. package/lib/libs/core/classification-types.js.map +1 -1
  29. package/lib/libs/core/constants.d.ts +1 -0
  30. package/lib/libs/core/constants.js +4 -0
  31. package/lib/libs/core/constants.js.map +1 -1
  32. package/lib/libs/core/file-mgmt/auditConfigFileManager.d.ts +1 -0
  33. package/lib/libs/core/file-mgmt/auditConfigFileManager.js +49 -4
  34. package/lib/libs/core/file-mgmt/auditConfigFileManager.js.map +1 -1
  35. package/lib/libs/core/mdapi/mdapiRetriever.d.ts +12 -68
  36. package/lib/libs/core/mdapi/mdapiRetriever.js +20 -90
  37. package/lib/libs/core/mdapi/mdapiRetriever.js.map +1 -1
  38. package/lib/libs/core/mdapi/metadataRegistryEntry.d.ts +40 -0
  39. package/lib/libs/core/mdapi/metadataRegistryEntry.js +46 -0
  40. package/lib/libs/core/mdapi/metadataRegistryEntry.js.map +1 -0
  41. package/lib/libs/core/mdapi/namedMetadataToolingQueryable.d.ts +33 -0
  42. package/lib/libs/core/mdapi/namedMetadataToolingQueryable.js +41 -0
  43. package/lib/libs/core/mdapi/namedMetadataToolingQueryable.js.map +1 -0
  44. package/lib/libs/core/mdapi/namedMetadataType.d.ts +20 -0
  45. package/lib/libs/core/mdapi/namedMetadataType.js +41 -0
  46. package/lib/libs/core/mdapi/namedMetadataType.js.map +1 -0
  47. package/lib/libs/core/mdapi/singletonMetadataType.d.ts +21 -0
  48. package/lib/libs/core/mdapi/singletonMetadataType.js +37 -0
  49. package/lib/libs/core/mdapi/singletonMetadataType.js.map +1 -0
  50. package/lib/libs/core/utils.d.ts +2 -0
  51. package/lib/libs/core/utils.js +6 -0
  52. package/lib/libs/core/utils.js.map +1 -1
  53. package/lib/libs/policies/profilePolicy.js +21 -28
  54. package/lib/libs/policies/profilePolicy.js.map +1 -1
  55. package/lib/libs/quick-scan/types.d.ts +17 -0
  56. package/lib/libs/quick-scan/types.js +2 -0
  57. package/lib/libs/quick-scan/types.js.map +1 -0
  58. package/lib/libs/quick-scan/userPermissionScanner.d.ts +22 -0
  59. package/lib/libs/quick-scan/userPermissionScanner.js +75 -0
  60. package/lib/libs/quick-scan/userPermissionScanner.js.map +1 -0
  61. package/messages/org.audit.init.md +12 -0
  62. package/messages/org.audit.run.md +12 -0
  63. package/messages/org.scan.user-perms.md +31 -0
  64. package/messages/policyclassifications.md +38 -2
  65. package/oclif.manifest.json +96 -2
  66. package/package.json +1 -1
  67. package/lib/libs/conf-init/defaultPolicyClassification.d.ts +0 -2
  68. package/lib/libs/conf-init/defaultPolicyClassification.js +0 -63
  69. package/lib/libs/conf-init/defaultPolicyClassification.js.map +0 -1
@@ -0,0 +1,41 @@
1
+ import { ComponentSet } from '@salesforce/source-deploy-retrieve';
2
+ import MetadataRegistryEntry, { cleanRetrieveDir, retrieve, } from './metadataRegistryEntry.js';
3
+ /**
4
+ * The entry is a typical named metadata that is organized in a dedicated source folder
5
+ * where all entities have the same format. The components are retrieved and organized
6
+ * by their developer name.
7
+ */
8
+ export default class NamedMetadata extends MetadataRegistryEntry {
9
+ constructor(opts) {
10
+ super(opts);
11
+ }
12
+ /**
13
+ * Resolves component names, retrieves the metadata and returns
14
+ * as a strongly typed result.
15
+ *
16
+ * @param con
17
+ * @param componentNames
18
+ * @returns
19
+ */
20
+ async resolve(con, componentNames) {
21
+ const cmpSet = new ComponentSet(componentNames.map((cname) => ({ type: this.retrieveType, fullName: cname })));
22
+ const retrieveResult = await retrieve(cmpSet, con);
23
+ const resolvedFiles = this.parseSourceFiles(retrieveResult.components, componentNames);
24
+ cleanRetrieveDir(retrieveResult.getFileResponses());
25
+ return resolvedFiles;
26
+ }
27
+ parseSourceFiles(componentSet, retrievedNames) {
28
+ const cmps = componentSet.getSourceComponents().toArray();
29
+ const result = {};
30
+ cmps.forEach((sourceComponent) => {
31
+ if (sourceComponent.xml && retrievedNames.includes(sourceComponent.name)) {
32
+ // the available method parseXmlSync on source component does not
33
+ // resolve the "rootNodeProblem" from XML. Therefore, we implement
34
+ // our own method to parse and return the "inner xml".
35
+ result[sourceComponent.name] = this.parse(sourceComponent.xml);
36
+ }
37
+ });
38
+ return result;
39
+ }
40
+ }
41
+ //# sourceMappingURL=namedMetadataType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"namedMetadataType.js","sourceRoot":"","sources":["../../../../src/libs/core/mdapi/namedMetadataType.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,qBAAqB,EAAE,EAC5B,gBAAgB,EAEhB,QAAQ,GACT,MAAM,4BAA4B,CAAC;AAEpC;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,aAA4C,SAAQ,qBAAgC;IACvG,YAAmB,IAA0C;QAC3D,KAAK,CAAC,IAAI,CAAC,CAAC;IACd,CAAC;IACD;;;;;;;OAOG;IACI,KAAK,CAAC,OAAO,CAAC,GAAe,EAAE,cAAwB;QAC5D,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;QAC/G,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACnD,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QACvF,gBAAgB,CAAC,cAAc,CAAC,gBAAgB,EAAE,CAAC,CAAC;QACpD,OAAO,aAAa,CAAC;IACvB,CAAC;IAEO,gBAAgB,CAAC,YAA0B,EAAE,cAAwB;QAC3E,MAAM,IAAI,GAAG,YAAY,CAAC,mBAAmB,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1D,MAAM,MAAM,GAA8B,EAAE,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,CAAC,eAAe,EAAE,EAAE;YAC/B,IAAI,eAAe,CAAC,GAAG,IAAI,cAAc,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzE,iEAAiE;gBACjE,kEAAkE;gBAClE,sDAAsD;gBACtD,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;YACjE,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,21 @@
1
+ import { Connection } from '@salesforce/core';
2
+ import MetadataRegistryEntry, { MetadataRegistryEntryOpts } from './metadataRegistryEntry.js';
3
+ /**
4
+ * The entry is a type that only has one single instance on the org, such as
5
+ * a Setting. The component is typically retrieved by a more generic name and
6
+ * organized & cached by the explicit name.
7
+ */
8
+ export default class SingletonMetadata<Type, Key extends keyof Type> extends MetadataRegistryEntry<Type, Key> {
9
+ retrieveName: string;
10
+ constructor(opts: MetadataRegistryEntryOpts<Type, Key>);
11
+ /**
12
+ * Resolves component names, retrieves the metadata and returns
13
+ * as a strongly typed result.
14
+ *
15
+ * @param con
16
+ * @param componentNames
17
+ * @returns
18
+ */
19
+ resolve(con: Connection): Promise<Type[Key]>;
20
+ private parseSourceFile;
21
+ }
@@ -0,0 +1,37 @@
1
+ import { ComponentSet } from '@salesforce/source-deploy-retrieve';
2
+ import MetadataRegistryEntry, { cleanRetrieveDir, retrieve, } from './metadataRegistryEntry.js';
3
+ /**
4
+ * The entry is a type that only has one single instance on the org, such as
5
+ * a Setting. The component is typically retrieved by a more generic name and
6
+ * organized & cached by the explicit name.
7
+ */
8
+ export default class SingletonMetadata extends MetadataRegistryEntry {
9
+ retrieveName;
10
+ constructor(opts) {
11
+ super(opts);
12
+ this.retrieveName = opts.retrieveName ?? String(this.rootNodeName);
13
+ }
14
+ /**
15
+ * Resolves component names, retrieves the metadata and returns
16
+ * as a strongly typed result.
17
+ *
18
+ * @param con
19
+ * @param componentNames
20
+ * @returns
21
+ */
22
+ async resolve(con) {
23
+ const cmpSet = new ComponentSet([{ type: this.retrieveType, fullName: this.retrieveName }]);
24
+ const retrieveResult = await retrieve(cmpSet, con);
25
+ const resolvedCmp = this.parseSourceFile(retrieveResult.components);
26
+ cleanRetrieveDir(retrieveResult.getFileResponses());
27
+ return resolvedCmp;
28
+ }
29
+ parseSourceFile(componentSet) {
30
+ const cmps = componentSet.getSourceComponents({ type: this.retrieveType, fullName: this.retrieveName }).toArray();
31
+ if (cmps.length > 0 && cmps[0].xml) {
32
+ return this.parse(cmps[0].xml);
33
+ }
34
+ throw new Error('Failed to resolve settings for: ' + this.retrieveName);
35
+ }
36
+ }
37
+ //# sourceMappingURL=singletonMetadataType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"singletonMetadataType.js","sourceRoot":"","sources":["../../../../src/libs/core/mdapi/singletonMetadataType.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAClE,OAAO,qBAAqB,EAAE,EAC5B,gBAAgB,EAEhB,QAAQ,GACT,MAAM,4BAA4B,CAAC;AAEpC;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAgD,SAAQ,qBAAgC;IACpG,YAAY,CAAS;IAC5B,YAAmB,IAA0C;QAC3D,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,IAAI,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACrE,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,OAAO,CAAC,GAAe;QAClC,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC;QAC5F,MAAM,cAAc,GAAG,MAAM,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACnD,MAAM,WAAW,GAAG,IAAI,CAAC,eAAe,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;QACpE,gBAAgB,CAAC,cAAc,CAAC,gBAAgB,EAAE,CAAC,CAAC;QACpD,OAAO,WAAW,CAAC;IACrB,CAAC;IAEO,eAAe,CAAC,YAA0B;QAChD,MAAM,IAAI,GAAG,YAAY,CAAC,mBAAmB,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QAClH,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;IAC1E,CAAC;CACF"}
@@ -1,3 +1,5 @@
1
1
  export declare function isEmpty(anything?: unknown): boolean;
2
2
  export declare function isNullish(anything: unknown): boolean;
3
+ export declare function capitalize(anyString: string): string;
4
+ export declare function uncapitalize(anyString: string): string;
3
5
  export type Optional<T, K extends keyof T> = Pick<Partial<T>, K> & Omit<T, K>;
@@ -10,4 +10,10 @@ export function isEmpty(anything) {
10
10
  export function isNullish(anything) {
11
11
  return !(Boolean(anything) && anything !== null);
12
12
  }
13
+ export function capitalize(anyString) {
14
+ return `${anyString[0].toUpperCase()}${anyString.slice(1)}`;
15
+ }
16
+ export function uncapitalize(anyString) {
17
+ return `${anyString[0].toLowerCase()}${anyString.slice(1)}`;
18
+ }
13
19
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/libs/core/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,OAAO,CAAC,QAAkB;IACxC,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,OAAO,CAAC,QAAS,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAiB;IACzC,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,KAAK,IAAI,CAAC,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../src/libs/core/utils.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,OAAO,CAAC,QAAkB;IACxC,IAAI,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,OAAO,CAAC,QAAS,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,QAAiB;IACzC,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,QAAQ,KAAK,IAAI,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,OAAO,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { Messages } from '@salesforce/core';
2
- import { isNullish } from '../core/utils.js';
2
+ import MDAPI from '../core/mdapi/mdapiRetriever.js';
3
3
  import { RuleRegistries } from '../core/registries/types.js';
4
4
  import { ProfilesRiskPreset } from '../core/policy-types.js';
5
5
  import Policy, { getTotal } from './policy.js';
@@ -22,42 +22,35 @@ export default class ProfilePolicy extends Policy {
22
22
  });
23
23
  const successfullyResolved = {};
24
24
  const ignoredEntities = {};
25
- const profileQueryResults = Array();
26
25
  const definitiveProfiles = this.config.profiles ?? {};
26
+ const classifiedProfiles = [];
27
27
  Object.entries(definitiveProfiles).forEach(([profileName, profileDef]) => {
28
- if (profileDef.preset !== ProfilesRiskPreset.UNKNOWN) {
29
- const qr = Promise.resolve(context.targetOrgConnection.tooling.query(`SELECT Name,Metadata FROM Profile WHERE Name = '${profileName}'`));
30
- profileQueryResults.push(qr);
31
- }
32
- else {
28
+ if (profileDef.preset === ProfilesRiskPreset.UNKNOWN) {
33
29
  ignoredEntities[profileName] = {
34
30
  name: profileName,
35
31
  message: messages.getMessage('preset-unknown', ['Profile']),
36
32
  };
37
33
  }
38
- });
39
- const queryResults = await Promise.all(profileQueryResults);
40
- queryResults.forEach((qr) => {
41
- if (qr.records && qr.records.length > 0) {
42
- const record = qr.records[0];
43
- if (isNullish(record.Metadata)) {
44
- ignoredEntities[record.Name] = {
45
- name: record.Name,
46
- message: messages.getMessage('profile-invalid-no-metadata'),
47
- };
48
- }
49
- else {
50
- successfullyResolved[record.Name] = {
51
- name: record.Name,
52
- preset: definitiveProfiles[record.Name].preset,
53
- metadata: record.Metadata,
54
- };
55
- }
34
+ else {
35
+ classifiedProfiles.push(profileName);
56
36
  }
57
37
  });
58
- Object.keys(definitiveProfiles).forEach((profileName) => {
59
- if (successfullyResolved[profileName] === undefined && ignoredEntities[profileName] === undefined) {
60
- ignoredEntities[profileName] = { name: profileName, message: messages.getMessage('entity-not-found') };
38
+ const mdapi = new MDAPI(context.targetOrgConnection);
39
+ const resolvedProfiles = await mdapi.resolve('Profile', classifiedProfiles);
40
+ classifiedProfiles.forEach((profileName) => {
41
+ const resolvedProfile = resolvedProfiles[profileName];
42
+ if (!resolvedProfile) {
43
+ ignoredEntities[profileName] = {
44
+ name: profileName,
45
+ message: messages.getMessage('entity-not-found'),
46
+ };
47
+ }
48
+ else {
49
+ successfullyResolved[profileName] = {
50
+ name: profileName,
51
+ preset: definitiveProfiles[profileName].preset,
52
+ metadata: resolvedProfile,
53
+ };
61
54
  }
62
55
  });
63
56
  const result = { resolvedEntities: successfullyResolved, ignoredEntities: Object.values(ignoredEntities) };
@@ -1 +1 @@
1
- {"version":3,"file":"profilePolicy.js","sourceRoot":"","sources":["../../../src/libs/policies/profilePolicy.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"profilePolicy.js","sourceRoot":"","sources":["../../../src/libs/policies/profilePolicy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { Connection } from '@salesforce/core';
2
+ export type QuickScanResult = {
3
+ permissions: QuickScanPermissionResult;
4
+ scannedProfiles: string[];
5
+ scannedPermissionSets: string[];
6
+ };
7
+ export type QuickScanPermissionResult = {
8
+ [permissionName: string]: PermissionScanResult;
9
+ };
10
+ export type PermissionScanResult = {
11
+ profiles: string[];
12
+ permissionSets: string[];
13
+ };
14
+ export type QuickScanOptions = {
15
+ targetOrg: Connection;
16
+ permissions: string[];
17
+ };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/libs/quick-scan/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { QuickScanOptions, QuickScanResult } from './types.js';
3
+ export type ScanStatusEvent = {
4
+ profiles: EntityScanStatus;
5
+ permissionSets: EntityScanStatus;
6
+ users: EntityScanStatus;
7
+ status: 'Pending' | 'In Progress' | 'Completed';
8
+ };
9
+ export type EntityScanStatus = {
10
+ total?: number;
11
+ resolved?: number;
12
+ status?: string;
13
+ };
14
+ export default class UserPermissionScanner extends EventEmitter {
15
+ private status;
16
+ constructor();
17
+ quickScan(opts: QuickScanOptions): Promise<QuickScanResult>;
18
+ private resolveEntities;
19
+ private resolveProfiles;
20
+ private resolvePermissionSets;
21
+ private emitProgress;
22
+ }
@@ -0,0 +1,75 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import MDAPI from '../core/mdapi/mdapiRetriever.js';
3
+ import { PERMISSION_SETS_QUERY, PROFILES_QUERY } from '../core/constants.js';
4
+ export default class UserPermissionScanner extends EventEmitter {
5
+ status = {
6
+ profiles: {},
7
+ permissionSets: {},
8
+ users: {},
9
+ status: 'Pending',
10
+ };
11
+ constructor() {
12
+ super();
13
+ }
14
+ async quickScan(opts) {
15
+ this.emitProgress({ status: 'Pending' });
16
+ const scannedEntities = await this.resolveEntities(opts.targetOrg);
17
+ const scanResult = {
18
+ permissions: {},
19
+ scannedProfiles: Object.keys(scannedEntities.profiles),
20
+ scannedPermissionSets: Object.keys(scannedEntities.permissionSets),
21
+ };
22
+ opts.permissions.forEach((permName) => {
23
+ const profiles = findGrantingEntities(permName, scannedEntities.profiles);
24
+ const permissionSets = findGrantingEntities(permName, scannedEntities.permissionSets);
25
+ scanResult.permissions[permName] = { permissionSets, profiles };
26
+ });
27
+ this.emitProgress({ status: 'Completed' });
28
+ return scanResult;
29
+ }
30
+ async resolveEntities(targetOrg) {
31
+ const promises = [];
32
+ this.emitProgress({ status: 'In Progress' });
33
+ promises.push(this.resolveProfiles(targetOrg));
34
+ promises.push(this.resolvePermissionSets(targetOrg));
35
+ const resolvedEntities = await Promise.all(promises);
36
+ return {
37
+ profiles: resolvedEntities[0],
38
+ permissionSets: resolvedEntities[1],
39
+ };
40
+ }
41
+ async resolveProfiles(targetOrg) {
42
+ const profiles = await targetOrg.query(PROFILES_QUERY);
43
+ this.emitProgress({ profiles: { total: profiles.records.length, resolved: 0 } });
44
+ const mdapi = MDAPI.create(targetOrg);
45
+ const resolved = await mdapi.resolve('Profile', profiles.records.map((permsetRecord) => permsetRecord.Profile.Name));
46
+ this.emitProgress({ profiles: { resolved: Object.keys(resolved).length } });
47
+ return resolved;
48
+ }
49
+ async resolvePermissionSets(targetOrg) {
50
+ const permSets = await targetOrg.query(PERMISSION_SETS_QUERY);
51
+ this.emitProgress({ permissionSets: { total: permSets.records.length, resolved: 0 } });
52
+ const mdapi = MDAPI.create(targetOrg);
53
+ const resolved = await mdapi.resolve('PermissionSet', permSets.records.map((permsetRecord) => permsetRecord.Name));
54
+ this.emitProgress({ permissionSets: { resolved: Object.keys(resolved).length } });
55
+ return resolved;
56
+ }
57
+ emitProgress(update) {
58
+ this.status.profiles = { ...this.status.profiles, ...update.profiles };
59
+ this.status.permissionSets = { ...this.status.permissionSets, ...update.permissionSets };
60
+ this.status.users = { ...this.status.users, ...update.users };
61
+ this.status.status = update.status ?? this.status.status;
62
+ this.emit('progress', structuredClone(this.status));
63
+ }
64
+ }
65
+ function findGrantingEntities(permName, resolvedEntities) {
66
+ const entities = new Set();
67
+ Object.entries(resolvedEntities).forEach(([entityName, metadata]) => {
68
+ const userPerms = metadata.userPermissions.map((userPerm) => userPerm.name);
69
+ if (userPerms.includes(permName)) {
70
+ entities.add(entityName);
71
+ }
72
+ });
73
+ return Array.from(entities);
74
+ }
75
+ //# sourceMappingURL=userPermissionScanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"userPermissionScanner.js","sourceRoot":"","sources":["../../../src/libs/quick-scan/userPermissionScanner.ts"],"names":[],"mappings":"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"}
@@ -14,12 +14,24 @@ Target org to export permissions, profiles, users, etc.
14
14
 
15
15
  Directory where the audit config is initialised. If not set, the root directory will be used.
16
16
 
17
+ # flags.preset.summary
18
+
19
+ Select a preset to initialise permission classifications (risk levels).
20
+
21
+ # flags.preset.description
22
+
23
+ The selected preset is applied before any other default mechanisms (such as template configs). This means, values from a selected template override the preset. Consult the documentation to learn more about the rationale behind the default risk levels. The risk levels interact with the configured preset on profiles and permission sets and essentially control, if a permission is allowed in a certain profile / permission set.
24
+
17
25
  # examples
18
26
 
19
27
  - Initialise audit policies at the root directory
20
28
 
21
29
  <%= config.bin %> <%= command.id %> -o MyTargetOrg
22
30
 
31
+ - Initialise audit config at custom directory with preset
32
+
33
+ <%= config.bin %> <%= command.id %> -o MyTargetOrg -d my_dir -p loose
34
+
23
35
  # success.perm-classification-summary
24
36
 
25
37
  Initialised %s permissions at %s.
@@ -35,3 +35,15 @@ At least one policy is not compliant. Review details below.
35
35
  # info.report-file-location
36
36
 
37
37
  Full report was written to: %s.
38
+
39
+ # NoAuditConfigFound
40
+
41
+ The target directory %s is empty or no valid audit config was found. A valid audit config must contain at least one policy.
42
+
43
+ # UserPermClassificationRequiredForProfiles
44
+
45
+ The "Profiles" policy requires at least userPermissions to be initialised, but none were found at the target directory.
46
+
47
+ # UserPermClassificationRequiredForPermSets
48
+
49
+ The "Permission Sets" policy requires at least userPermissions to be initialised, but none were found at the target directory.
@@ -0,0 +1,31 @@
1
+ # summary
2
+
3
+ Performs a quick scan to check permission sets and profiles for user permissions.
4
+
5
+ # description
6
+
7
+ The quick scan does not need an audit config and does not create reports. The target org is scanned "in memory" and simply outputs information, where the searched user permissions
8
+
9
+ # flags.name.summary
10
+
11
+ One or more permissions to be scanned.
12
+
13
+ # flags.name.description
14
+
15
+ You can specify any valid user permission on your org, such as "AuthorApex", "CustomizeApplication" or "ViewSetup". If you are unsure what permissions are available on your org, initialise a new audit config and check the created userPermissions.yml.
16
+
17
+ # flags.target-org.summary
18
+
19
+ The target org to scan.
20
+
21
+ # examples
22
+
23
+ - <%= config.bin %> <%= command.id %>
24
+
25
+ # success.profiles-count
26
+
27
+ Scanned %s profiles.
28
+
29
+ # success.permissionsets-count
30
+
31
+ Scanned %s permission sets.
@@ -2,9 +2,25 @@
2
2
 
3
3
  Allows to modify all parts of the app, including security settings.
4
4
 
5
- # Packaging
5
+ # ModifyMetadata
6
6
 
7
- Allows to create, manage and install packages.
7
+ Allows to modify all parts of the app, including security settings.
8
+
9
+ # Packaging2
10
+
11
+ General permissions for 2nd generation packages.
12
+
13
+ # InstallPackaging
14
+
15
+ Install unlocked and managed packages.
16
+
17
+ # Packaging2PromoteVersion
18
+
19
+ Promote 2nd generation packages for distribution and install on production orgs.
20
+
21
+ # Packaging2Delete
22
+
23
+ Delete versions of 2nd generation packages.
8
24
 
9
25
  # ViewSetup
10
26
 
@@ -14,6 +30,10 @@ Allows to browse setup and view sensitive configurations.
14
30
 
15
31
  Bypass all sharing, making all sharing architecture obsolete.
16
32
 
33
+ # ModifyAllData
34
+
35
+ Bypass all sharing and layout permissions.
36
+
17
37
  # AuthorApex
18
38
 
19
39
  Apex can perform harmful actions, and deployed Apex runs in system mode.
@@ -33,3 +53,19 @@ Set up and reset the connected MFA for a user.
33
53
  # CanApproveUninstalledApps
34
54
 
35
55
  Allows to authorize new connected apps and therefore new integrations.
56
+
57
+ # UseAnyApiClient
58
+
59
+ Bypass all security settings and use deprecated login types.
60
+
61
+ # ViewClientSecret
62
+
63
+ Access and export secrets from connected apps.
64
+
65
+ # ExportReport
66
+
67
+ Reports allow to export classified or sensitive data.
68
+
69
+ # ManageRemoteAccess
70
+
71
+ Manage, create, edit, and delete connected applications.
@@ -5,7 +5,8 @@
5
5
  "args": {},
6
6
  "description": "Exports permissions (standard and custom), permission sets, profiles, users, etc from the target org. All classifications are initialised with sane defaults that you can customize later.",
7
7
  "examples": [
8
- "Initialise audit policies at the root directory\n<%= config.bin %> <%= command.id %> -o MyTargetOrg"
8
+ "Initialise audit policies at the root directory\n<%= config.bin %> <%= command.id %> -o MyTargetOrg",
9
+ "Initialise audit config at custom directory with preset\n<%= config.bin %> <%= command.id %> -o MyTargetOrg -d my_dir -p loose"
9
10
  ],
10
11
  "flags": {
11
12
  "json": {
@@ -43,6 +44,21 @@
43
44
  "multiple": false,
44
45
  "type": "option"
45
46
  },
47
+ "preset": {
48
+ "char": "p",
49
+ "description": "The selected preset is applied before any other default mechanisms (such as template configs). This means, values from a selected template override the preset. Consult the documentation to learn more about the rationale behind the default risk levels. The risk levels interact with the configured preset on profiles and permission sets and essentially control, if a permission is allowed in a certain profile / permission set.",
50
+ "name": "preset",
51
+ "summary": "Select a preset to initialise permission classifications (risk levels).",
52
+ "default": "strict",
53
+ "hasDynamicHelp": false,
54
+ "multiple": false,
55
+ "options": [
56
+ "strict",
57
+ "loose",
58
+ "none"
59
+ ],
60
+ "type": "option"
61
+ },
46
62
  "api-version": {
47
63
  "description": "Override the api version used for api requests made by this command",
48
64
  "name": "api-version",
@@ -155,7 +171,85 @@
155
171
  "run:org:audit",
156
172
  "run:audit:org"
157
173
  ]
174
+ },
175
+ "org:scan:user-perms": {
176
+ "aliases": [],
177
+ "args": {},
178
+ "description": "The quick scan does not need an audit config and does not create reports. The target org is scanned \"in memory\" and simply outputs information, where the searched user permissions",
179
+ "examples": [
180
+ "<%= config.bin %> <%= command.id %>"
181
+ ],
182
+ "flags": {
183
+ "json": {
184
+ "description": "Format output as json.",
185
+ "helpGroup": "GLOBAL",
186
+ "name": "json",
187
+ "allowNo": false,
188
+ "type": "boolean"
189
+ },
190
+ "flags-dir": {
191
+ "helpGroup": "GLOBAL",
192
+ "name": "flags-dir",
193
+ "summary": "Import flag values from a directory.",
194
+ "hasDynamicHelp": false,
195
+ "multiple": false,
196
+ "type": "option"
197
+ },
198
+ "name": {
199
+ "char": "n",
200
+ "description": "You can specify any valid user permission on your org, such as \"AuthorApex\", \"CustomizeApplication\" or \"ViewSetup\". If you are unsure what permissions are available on your org, initialise a new audit config and check the created userPermissions.yml.",
201
+ "name": "name",
202
+ "required": true,
203
+ "summary": "One or more permissions to be scanned.",
204
+ "hasDynamicHelp": false,
205
+ "multiple": true,
206
+ "type": "option"
207
+ },
208
+ "target-org": {
209
+ "char": "o",
210
+ "name": "target-org",
211
+ "noCacheDefault": true,
212
+ "required": true,
213
+ "summary": "The target org to scan.",
214
+ "hasDynamicHelp": true,
215
+ "multiple": false,
216
+ "type": "option"
217
+ },
218
+ "api-version": {
219
+ "description": "Override the api version used for api requests made by this command",
220
+ "name": "api-version",
221
+ "hasDynamicHelp": false,
222
+ "multiple": false,
223
+ "type": "option"
224
+ }
225
+ },
226
+ "hasDynamicHelp": true,
227
+ "hiddenAliases": [],
228
+ "id": "org:scan:user-perms",
229
+ "pluginAlias": "@j-schreiber/sf-cli-security-audit",
230
+ "pluginName": "@j-schreiber/sf-cli-security-audit",
231
+ "pluginType": "core",
232
+ "strict": true,
233
+ "summary": "Performs a quick scan to check permission sets and profiles for user permissions.",
234
+ "enableJsonFlag": true,
235
+ "isESM": true,
236
+ "relativePath": [
237
+ "lib",
238
+ "commands",
239
+ "org",
240
+ "scan",
241
+ "user-perms.js"
242
+ ],
243
+ "aliasPermutations": [],
244
+ "permutations": [
245
+ "org:scan:user-perms",
246
+ "scan:org:user-perms",
247
+ "scan:user-perms:org",
248
+ "org:user-perms:scan",
249
+ "user-perms:org:scan",
250
+ "user-perms:scan:org"
251
+ ]
158
252
  }
159
253
  },
160
- "version": "0.4.1"
254
+ "version": "0.6.0"
161
255
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@j-schreiber/sf-cli-security-audit",
3
3
  "description": "Salesforce CLI plugin to automate highly configurable security audits",
4
- "version": "0.4.1",
4
+ "version": "0.6.0",
5
5
  "repository": {
6
6
  "type": "https",
7
7
  "url": "https://github.com/j-schreiber/js-sf-cli-security-audit"
@@ -1,2 +0,0 @@
1
- import { PermissionsClassification } from '../core/file-mgmt/schema.js';
2
- export declare const DEFAULT_CLASSIFICATIONS: Record<string, PermissionsClassification>;