Skip to content

Commit 41388f8

Browse files
authored
Merge pull request #110 from Lightning-Flow-Scanner/feature/unsafe-running-context
feat: new rule unsafe running context
2 parents 932aa81 + 1053746 commit 41388f8

12 files changed

+393
-149
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,6 @@ src/main/.DS_Store
88
dist/
99
out/
1010

11-
.nyc_output/
11+
.nyc_output/
12+
13+
.DS_Store

package-lock.json

Lines changed: 138 additions & 136 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
"precommit": "lint-staged",
1414
"lint": "eslint src",
1515
"prepack": "npm run build && npm run dist",
16-
"dist": "rollup --config",
16+
"rollup": "rollup --config",
17+
"uglify": "uglifyjs dist/index.js --compress -o dist/index.min.js",
18+
"dist": "npm run rollup && npm run uglify",
1719
"test:coverage": "nyc npm test"
1820
},
1921
"keywords": [],
@@ -29,22 +31,23 @@
2931
"@rollup/plugin-typescript": "^11.1.6",
3032
"@types/chai": "^4.3.16",
3133
"@types/mocha": "^10.0.7",
32-
"@types/node": "^20.14.10",
34+
"@types/node": "^20.14.12",
3335
"chai": "4.4.1",
3436
"eslint": "8.57.0",
35-
"husky": "^9.0.11",
37+
"husky": "^9.1.1",
3638
"lint-staged": "^15.2.7",
3739
"logging": "^3.3.0",
38-
"mocha": "^10.6.0",
40+
"mocha": "^10.7.0",
3941
"nyc": "^17.0.0",
4042
"prettier": "^3.3.3",
41-
"rollup": "^4.18.1",
43+
"rollup": "^4.19.0",
4244
"rollup-plugin-polyfill-node": "^0.13.0",
4345
"semantic-release": "^24.0.0",
4446
"ts-node": "^10.9.2",
4547
"tslib": "^2.6.3",
46-
"typescript": "^5.5.3",
47-
"typescript-eslint": "^7.16.0"
48+
"typescript": "^5.5.4",
49+
"typescript-eslint": "^7.17.0",
50+
"uglify-js": "^3.19.0"
4851
},
4952
"packageManager": "[email protected]",
5053
"dependencies": {

readme.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ _An Extensible Rule Engine capable of conducting static analysis on the metadata
2929
| **SOQL Query In A Loop** ([`SOQLQueryInLoop`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/SOQLQueryInLoop.ts)) | To prevent exceeding Apex governor limits, it is advisable to consolidate all your SOQL queries at the conclusion of the flow. |
3030
| **Unconnected Element** ([`UnconnectedElement`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/UnconnectedElement.ts)) | Unconnected elements which are not being used by the Flow should be avoided to keep Flows efficient and maintainable. |
3131
| **Unused Variable** ([`UnusedVariable`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/UnusedVariable.ts)) | To maintain the efficiency and manageability of your Flow, it's advisable to avoid including unconnected variables that are not in use. |
32+
| **Unsafe Running Context** ([`UnsafeRunningContext`](https://github.com/Lightning-Flow-Scanner/lightning-flow-scanner-core/tree/master/src/main/rules/UnsafeRunningContext.ts)) | This flow is configured to run in System Mode without Sharing. This system context grants all running users the permission to view and edit all data in your org. Running a flow in System Mode without Sharing can lead to unsafe data access. |
3233

3334
## Core Functions
3435

rollup.config.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ export default {
99
dir: "dist",
1010
format: "umd",
1111
name: "lightningflowscanner",
12-
sourcemap: true,
12+
sourcemap: false,
1313
globals: {
1414
"path-browserify": "p",
1515
xmlbuilder2: "xmlbuilder2",
1616
},
17-
}
17+
},
1818
],
1919
plugins: [
2020
nodePolyfills(),
21-
typescript({tsconfig: `tsconfig.umd.json`}),
21+
typescript({ tsconfig: `tsconfig.umd.json` }),
2222
nodeResolve({ preferBuiltins: true }),
2323
nodePolyfills(),
2424
],
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import * as core from "../internals/internals";
2+
import { RuleCommon } from "../models/RuleCommon";
3+
4+
export class UnsafeRunningContext extends RuleCommon implements core.IRuleDefinition {
5+
constructor() {
6+
super(
7+
{
8+
name: "UnsafeRunningContext",
9+
label: "Unsafe Flow Running Context",
10+
description:
11+
"This flow is configured to run in System Mode without Sharing. This system context grants all running users the permission to view and edit all data in your org. Running a flow in System Mode without Sharing can lead to unsafe data access.",
12+
supportedTypes: [...core.FlowType.backEndTypes, ...core.FlowType.visualTypes],
13+
docRefs: [
14+
{
15+
label:
16+
"Learn about data safety when running flows in system context in Salesforce Help",
17+
path: "https://help.salesforce.com/s/articleView?id=sf.flow_distribute_context_data_safety_system_context.htm&type=5",
18+
},
19+
],
20+
isConfigurable: false,
21+
autoFixable: false,
22+
},
23+
{ severity: "warning" }
24+
);
25+
}
26+
27+
public execute(flow: core.Flow): core.RuleResult {
28+
const hasRunInMode = "runInMode" in flow.xmldata;
29+
const runInMode: string = hasRunInMode ? flow.xmldata.runInMode : undefined;
30+
const riskyMode: string = "SystemModeWithoutSharing";
31+
32+
const results: core.ResultDetails[] = [];
33+
34+
if (hasRunInMode && runInMode === riskyMode) {
35+
results.push(
36+
new core.ResultDetails(new core.FlowAttribute(runInMode, "runInMode", `== ${riskyMode}`))
37+
);
38+
}
39+
return new core.RuleResult(this, results);
40+
}
41+
}

src/main/store/DefaultRuleStore.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import { MissingNullHandler } from "../rules/MissingNullHandler";
1212
import { ProcessBuilder } from "../rules/ProcessBuilder";
1313
import { SOQLQueryInLoop } from "../rules/SOQLQueryInLoop";
1414
import { UnconnectedElement } from "../rules/UnconnectedElement";
15+
import { UnsafeRunningContext } from "../rules/UnsafeRunningContext";
1516
import { UnusedVariable } from "../rules/UnusedVariable";
1617

17-
export const DefaultRuleStore: {} = {
18+
export const DefaultRuleStore: object = {
1819
APIVersion,
1920
AutoLayout,
2021
CopyAPIName,
@@ -30,4 +31,5 @@ export const DefaultRuleStore: {} = {
3031
UnconnectedElement,
3132
UnusedVariable,
3233
InactiveFlow,
34+
UnsafeRunningContext,
3335
};

tests/UnsafeRunningContext.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { expect } from "chai";
2+
import "mocha";
3+
import * as core from "../src";
4+
import * as path from "path-browserify";
5+
6+
import { ParseFlows } from "../src/main/libs/ParseFlows";
7+
import { ParsedFlow } from "../src/main/models/ParsedFlow";
8+
9+
import { UnsafeRunningContext } from "../src/main/rules/UnsafeRunningContext";
10+
11+
describe("UnsafeRunningContext", () => {
12+
const unsafeRunningContext: UnsafeRunningContext = new UnsafeRunningContext();
13+
14+
it("should have a scan result for without sharing system mode", async () => {
15+
const unsafeContextTestFile = path.join(
16+
__dirname,
17+
"./xmlfiles/Unsafe_Running_Context.flow-meta.xml"
18+
);
19+
const parsed: ParsedFlow = (await ParseFlows([unsafeContextTestFile])).pop() as ParsedFlow;
20+
const ruleResult: core.RuleResult = unsafeRunningContext.execute(parsed.flow as core.Flow);
21+
expect(ruleResult.occurs).to.be.true;
22+
expect(ruleResult.details).to.not.be.empty;
23+
expect(ruleResult.ruleDefinition.severity).to.be.equal("warning");
24+
});
25+
26+
it("should not have a scan result for with sharing system mode", async () => {
27+
const unsafeContextTestFile = path.join(
28+
__dirname,
29+
"./xmlfiles/Unsafe_Running_Context_WithSharing.flow-meta.xml"
30+
);
31+
const parsed: ParsedFlow = (await ParseFlows([unsafeContextTestFile])).pop() as ParsedFlow;
32+
const ruleResult: core.RuleResult = unsafeRunningContext.execute(parsed.flow as core.Flow);
33+
expect(ruleResult.occurs).to.be.false;
34+
expect(ruleResult.details).to.be.empty;
35+
});
36+
37+
it("should not have a scan result for default mode", async () => {
38+
const unsafeContextTestFile = path.join(
39+
__dirname,
40+
"./xmlfiles/Unsafe_Running_Context_Default.flow-meta.xml"
41+
);
42+
const parsed: ParsedFlow = (await ParseFlows([unsafeContextTestFile])).pop() as ParsedFlow;
43+
const ruleResult: core.RuleResult = unsafeRunningContext.execute(parsed.flow as core.Flow);
44+
expect(ruleResult.occurs).to.be.false;
45+
expect(ruleResult.details).to.be.empty;
46+
});
47+
});
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>61.0</apiVersion>
4+
<environments>Default</environments>
5+
<interviewLabel>test {!$Flow.CurrentDateTime}</interviewLabel>
6+
<label>test</label>
7+
<processMetadataValues>
8+
<name>BuilderType</name>
9+
<value>
10+
<stringValue>LightningFlowBuilder</stringValue>
11+
</value>
12+
</processMetadataValues>
13+
<processMetadataValues>
14+
<name>CanvasMode</name>
15+
<value>
16+
<stringValue>AUTO_LAYOUT_CANVAS</stringValue>
17+
</value>
18+
</processMetadataValues>
19+
<processMetadataValues>
20+
<name>OriginBuilderType</name>
21+
<value>
22+
<stringValue>LightningFlowBuilder</stringValue>
23+
</value>
24+
</processMetadataValues>
25+
<processType>Flow</processType>
26+
<runInMode>SystemModeWithoutSharing</runInMode>
27+
<screens>
28+
<name>testtest</name>
29+
<label>test</label>
30+
<locationX>176</locationX>
31+
<locationY>134</locationY>
32+
<allowBack>true</allowBack>
33+
<allowFinish>true</allowFinish>
34+
<allowPause>true</allowPause>
35+
<showFooter>true</showFooter>
36+
<showHeader>true</showHeader>
37+
</screens>
38+
<start>
39+
<locationX>50</locationX>
40+
<locationY>0</locationY>
41+
<connector>
42+
<targetReference>testtest</targetReference>
43+
</connector>
44+
</start>
45+
<status>Draft</status>
46+
</Flow>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Flow xmlns="http://soap.sforce.com/2006/04/metadata">
3+
<apiVersion>61.0</apiVersion>
4+
<environments>Default</environments>
5+
<interviewLabel>Unsafe Running Context {!$Flow.CurrentDateTime}</interviewLabel>
6+
<label>Unsafe Running Context</label>
7+
<processMetadataValues>
8+
<name>BuilderType</name>
9+
<value>
10+
<stringValue>LightningFlowBuilder</stringValue>
11+
</value>
12+
</processMetadataValues>
13+
<processMetadataValues>
14+
<name>CanvasMode</name>
15+
<value>
16+
<stringValue>AUTO_LAYOUT_CANVAS</stringValue>
17+
</value>
18+
</processMetadataValues>
19+
<processMetadataValues>
20+
<name>OriginBuilderType</name>
21+
<value>
22+
<stringValue>LightningFlowBuilder</stringValue>
23+
</value>
24+
</processMetadataValues>
25+
<processType>AutoLaunchedFlow</processType>
26+
<recordCreates>
27+
<description>asdfasdfasdfasdfasdf</description>
28+
<name>Create_Record</name>
29+
<label>Create Record</label>
30+
<locationX>176</locationX>
31+
<locationY>134</locationY>
32+
<inputAssignments>
33+
<field>LastName</field>
34+
<value>
35+
<stringValue>123</stringValue>
36+
</value>
37+
</inputAssignments>
38+
<object>Contact</object>
39+
<storeOutputAutomatically>true</storeOutputAutomatically>
40+
</recordCreates>
41+
<runInMode>DefaultMode</runInMode>
42+
<start>
43+
<locationX>50</locationX>
44+
<locationY>0</locationY>
45+
<connector>
46+
<targetReference>Create_Record</targetReference>
47+
</connector>
48+
</start>
49+
<status>Active</status>
50+
</Flow>

0 commit comments

Comments
 (0)