lsp

package
v3.101.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 1, 2026 License: AGPL-3.0 Imports: 29 Imported by: 0

Documentation

Index

Constants

View Source
const SourceSCA = "vulnetix-sca"

SourceSCA is the Diagnostic.source for dependency findings.

The exact string matters. The extension routes diagnostics into one collection per scanner family by matching this against a fixed list, and anything it does not recognise is filed as SAST — so a near miss here does not fail loudly, it quietly mislabels every dependency finding.

Variables

This section is empty.

Functions

func CountBySeverity

func CountBySeverity(diags map[string][]protocol.Diagnostic) map[string]int

CountBySeverity summarises diagnostics for the status bar.

func DefaultLogger

func DefaultLogger(prefix string) func(string, ...any)

DefaultLogger writes server logs to stderr, which is where they belong: the JSON-RPC channel owns stdout.

func DescribeCounts

func DescribeCounts(counts map[string]int) string

DescribeCounts renders a count summary for a log line.

func GroupByURI

func GroupByURI(findings []sast.Finding, docs map[string]docText, m SeverityMapping) map[string][]protocol.Diagnostic

GroupByURI converts findings into diagnostics grouped by document URI.

docs maps a repository-relative path to that document's current text. A finding whose file is not in docs is skipped: without the current text there is no way to compute a range that matches what the user is looking at, and guessing produces an underline in the wrong place.

Every URI in docs appears in the result, including those with no findings. That is required rather than an optimisation: publishDiagnostics replaces the whole list for a URI, so a file whose last finding was just fixed needs an explicit empty list or the stale diagnostic stays on screen forever.

func PathToURI

func PathToURI(path string) string

PathToURI converts a local path to a file:// URI.

func RelPathFor

func RelPathFor(root, uri string) (string, bool)

RelPathFor returns doc's path relative to root, in slash form, and whether it is inside root at all.

A document outside every workspace folder is not scanned: the rule engine keys everything on repository-relative paths, and a file from elsewhere would either escape the root with .. segments or collide with a real path.

func Serve

func Serve(ctx context.Context, r io.Reader, w io.Writer, cfg Config) error

Serve runs the server over the given streams until the connection closes.

r and w are the JSON-RPC channel. The caller is responsible for making sure nothing else in the process writes to w: cmd/ writes to os.Stdout in 144 places, and a single stray byte desynchronises the framing and the client hard-fails. See cmd/lsp.go.

func SourceForKind

func SourceForKind(kind string) string

SourceForKind returns the diagnostic source for a rule kind, defaulting to the sast source for a kind that has not been given one.

func ToDiagnostic

func ToDiagnostic(f sast.Finding, docText string, m SeverityMapping) (protocol.Diagnostic, bool)

ToDiagnostic converts one Rego finding into an LSP diagnostic against the current text of the document it belongs to.

Returns false when the finding cannot be placed in the current text, which happens when the user has edited above it since the scan. Dropping is deliberate: a stale finding rendered confidently on the wrong line is worse than a missing one, and the document is re-analysed moments later anyway.

func URIToPath

func URIToPath(uri string) (string, bool)

URIToPath converts a file:// URI to a local filesystem path.

Not merely stripping a prefix. Editors percent-encode spaces and non-ASCII, and on Windows send file:///c:/... with a leading slash before the drive letter. Getting either wrong produces a path that does not exist, and the resulting "file not found" points at the wrong thing entirely.

Types

type Config

type Config struct {
	// CLIVersion, Commit and BuildDate come from the ldflags-injected build
	// metadata and are reported in the handshake.
	CLIVersion string
	Commit     string
	BuildDate  string

	// Debounce is the quiet period after a change before analysis runs.
	Debounce time.Duration

	// MaxTotalBytes caps how much file content is held in memory at once.
	// Zero means unlimited, which is the CLI default and the wrong choice for a
	// long-lived process.
	MaxTotalBytes int64

	// Logf receives server-side log lines. Never stdout: that is the JSON-RPC
	// channel. Nil discards.
	Logf func(format string, args ...any)
}

Config is what the process supplies to the server.

type DiagnosticData

