Skip to content

A CLI plugin, VS Code Extension and GitHub Action for analysis and optimization of Salesforce Flow. Scans metadata for 20+ issues such as hardcoded IDs, unsafe contexts, inefficient DML operations, recursion risks, and more. Supports auto-fixes, rule configurations, and CI/CD integration to help maintain secure and reliable Flow automations.

License

Notifications You must be signed in to change notification settings

Flow-Scanner/lightning-flow-scanner

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

GitHub stars Core version CLI version VS Code version Downloads <v6 Downloads >v6

Lightning Flow Scanner

Detect unsafe contexts, queries in loops, hardcoded IDs, and more to optimize Salesforce Flows


Table of contents


Default Rules

📌Tip: To link directly to a specific rule, use the full GitHub anchor link format. Example:

https://flow-scanner.github.io/lightning-flow-scanner/#unsafe-running-context

Want to code a new rule? → See How to Write a Rule

Action Call In A Loop

To prevent exceeding Apex governor limits, it is advisable to consolidate and bulkify your apex calls, utilizing a single action call containing a collection variable at the end of the loop.

Rule ID: action-call-in-loop Class Name: ActionCallsInLoop Severity: 🔴 Error

DML Statement In A Loop

To prevent exceeding Apex governor limits, consolidate all your database operations—record creation, updates, or deletions—at the conclusion of the flow.

Rule ID: dml-in-loop Class Name: DMLStatementInLoop Severity: 🔴 Error

Duplicate DML Operation

When a flow executes database changes or actions between two screens, prevent users from navigating backward between screens; otherwise, duplicate database operations may be performed.

Rule ID: duplicate-dml Class Name: DuplicateDMLOperation Severity: 🟡 Warning

Excessive Cyclomatic Complexity

The number of loops and decision rules, plus the number of decisions. Use a combination of 1) subflows and 2) breaking flows into multiple concise trigger ordered flows, to reduce the cyclomatic complexity within a single flow, ensuring maintainability and simplicity.

Rule ID: excessive-cyclomatic-complexity Class Name: CyclomaticComplexity Severity: 🔵 Note

Flow Naming Convention

The readability of a flow is paramount. Establishing a naming convention significantly enhances findability, searchability, and overall consistency. Include at least a domain and a brief description of the flow’s actions, for example Service_OrderFulfillment.

Rule ID: invalid-naming-convention Class Name: FlowName Severity: 🔴 Error

Get Record All Fields

Following the principle of least privilege (PoLP), avoid using Get Records with “Automatically store all fields” unless necessary.

Rule ID: get-record-all-fields Class Name: GetRecordAllFields Severity: 🟡 Warning

Hardcoded Id

Avoid hard-coding IDs because they are org specific. Instead, pass them into variables at the start of the flow—via merge-field URL parameters or a Get Records element.

Rule ID: hardcoded-id Class Name: HardcodedId Severity: 🔴 Error

Hardcoded Url

Avoid hard-coding URLs because they are environment specific. Use an $API formula (preferred) or environment-specific sources like custom labels, metadata, or settings.

Rule ID: hardcoded-url Class Name: HardcodedUrl Severity: 🔴 Error

Inactive Flow

Like cleaning out your closet: deleting unused flows is essential. Inactive flows can still cause trouble—such as accidentally deleting records during testing, or being activated as subflows.

Rule ID: inactive-flow Class Name: InactiveFlow Severity: 🟡 Warning

Invalid API Version

Introducing newer API components may lead to unexpected issues with older versions of Flows, as they might not align with the underlying mechanics. Starting from API version 50.0, the Api Version attribute has been readily available on the Flow Object. To ensure smooth operation and reduce discrepancies between API versions, it is strongly advised to regularly update and maintain them.

Rule ID: invalid-api-version Class Name: APIVersion Severity: 🟡 Warning

Missing Auto Layout

With Canvas Mode set to Auto-Layout, elements are spaced, connected, and aligned automatically, keeping your Flow neatly organized—saving you time.

Rule ID: missing-auto-layout Class Name: AutoLayout Severity: 🔵 Note

Missing Fault Path

A flow may fail to execute an operation as intended. By default, the flow displays an error to the user and emails the creator. Customize this behavior by incorporating a Fault Path.

Rule ID: missing-fault-path Class Name: MissingFaultPath Severity: 🟡 Warning

Missing Filter Record Trigger Beta

Record-triggered flows that lack filters on changed fields or entry conditions can lead to unnecessary executions on every record change. This may degrade system performance, hit governor limits faster, and increase resource consumption in high-volume orgs.

