Skip to main content

VS Code, Cursor, and Windsurf extension

Vulkro ships two editor extensions built from one shared source tree:

  • Vulkro (the core edition) for general-purpose code. In a Salesforce DX workspace it also starts the vulkro-sf language server as a sidecar.
  • Vulkro for Salesforce (the Salesforce edition, extension id vulkro.vulkro-sf) where vulkro-sf lsp is the primary scanner for the whole SFDX project.

Both load in Cursor and Windsurf (VS Code forks) from the same .vsix. The extension is a thin shell over the language server: it does not re-implement detection. Findings come from the vulkro-sf engine exactly as it produces them. The detectors stay closed source; the extension is the UI surface, not a second implementation.

This page covers the Salesforce side. For the general behaviour shared with the core scanner see the core extension page.

Install

The extension is distributed as a .vsix package today; a Visual Studio Marketplace listing is coming soon. Install the Salesforce edition with:

code --install-extension vulkro-sf-<version>.vsix

Cursor and Windsurf accept the same file through their own "Install from VSIX" action. The extension requires VS Code 1.101 or later; the newer AI APIs (language-model tools, the MCP provider) are feature-detected at runtime, so a fork that lacks them degrades gracefully instead of failing to activate.

Requirements

  • The vulkro-sf binary. The extension resolves it in this order: the vulkro-sf.path setting, a vendored <workspace>/.vulkro/bin/vulkro-sf, a binary bundled inside a platform-specific .vsix, $PATH, then the managed install at ~/.vulkro/bin. If none resolves, the startup error offers an Install action that downloads the release, verifies its .sha256 checksum, and installs it into ~/.vulkro/bin (no sudo, no PATH changes). The installer honours HTTPS_PROXY / NO_PROXY, and VULKRO_OFFLINE=1 disables it entirely (install manually instead). Prebuilt binaries exist for macOS (arm64, x64), Linux (x64, arm64), and Windows (x64).
  • The Salesforce Extension Pack (or any extension that contributes the apex language id). Apex highlighting routes on that language id; without it, .cls / .trigger files are still matched by file glob but you lose Apex syntax support.

Which server scans what

The Salesforce edition runs vulkro-sf lsp as its primary and only language server. Its document selector covers:

  • Apex: the apex language id, *.cls, *.trigger
  • Visualforce: *.page, *.component
  • Aura: aura/**/*.cmp markup plus aura/**/*.js controllers
  • LWC: lwc/**/*.js and lwc/**/*.html (scoped to the lwc/ folder, so it never claims general JavaScript)
  • Metadata: *.flow-meta.xml, *.permissionset-meta.xml, *.profile-meta.xml

In the core edition, opening a workspace with an SFDX marker (sfdx-project.json, .forceignore, or force-app/main/default) starts a second client for vulkro-sf lsp alongside the core vulkro lsp server. There the Salesforce client takes Apex, Visualforce, Aura markup, and the metadata shapes, while general JavaScript stays with the core server. Apex is deliberately absent from the core client's selector so a finding is never published twice, and the two servers tag diagnostics with distinct sources (vulkro vs vulkro-sf).

Under the hood the two servers keep disjoint advertised command lists: the vulkro-sf server advertises only its vulkro-sf.* commands, and the shared editor surface (overview, codemap, impact, AI fix, and so on) is dispatched over raw workspace/executeCommand under the same vulkro.* names the core server uses. That is what lets the core edition run both clients in one editor window without command collisions, and it is why the Salesforce edition's sidebar views work unchanged: the vulkro-sf server answers the same requests with the same JSON shapes.

When scans run

  • On open: the server publishes a fast single-file first paint, then runs the full project scan and reconciles. Squiggles appear in a fraction of a second; project-wide results follow.
  • On save: same pattern - a fast repaint of the saved file, then a project rescan.
  • On edit: textDocument/didChange republishes diagnostics from the cached scan (debounced, 300 ms by default, clamped to 50 - 5000 ms via the VULKRO_SF_LSP_DEBOUNCE_MS environment variable). Typing does not trigger a rescan; open and save do.
  • Watch mode (vulkro-sf.watch, off by default): additionally watches Apex, Visualforce, Aura markup, and flow / permission-set / profile metadata on disk, so the whole DX project rescans as files change, surfacing findings for files you never opened. Toggling the setting restarts the Salesforce server.