type DiagnosticData struct {
	FindingID        string `json:"findingId"`
	Tool             string `json:"tool"`
	RuleID           string `json:"ruleId,omitempty"`
	AnchorConfidence string `json:"anchorConfidence,omitempty"`
	FixAvailable     bool   `json:"fixAvailable,omitempty"`
	Suppressible     bool   `json:"suppressible"`

	// SCA is present only on dependency findings. It carries the package
	// coordinate and the resolved fix target so a code action can be built
	// without repeating the lookup.
	SCA *scaDiagnosticData `json:"sca,omitempty"`
}

DiagnosticData rides on Diagnostic.data and round-trips to the client untouched.

It carries what a code action needs so the action can be offered without a second lookup, and the anchor confidence so an approximate position can be rendered as one rather than implying precision the finding does not have.

type Document

type Document struct {
	URI        string
	LanguageID string
	Version    int
	Text       string
	// RelPath is the path relative to the workspace root, in slash form, which
	// is how the rule engine keys FileSet and FileContents.
	RelPath string
}

Document is one open editor buffer.

Text is the editor's version, which may differ from what is on disk: that is the entire point of holding it, because the user wants findings about what they are looking at, not about what they last saved.

type DocumentStore

type DocumentStore struct {
	// contains filtered or unexported fields
}

DocumentStore holds the open buffers.

Safe for concurrent use: the read loop mutates it on didOpen/didChange while scan goroutines read it.

func NewDocumentStore

func NewDocumentStore() *DocumentStore

func (*DocumentStore) All

func (s *DocumentStore) All() []Document

All returns a snapshot of every open document.

The Document values are copied, so a caller can read them without holding the lock and without racing the read loop's next update.

func (*DocumentStore) Close

func (s *DocumentStore) Close(uri string)

func (*DocumentStore) Get

func (s *DocumentStore) Get(uri string) (*Document, bool)

func (*DocumentStore) Languages

func (s *DocumentStore) Languages() []string

Languages returns the distinct language ids currently open, which is what decides the rule subset worth compiling.

func (*DocumentStore) Len

func (s *DocumentStore) Len() int

func (*DocumentStore) Open

func (s *DocumentStore) Open(uri, languageID string, version int, text, relPath string) *Document

func (*DocumentStore) Snapshot

func (s *DocumentStore) Snapshot() map[string]string

Snapshot returns relPath to text for every open document.

A copy, because the result is handed to an evaluation that runs concurrently with further edits, and the rule engine must see a consistent picture rather than a map changing underneath it.

func (*DocumentStore) Update

func (s *DocumentStore) Update(uri string, version int, text string) (*Document, bool)

Update replaces a document's text.

A change for a document that was never opened is ignored rather than synthesised: the client is required to send didOpen first, and inventing a document from a change would hide a client bug behind a plausible-looking buffer with no language id and no path.

type Scheduler

type Scheduler struct {
	// contains filtered or unexported fields
}

Scheduler decides when analysis runs and makes sure it can be stopped.

Three queues with different policies, because the triggers have genuinely different costs and expectations:

doc        didChange. Debounced, coalesced per URI, cheap rule kinds only,
           and structurally unable to touch the network.
save       didSave. Immediate, coalesced per URI, adds the expensive kinds.
workspace  an explicit scan or a watched-file change. One at a time.

The doc queue is the one users feel. It cancels the in-flight evaluation for a URI when a newer change arrives, because the result of analysing text the user has already replaced is worth nothing and finishing it delays the answer they do want.

func NewScheduler

func NewScheduler(debounce time.Duration) *Scheduler

func (*Scheduler) CancelDocument

func (s *Scheduler) CancelDocument(uri string)

CancelDocument stops queued and running work for a URI, which is what didClose does: nobody is looking at the result any more.

func (*Scheduler) CancelWorkspace

func (s *Scheduler) CancelWorkspace()

CancelWorkspace stops the running workspace scan, if any.

func (*Scheduler) Close

func (s *Scheduler) Close()

Close cancels everything and waits for it to stop.

Called on shutdown. Without the wait, the process can exit while an evaluation is mid-write to the connection, which the client sees as a truncated message rather than a clean shutdown.

func (*Scheduler) RunDocumentNow

func (s *Scheduler) RunDocumentNow(uri string, fn func(context.Context))

RunDocumentNow runs work for a document immediately, cancelling anything queued or running for it. This is the save path: the user has committed to the text, so waiting out a debounce would only add latency.

func (*Scheduler) RunWorkspace