Rule ID: missing-record-trigger-filter Class Name: MissingFilterRecordTrigger Severity: 🟡 Warning

Missing Flow Description

Descriptions play a vital role in documentation. It is highly recommended to include details about where a flow is used and its intended purpose.

Rule ID: missing-flow-description Class Name: FlowDescription Severity: 🔴 Error

Missing Metadata Description Beta

Flags Flow elements (Get Records, Assignments, Decisions, Actions, etc.) and metadata components (Variables, Formulas, Constants, Text Templates) that lack a description. Adding concise descriptions greatly improves readability, maintainability, and helps AI tools understand your automation intent.

Rule ID: missing-metadata-description Class Name: MissingMetadataDescription Severity: 🔴 Error

Missing Null Handler

When a Get Records operation finds no data, it returns null. Validate data by using a Decision element to check for a non-null result.

Rule ID: missing-null-handler Class Name: MissingNullHandler Severity: 🟡 Warning

Missing Trigger Order

Guarantee your flow execution order with the Trigger Order property introduced in Spring '22.value to their flows and guarantee their execution order. This priority value is not an absolute value, so the values need not be sequentially numbered as 1, 2, 3, and so on.

Rule ID: unspecified-trigger-order Class Name: TriggerOrder Severity: 🔵 Note

Process Builder

Salesforce is transitioning away from Workflow Rules and Process Builder in favor of Flow. Begin migrating your organization’s automation to Flow.

Rule ID: process-builder-usage Class Name: ProcessBuilder Severity: 🟡 Warning

Record ID as String Beta

Detects flows using a String variable named recordId as input when they could receive the entire record object instead. Since recent Salesforce releases, record pages and quick actions can pass the complete record, eliminating the need for an additional Get Records query and improving performance.

Rule ID: record-id-as-string Class Name: RecordIdAsString Severity: 🔴 Error

Recursive After Update

After-update flows are meant for modifying other records. Using them on the same record can cause recursion. Consider before-save flows for same-record updates.

Rule ID: recursive-record-update Class Name: RecursiveAfterUpdate Severity: 🟡 Warning

Same Record Field Updates

Similar to triggers, before-save contexts can update the same record via $Record without invoking DML.

Rule ID: same-record-field-updates Class Name: SameRecordFieldUpdates Severity: 🟡 Warning

SOQL Query In A Loop

To prevent exceeding Apex governor limits, consolidate all SOQL queries at the end of the flow.

Rule ID: soql-in-loop Class Name: SOQLQueryInLoop Severity: 🔴 Error

Transform Instead of Loop Beta

Detects Loop elements that directly connect to Assignment elements. Transform elements handle collection manipulation in bulk operations, providing significant performance improvements over iterative loop-assignment patterns.

Rule ID: transform-instead-of-loop Class Name: TransformInsteadOfLoop Severity: 🔵 Note

Unclear API Name

Maintaining multiple elements with a similar name, like Copy_X_Of_Element, can diminish the overall readability of your Flow. When copying and pasting these elements, remember to update the API name of the newly created copy.

Rule ID: unclear-api-naming Class Name: CopyAPIName Severity: 🟡 Warning

Unreachable Element

Avoid unconnected elements that are not used by the flow to keep flows efficient and maintainable.

Rule ID: unreachable-element Class Name: UnconnectedElement Severity: 🟡 Warning

Unsafe Running Context

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.

Rule ID: unsafe-running-context Class Name: UnsafeRunningContext Severity: 🔴 Error

Unused Variable

To maintain efficiency and manageability, avoid including variables that are never referenced.

Rule ID: unused-variable Class Name: UnusedVariable Severity: 🟡 Warning


Configuration

It is recommend to configure and define:

  • The severity of violating any specific rule.
  • Expressions used for rules, such as REGEX patterns and comparison operators.
  • Any known exceptions that should be ignored during scanning.
  • Flows or directories to exclude from scanning entirely.
{
  "rules": {
    // Your rules here
  },
  "exceptions": {
    // Your exceptions here
  }
}

Most Lightning Flow Scanner distributions automatically resolve configurations from .flow-scanner.yml, .flow-scanner.json, or package.jsonflowScanner.

By default, all default rules are executed. You can customize individual rules and override the rules to be executed without having to specify every rule. Below is a breakdown of the available attributes of rule configuration:

{
  "rules": {
    "<RuleId>": {
      "severity": "<Severity>", // Override severity level
      "expression": "<Expression>", // Override rule expression
      "enabled": "false" // Disable this rule
    }
  }
}

Configure Severity Levels

When the severity is not provided it will be warning by default. Other available values for severity are error and note. Configure the severity per rule as shown below:

{
  "rules": {
    "missing-flow-description": {
      "severity": "error"
    },
    "unused-variable": {
      "severity": "note"
    }
  }
}

Overwrite Expressions

Some rules have an expression to configure, such as the expression, that will overwrite default values. These can be configured in the same way as severity as shown in the following example.

{
  "rules": {
    "invalid-api-version": {
      "expression": "===58" // comparison operator
    },
    "invalid-naming-convention": {
      "expression": "[A-Za-z0-9]" // regular expression
    }
  }
}

Define Exceptions

Defining exceptions allows you to exclude specific scenarios from rule enforcement. Exceptions can be specified at the flow, rule, or result level to provide fine-grained control. Below is a breakdown of the available attributes of exception configuration:

{
  "exceptions": {
    "<FlowName>": {
      "<RuleId>": [
        "<ResultName>", // Suppress a result
        "*", // Wildcard to suppress all results
        ...
      ]
    },
    ...
  }
}

Example

{
  "exceptions": {
    "MyFlow": {
      "hardcoded-id": ["Old_Lookup_1"],
      "missing-null-handler": ["*"]
    }
  }
}

Exclude Flows from Scanning

Lightning Flow Scanner provides two complementary ways to exclude flows from scanning:

Exclude by File Path (Node.js only)

Use glob patterns to exclude flows based on their file system location. This is useful for excluding entire directories or specific file patterns during the flow discovery phase:

{
  "ignore": [
    "**/testing/**",
    "**/*_Deprecated.flow-meta.xml"
  ]
}

Note: The ignore option uses glob patterns and applies during file discovery before flows are parsed. This is the most efficient way to exclude large numbers of flows.

Environment compatibility: ignore requires Node.js (file system access) and is available in CLI Plugin, VS Code Extension, and GitHub Action. It is not available when using the Core Library in browser/web environments.

Exclude by Flow API Name (Browser-compatible)

Exclude specific flows by their unique API names, regardless of their location. This is particularly useful for:

  • Excluding specific flows without knowing their exact file path
  • Working with metadata API deployments where directory structures may vary
  • More precise control than path-based patterns
{
  "ignoreFlows": [
    "My_Legacy_Flow",
    "Temporary_Test_Flow",
    "Deprecated_Process_Builder"
  ]
}

Note: The ignoreFlows option applies after flows are parsed, using the flow's API name (the <fullName> element in the flow metadata). Flow names are unique identifiers and work regardless of the flow's file system location.

Environment compatibility: ignoreFlows works in all environments including Node.js and browser/web distributions, as it operates on parsed flow data rather than file system paths.

Scan Modes

Beta Mode

New rules are introduced in Beta mode before being added to the default ruleset. To include current Beta rules, enable the optional betamode parameter in your configuration:

{
  "betaMode": true
}

Rule Mode

By default, Lightning Flow Scanner runs all default rules and merges any custom configurations you provide. This means you can override specific rules without having to list every rule to be executed. If instead, you want to run only the rules you explicitly specify, use "ruleMode": "isolated":

{
  "ruleMode": "isolated"
}

Installation

Distributions

Distribution Best for Install
Salesforce CLI Plugin Local development, scratch orgs, CI/CD sf plugins install lightning-flow-scanner
VS Code Extension Real-time scanning inside VS Code code --install-extension ForceConfigControl.lightning-flow-scanner-vsx
Salesforce App (Managed Package) Run scans directly inside a Salesforce org sf package install --package 04tgK0000008CLlQAM
GitHub Action Native PR checks uses: Flow-Scanner/lightning-flow-scanner@main
Core Library (Node.js + Browser) Custom tools, scripts, extensions, web apps npm install -g @flow-scanner/lightning-flow-scanner-core

Privacy: Zero user data collected. All processing is client-side. → See our Security Policy.

CICD Templates

Ready-to-use CI/CD templates and a Copado Plugin.

Platform Type Link
Azure DevOps Full Project Scan azure-pipelines-flow-FullScan.yml
Azure DevOps Change-Based Scan azure-pipelines-flow-changedFiles.yml
Copado Plugin Copado Plugin Copado Marketplace

Quick Start

Salesforce CLI Plugin

Use lightning-flow-scanner in the Salesforce CLI:

sf flow:scan # Scan flows in the current directory
sf flow:scan --sarif > report.sarif # Export scan results as SARIF
sf flow scan --csv > results.csv # Export scan results as CSV
sf flow doc > flow-docs.md # Generate flow documentation (Single markdown file)
sf flow doc --output flow-docs --separate # Generate one Markdown file per flow
sf flow:fix -d src/force-app # Fix flows in a specific directory

For full details, see the CLI Readme.

VS Code Extension

Use our side bar or the Command Palette and type flow scanner to see the list of all available commands.

  • Configure Scanner - Set up rules in .flow-scanner.yml
  • Scan Flows - Analyze a directory or selected flow files
  • Fix Flows - Automatically apply available fixes
  • Generate Flow Documentation - Generate flow documentation
  • Open Scanner Documentation - Open the rules reference guide

For full details, see the VSX Readme.

GitHub Action

Add a GitHub workflow file .github/workflows/scan-flows.yml to detect issues directly in pull requests:

- name: Lightning Flow Scan
  id: flowscanner
  uses: Flow-Scanner/lightning-flow-scanner@main
  with:
    sarif-only: true  # Strict mode for PRs

- name: Upload SARIF to Code Scanning
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: ${{ steps.flowscanner.outputs.sarifPath }}

For full details, see the Action Readme.

Core Module

Use lightning-flow-scanner-core as a Node.js/browser dependency:

// Basic
import { parse, scan } from "@flow-scanner/lightning-flow-scanner-core";
parse("flows/*.xml").then(scan);

// Get SARIF output (e.g. for GitHub Code Scanning)
import { parse, scan, exportSarif } from "@flow-scanner/lightning-flow-scanner-core";
parse("flows/**/*.flow-meta.xml").then(scan).then(exportSarif)
  // .then(sarif => fs.writeFile("results.sarif", sarif))

// Generate Markdown documentation with Mermaid flow diagrams
import { parse, exportDiagram } from "@flow-scanner/lightning-flow-scanner-core";
parse("flows/**/*.flow-meta.xml").then(exportDiagram)
  // .then(md => fs.writeFile("flow-docs.md", md))

// Browser Usage (Tooling API)
const { Flow, scan } = window.lightningflowscanner;
const metadataRes = await conn.tooling.query(`SELECT Id, FullName, Metadata FROM Flow`);
const results = scan(
  metadataRes.records.map((r) => ({
    uri: `/services/data/v60.0/tooling/sobjects/Flow/${r.Id}`,
    flow: new Flow(r.FullName, r.Metadata),
  })) //, optionsForScan
);

For more on Programmatic API, types, and advanced usage of @flow-scanner/lightning-flow-scanner-core, see the Core Library Reference.

Development

This project optionally uses Volta to guarantee the exact same Node.js and tool versions for every contributor.

MacOs/Linux:

curl https://get.volta.sh | bash

Windows:

winget install Volta.Volta

Volta will automatically install and lock the tool versions defined in package.json.

  1. Clone the repository

    git clone https://github.com/Flow-Scanner/lightning-flow-scanner.git
  2. Install dependencies:

    pnpm install
  3. Compile:

    pnpm run build

    To compile just the core package::

    pnpm build:core
  4. Run tests:

    pnpm test

    Or to test a new version of the core:

    pnpm test:core
  5. Linking the core module locally(Optional):

    To link the module, run:

    pnpm link --global @flow-scanner/lightning-flow-scanner-core

    You can now do Ad-Hoc Testing with node:

    node -i -e "import('@flow-scanner/lightning-flow-scanner-core').then(m => { Object.assign(global, m.default ? m.default : m); console.log('✅ Core loaded! Try: await parse(...), scan(...), etc.'); })"

    Or test in a dependent project with npm link @flow-scanner/lightning-flow-scanner-core

  6. Deploy Demo Flows (Optional):

    cd example-flows && sf project deploy start

    Navigate to the Demo Readme for full details

  7. Create a standalone UMD Module(Optional):

      pnpm dist

    This creates UMD at dist/lightning-flow-scanner-core.umd.js.

Want to help improve Lightning Flow Scanner? See our Contributing Guidelines

About

A CLI plugin, VS Code Extension and GitHub Action for analysis and optimization of Salesforce Flow. Scans metadata for 20+ issues such as hardcoded IDs, unsafe contexts, inefficient DML operations, recursion risks, and more. Supports auto-fixes, rule configurations, and CI/CD integration to help maintain secure and reliable Flow automations.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project