Inline findings

Findings render as native diagnostics with the rule message and remediation in the hover and the Problems panel:

  • Critical and High render as Error, Medium as Warning, Low as Information, Info as Hint. Because Critical and High share the Error colour, the critical and high display floors behave identically in the editor.
  • Each diagnostic carries a stable rule code (the kebab-case detector signal, or a literal id such as AP-001) that stays the same across scans, plus a clickable link to the rule's documentation.
  • Findings with a traced taint flow surface the source, steps, and sink as related information, so the full path renders natively in the Problems panel and the peek view.
  • vulkro-sf.severityThreshold (default low) is a display floor, not a scan setting: the scanner still detects everything, but findings below the floor are hidden from the editor and the sidebar views. With the default, info findings are hidden.

The sidebar

The Vulkro activity-bar container shows four views by default: Overview, This File, Issues, and Account. Turning on vulkro.showAdvancedViews reveals the full set: Org, Changes, History, Impact, Codemap, Trends, Dependencies, AI Hunt, Suppressions, and Ask Vulkro. All of them are driven by the vulkro-sf server in the Salesforce edition.

The views carry actionable empty states: a missing binary offers the managed install, and a signed-out state offers sign-in. The first sign-in on a device starts a 14-day trial of the full product; licenses are issued directly by our team ([email protected]). Air-gapped machines skip sign-in and activate a machine-bound .lic file instead (the Activate license file command).

Salesforce commands

Three commands are contributed under the Vulkro for Salesforce prefix (always present in the command palette; outside a running Salesforce server they show an actionable message instead of failing silently):

  • Vulkro for Salesforce: Explain finding by rule ID renders the rule's long-form explainer into the "Vulkro for Salesforce" output channel. Accepts a Vulkro rule id (AP-001, an OWASP slug, or an SF-PERM- / SF-FLOW- id).
  • Vulkro for Salesforce: Suppress finding on this line reads the rule id from the diagnostic under the cursor (falling back to a prompt), asks the server for a // vulkro:disable next-line <RULE> edit, and applies it. The server never edits the file itself; it returns the edit and the client applies it, preserving the read-only-by-design contract. No logic is ever rewritten.
  • Vulkro for Salesforce: Audit configured target org runs a permission-posture audit against vulkro-sf.targetOrg (see below).

The live-org audit

The org-audit command runs entirely through your own authenticated sf CLI: the extension never implements an OAuth flow, never stores a token, and never calls a Vulkro server for org data. It resolves the target org from the vulkro-sf.targetOrg setting (or the VULKRO_SF_LSP_TARGET_ORG environment variable) and queries that org via sf.

The in-editor audit covers permission sets: over-privilege signals such as Modify All Data, View All Data, and Author Apex. The broader posture surface (session settings, MFA, sharing, packages, connected apps, trust status) is reported by the vulkro-sf org CLI commands on a full audit; see the sf CLI handoff and Live-org setup.

The Org sidebar view (behind vulkro.showAdvancedViews) renders the result of the last explicit audit. It never runs sf behind your back: an org audit only fires when you invoke it.

If sf is not installed or you are not logged in, the command surfaces an actionable error (run sf org login web, then retry).

Settings

// .vscode/settings.json (or User Settings)
{
// Path to the vulkro-sf binary. Default: vulkro-sf (resolved as described
// under Requirements).
"vulkro-sf.path": "/usr/local/bin/vulkro-sf",

// Display floor for the Salesforce findings. Default: low (hides info).
// One of: critical, high, medium, low, info.
"vulkro-sf.severityThreshold": "high",

// Watch mode: rescan the whole DX project as files change on disk.
// Default: false.
"vulkro-sf.watch": false,

// Default org alias or username for the org-audit command. Also honoured
// via the VULKRO_SF_LSP_TARGET_ORG environment variable.
"vulkro-sf.targetOrg": "my-scratch-org",

// Local AI model override. Empty = the engine default (qwen2.5-coder:7b).
"vulkro-sf.ai.model": "",

// Local AI endpoint override. Empty = the default local Ollama endpoint
// (http://127.0.0.1:11434/v1).
"vulkro-sf.ai.url": "",

// AI backend: "local" (default, local Ollama, fully offline) or "editor"
// (reuse the editor's own AI model; opt-in, see below).
"vulkro-sf.ai.transport": "local",

// Trace LSP traffic in the "Vulkro for Salesforce" output channel.
"vulkro-sf.trace.server": "off"
}

vulkro-sf.path is independent of the core vulkro.path, so in the core edition on an SFDX workspace you can point each server at its own build. Changing any of these restarts the Salesforce language server so the new value takes effect.

AI in the editor

AI assistance is advisory and local by default: it never changes a deterministic scan result, and the published benchmark is AI-free. The findings, severities, and exit codes are the deterministic engine's alone.

  • Set up AI model walks you through selecting a local model served by Ollama (the engine default is qwen2.5-coder:7b) and verifies the connection. Test AI connection and Disable AI manage it afterwards. The AI actions on the sidebar views stay hidden until the AI layer reports ready.
  • Fix issue with AI (review + apply) asks the server for a proposed patch for the finding under the cursor, shows the diff in a review panel, and applies it only when you accept. Each proposal is validated by re-running the deterministic detector on the patched class: a fix is verified when a fresh deterministic re-scan no longer reports the finding and the file still parses (and no new finding appears in the file). Verified means exactly that; it does not claim your build compiles or your tests pass. A patch that fails validation is refused with a reason, never applied.
  • Use editor AI model / Use local AI model switch the AI backend. The editor transport reuses the model your editor already provides (for example GitHub Copilot) through a loopback shim, so you do not have to install Ollama. It is opt-in behind an explicit confirmation, because prompt content (which can include your Apex source and findings) is sent to whatever provider backs that model, usually a cloud service. It is refused when VULKRO_OFFLINE=1 is set. The local transport keeps everything on your machine.

Your editor's AI agent can call the scanner

The Salesforce edition also exposes the engine to the editor's AI:

  • Two language-model tools are contributed for agent mode: vulkro_sf_scan_file (scan a Salesforce file and return its findings) and vulkro_sf_explain_finding (explain a rule id). Both are referenceable in prompts.
  • An MCP server-definition provider registers Vulkro for Salesforce Security, launching the engine's own vulkro-sf mcp serve (stdio) with VULKRO_OFFLINE=1 set, so the agent's scans never reach the network. The MCP server exposes nine Salesforce tools: sf_scan_project, sf_scan_metadata_only, sf_antipatterns, sf_org_perms, sf_org_effective_perms, sf_org_session, sf_org_mfa, sf_org_health_check, and sf_run_compliance_report. The org tools use your own authenticated sf CLI, like everything else.

Report export

Vulkro: Export report runs the same whole-project scan CI would (vulkro-sf scan <root> --format <fmt>) and writes the output to a file you choose. SARIF 2.1.0 is the portable default (open it in the Microsoft SARIF Viewer or upload it to code scanning); JSON is the native findings document for scripts and jq pipelines. A scan that reports findings still produces a report: only a real scan error aborts the export.

What this isn't (yet)

  • Not on the marketplaces yet. The .vsix install above is the distribution channel today; the Marketplace listing is coming soon.
  • No as-you-type rescans. Edits republish cached results; scans run on open and save (or continuously from disk in watch mode).
  • The in-editor org audit is permission sets only. The full posture surface lives in the vulkro-sf org CLI commands.
  • No org picker. The org command audits the single configured vulkro-sf.targetOrg.

Where to go next