func (s *Scheduler) RunWorkspace(fn func(context.Context)) <-chan struct{}

RunWorkspace starts a workspace scan, cancelling any scan already running.

At most one at a time. A workspace scan is minutes of work on a large repository, and running two concurrently would double memory and halve the throughput of both for no benefit.

Returns a channel closed when the scan finishes, so a caller that needs to wait can, without the scheduler holding a lock while it does.

func (*Scheduler) ScheduleDocument

func (s *Scheduler) ScheduleDocument(uri string, fn func(context.Context))

ScheduleDocument queues work for a document after the debounce interval.

A newer change for the same URI resets the timer and cancels any evaluation already running for it. fn receives a context that is cancelled on either.

func (*Scheduler) SetDebounce added in v3.100.0

func (s *Scheduler) SetDebounce(d time.Duration)

SetDebounce changes the quiet period for subsequent scheduling.

Timers already pending keep the interval they were created with. Rescheduling them would restart the clock for a change the user has already finished making, which is the opposite of what a shorter debounce was asked for.

func (*Scheduler) WorkspaceRunning

func (s *Scheduler) WorkspaceRunning() bool

WorkspaceRunning reports whether a workspace scan is in flight.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server implements the Vulnetix language server.

func NewServer

func NewServer(cfg Config) *Server

NewServer constructs a server. Serve wires it to a connection.

type Settings added in v3.100.0

type Settings struct {
	// Debounce is the quiet period after a keystroke before analysis runs.
	Debounce time.Duration
	// MemoryLimitBytes caps the document content held in memory.
	MemoryLimitBytes int64
	// LowAsHint renders low and info findings as Hint rather than Information.
	// Set from diagnostics.mapLowTo.
	LowAsHint bool
	// MinimumSeverity hides findings below this level. Empty means show all.
	MinimumSeverity string
	// ShowSuppressed publishes suppressed findings as hints instead of dropping
	// them.
	ShowSuppressed bool
	// MemoryPath overrides the location of the .vulnetix directory holding
	// memory.yaml. Empty means the workspace default.
	MemoryPath string
	// Suppressions are editor-level ignore rules, which take precedence over the
	// memory file.
	Suppressions []SuppressionSetting
	// SCA configures dependency analysis.
	SCA scaSettings
}

Settings is the validated view of the editor's `vulnetix.*` configuration.

Every field here has already been range-checked. That is the contract, not a convenience: the settings arrive from a user-editable JSON file over workspace/didChangeConfiguration, and a scanner that stops working because someone typed a negative number into settings.json is indistinguishable from a broken scanner. A value that cannot be used is logged and replaced with the default; it is never propagated and never disables analysis.

func DefaultSettings added in v3.100.0

func DefaultSettings() Settings

DefaultSettings is what the server runs with before the client says anything, and what any individual invalid value falls back to.

func ParseSettings added in v3.100.0

func ParseSettings(raw map[string]any, logf func(string, ...any)) Settings

ParseSettings converts the raw configuration object into validated settings.

raw is the value of the `vulnetix` key from initializationOptions or from workspace/didChangeConfiguration. Anything missing, wrongly typed or out of range falls back to its default and is reported through logf.

type SeverityMapping

type SeverityMapping struct {
	// LowAsHint renders low and info findings as Hint rather than Information.
	//
	// A Hint is a subtle underline with no Problems-panel entry, which some
	// people want for style-level rules and others experience as findings
	// silently vanishing. Hence a setting, defaulting off.
	LowAsHint bool
}

SeverityMapping controls how a Vulnetix severity becomes an LSP severity.

type SuppressionSetting added in v3.100.0

type SuppressionSetting struct {
	RuleID    string
	FindingID string
	FilePath  string
	Reason    string
}

SuppressionSetting is one editor-level ignore rule.

Directories

Path Synopsis
Package anchor resolves which line of a dependency manifest declares a given package.
Package anchor resolves which line of a dependency manifest declares a given package.
Package protocol is the Language Server Protocol subset the Vulnetix server speaks, plus the vulnetix/* extension methods.
Package protocol is the Language Server Protocol subset the Vulnetix server speaks, plus the vulnetix/* extension methods.
Package rangefix turns a line-only finding into an editor range.
Package rangefix turns a line-only finding into an editor range.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL