|
| 1 | +# Sysdig LSP – Unified Assistant Context & Repository Guidelines |
| 2 | + |
| 3 | +## 1. Project Overview |
| 4 | + |
| 5 | +**Sysdig LSP** is a Language Server Protocol (LSP) implementation written in Rust. It integrates container image vulnerability scanning and Infrastructure-as-Code (IaC) analysis directly into code editors (e.g. VS Code, Helix, Neovim). |
| 6 | + |
| 7 | +It is designed to detect issues early in the development workflow by scanning: |
| 8 | + |
| 9 | +* Dockerfiles |
| 10 | +* Docker Compose files |
| 11 | +* Kubernetes manifests |
| 12 | +* Other IaC files |
| 13 | + |
| 14 | +The server is built on top of the `tower-lsp` framework and integrates with Sysdig’s Secure backend via a dedicated scanner binary and HTTP APIs. |
| 15 | + |
| 16 | +### Key Features |
| 17 | + |
| 18 | +* Vulnerability scanning of base images and dependencies. |
| 19 | +* Code Lens support (e.g. “Scan base image” on `FROM` lines). |
| 20 | +* Layered analysis for container images. |
| 21 | +* Integration with Sysdig’s Secure backend APIs through a CLI scanner binary. |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## 2. Project Structure & Architecture |
| 26 | + |
| 27 | +The project follows a modular, three-layer, Hexagonal-like architecture that cleanly separates domain logic, application orchestration, and infrastructure concerns. |
| 28 | + |
| 29 | +### 2.1 Workspace & Modules |
| 30 | + |
| 31 | +* Rust workspace with entrypoint in `src/main.rs` (initializes `LSPServer` with `tower-lsp` and configures logging). |
| 32 | +* Library exports in `src/lib.rs`, which also enforces linting rules (denies `unwrap` / `expect` in production code). |
| 33 | +* LSP orchestration / use-cases live in `src/app`. |
| 34 | +* Domain types and business logic live in `src/domain`. |
| 35 | +* Adapters and integrations (infrastructure) live in `src/infra`. |
| 36 | +* Integration tests and shared fixtures live under `tests/`: |
| 37 | + * `tests/general.rs` |
| 38 | + * `tests/common.rs` |
| 39 | + * `tests/fixtures/` (sample Dockerfiles, scan results, etc.) |
| 40 | +* Documentation for user-facing capabilities is under `docs/features/`. |
| 41 | +* Build tooling and shortcuts are defined in `Justfile` and `flake.nix`. |
| 42 | + |
| 43 | +### 2.2 Domain Layer (`src/domain/`) |
| 44 | + |
| 45 | +The domain layer contains pure business logic and domain models. |
| 46 | + |
| 47 | +Key module: |
| 48 | + |
| 49 | +* `scanresult/`: defines core entities and value objects: |
| 50 | + * `ScanResult`: core aggregate representing a full scan result. |
| 51 | + * `Vulnerability`: CVE, severity, package details, etc. |
| 52 | + * `Package`: name, version, package type. |
| 53 | + * `Layer`: container image layer information. |
| 54 | + * `Policy`: policy evaluation results. |
| 55 | + * Value objects such as `Severity`, `Architecture`, `OperatingSystem`. |
| 56 | + |
| 57 | +### 2.3 Application Layer (`src/app/`) |
| 58 | + |
| 59 | +The application layer orchestrates domain and infrastructure components and implements LSP-specific behavior. |
| 60 | + |
| 61 | +Key components: |
| 62 | + |
| 63 | +* **`LSPServer` (`lsp_server/`)** – main LSP implementation built on `tower-lsp`: |
| 64 | + * `lsp_server_inner.rs`: core LSP protocol handlers (initialize, text sync, code lenses, commands, diagnostics, hover, etc.). |
| 65 | + * `commands/`: concrete LSP command implementations (e.g. `scan_base_image`, `build_and_scan`). |
| 66 | + * `command_generator.rs`: generates Code Lens entries and associated commands. |
| 67 | + * `supported_commands.rs`: registry of available commands exposed to the client. |
| 68 | +* **`LspInteractor`** – manages communication with the LSP client and document state. |
| 69 | +* **`ImageScanner`** – trait for scanning container images (implemented by infrastructure components). |
| 70 | +* **`ImageBuilder`** – trait for building Docker images. |
| 71 | +* **`DocumentDatabase` (`document_database.rs`)** – in-memory store for: |
| 72 | + * Document text |
| 73 | + * Diagnostics (LSP warnings/errors for vulnerabilities) |
| 74 | + * Hover documentation (detailed vulnerability explanations) |
| 75 | +* **`markdown/`** – formats scan results into Markdown tables for display in editors. |
| 76 | +* **`ComponentFactory`** – abstract factory for dependency injection and component creation. |
| 77 | + |
| 78 | +### 2.4 Infrastructure Layer (`src/infra/`) |
| 79 | + |
| 80 | +The infrastructure layer implements technical concerns and external integrations. |
| 81 | + |
| 82 | +Key components: |
| 83 | + |
| 84 | +* **`SysdigImageScanner`** |
| 85 | + * Integrates with the Sysdig CLI scanner binary and Sysdig Secure backend. |
| 86 | + * Downloads and manages scanner binary versions. |
| 87 | + * Parses JSON scan results (e.g. via `sysdig_image_scanner_json_scan_result_v1.rs`). |
| 88 | + |
| 89 | +* **`DockerImageBuilder`** |
| 90 | + * Builds container images using Bollard (Docker API client). |
| 91 | + |
| 92 | +* **Dockerfile / Compose AST Parsers** |
| 93 | + * Parse Dockerfiles to extract image references from `FROM` instructions (including multi-stage builds). |
| 94 | + * Parse Docker Compose YAML (e.g. service `image:` fields). |
| 95 | + * Handle complex scenarios such as build args and multi-platform images. |
| 96 | + * Implemented via modules like `ast_parser.rs`. |
| 97 | + |
| 98 | +* **`ScannerBinaryManager`** |
| 99 | + * Downloads the Sysdig CLI scanner binary on demand. |
| 100 | + * Caches binaries and checks GitHub releases for the latest version compatible with the current platform. |
| 101 | + |
| 102 | +* **`LSPLogger`** |
| 103 | + * `tracing` subscriber that logs diagnostics and events to the LSP client or stderr. |
| 104 | + |
| 105 | +* **`ConcreteComponentFactory`** |
| 106 | + * Production wiring of dependencies implementing the `ComponentFactory` trait. |
| 107 | + |
| 108 | +### 2.5 LSP Protocol Flow |
| 109 | + |
| 110 | +The high-level LSP flow is: |
| 111 | + |
| 112 | +1. **Initialize** – Client sends configuration (e.g. `api_url`, `api_token`) via `initializationOptions`. |
| 113 | +2. **`didOpen` / `didChange`** – Document updates trigger parsing and analysis. |
| 114 | +3. **`codeLens`** – The server generates “Scan base image” code lenses on relevant lines (e.g. Dockerfile `FROM` instructions). |
| 115 | +4. **`executeCommand`** – Clicking a lens triggers commands like `scan_base_image` or `build_and_scan`. |
| 116 | +5. **`publishDiagnostics`** – Vulnerability findings are sent as diagnostics to the editor. |
| 117 | +6. **`hover`** – Hovering on diagnostics or vulnerable elements shows detailed vulnerability information. |
| 118 | + |
| 119 | +### 2.6 Document State Management |
| 120 | + |
| 121 | +Document state is managed in-memory via `InMemoryDocumentDatabase` (an implementation of `DocumentDatabase`), maintaining per-document: |
| 122 | +1. Raw document text. |
| 123 | +2. Diagnostics with vulnerability details. |
| 124 | +3. Pre-computed hover documentation. |
| 125 | + |
| 126 | +This allows the LSP to provide rich, contextual information without re-running scans on every request. |
| 127 | + |
| 128 | +--- |
| 129 | + |
| 130 | +## 3. Development Environment & Tooling |
| 131 | + |
| 132 | +### 3.1 Nix & Development Shell |
| 133 | + |
| 134 | +* `nix develop` – enter a reproducible development shell with the exact Rust toolchain and dependencies required by the project, as defined in `flake.nix`. You can assume the user already started the development shell. |
| 135 | + |
| 136 | +### 3.2 Build Commands |
| 137 | + |
| 138 | +* `cargo build` – build the server in debug mode. |
| 139 | +* `cargo build --release` – build an optimized release binary. |
| 140 | +* `nix build .#sysdig-lsp` – Nix-based build, with cross targets available (e.g. CI or other architectures). |
| 141 | +* Cross-compilation example: `nix build .#sysdig-lsp-linux-amd64`. |
| 142 | + |
| 143 | +The resulting `sysdig-lsp` binary is designed to be run by an LSP client (editor), rather than directly by users. |
| 144 | + |
| 145 | +### 3.3 Testing & Quality Commands |
| 146 | + |
| 147 | +The project uses `just` as a command runner to encapsulate common workflows. |
| 148 | + |
| 149 | +* `just test` |
| 150 | + * Runs the test suite via `cargo nextest run` (primary test runner). |
| 151 | + * Some tests require the `SECURE_API_TOKEN` environment variable. |
| 152 | + |
| 153 | +* `just lint` |
| 154 | + * Runs `cargo check` and `cargo clippy` for quick static analysis. |
| 155 | + |
| 156 | +* `just fmt` |
| 157 | + * Runs `cargo fmt` according to `rustfmt.toml`. |
| 158 | + |
| 159 | +* `just fix` |
| 160 | + * Runs `cargo fix` and `cargo machete` / `cargo machete --fix` to clean up unused dependencies and minor issues. |
| 161 | + |
| 162 | +* `just watch` |
| 163 | + * Provides a watch mode to run tests (or other commands) on file changes. |
| 164 | + |
| 165 | +Additional helpful commands: |
| 166 | + |
| 167 | +* `cargo test -- --nocapture` – run tests with full output when debugging. |
| 168 | + |
| 169 | +### 3.4 Pre-commit Hooks |
| 170 | + |
| 171 | +Pre-commit hooks are configured in `.pre-commit-config.yaml` to run: |
| 172 | + |
| 173 | +* Formatting (`cargo fmt`). |
| 174 | +* `cargo check`. |
| 175 | +* `cargo clippy`. |
| 176 | + |
| 177 | +These should run cleanly before opening a PR. |
| 178 | +They are automatically executed before a commit is done. |
| 179 | +If they are not executed, you need to execute: `pre-commit install` to configure it. |
| 180 | +If any of the steps of the pre-commit fails for whatever reason, you need to understand that the commit was not created. |
| 181 | + |
| 182 | +--- |
| 183 | + |
| 184 | +## 4. Coding Style, Technologies & Design Patterns |
| 185 | + |
| 186 | +### 4.1 Languages & Key Libraries |
| 187 | + |
| 188 | +* **Language:** Rust (Edition 2024). |
| 189 | +* **LSP Framework:** `tower-lsp`. |
| 190 | +* **Async Runtime:** `tokio`. |
| 191 | +* **HTTP Client:** `reqwest`. |
| 192 | +* **Serialization:** `serde`. |
| 193 | +* **Logging:** `tracing` (plus `LSPLogger` integration). |
| 194 | +* **CLI Args:** `clap`. |
| 195 | +* **Testing Libraries:** `rstest`, `mockall`, `serial_test`, along with `cargo nextest`. |
| 196 | + |
| 197 | +### 4.2 Code Style & Naming Conventions |
| 198 | + |
| 199 | +* Use standard Rust formatting (`rustfmt`) with 4-space indentation. |
| 200 | +* **Naming:** |
| 201 | + * `snake_case` for modules and functions. |
| 202 | + * `CamelCase` for types. |
| 203 | + * `SCREAMING_SNAKE_CASE` for constants. |
| 204 | +* Import ordering uses `reorder_imports = true` in `rustfmt.toml`. |
| 205 | +* Prefer trait-based abstractions over concrete types for testability and clear architecture boundaries. |
| 206 | +* Keep public APIs documented and keep modules small, mirroring the `app` / `domain` / `infra` boundaries. |
| 207 | +* Use `tracing` for structured logging, sending logs to the LSP client or stderr via `LSPLogger`. |
| 208 | + |
| 209 | +### 4.3 Error Handling |
| 210 | + |
| 211 | +Error handling is intentionally strict: |
| 212 | + |
| 213 | +* **No `unwrap()` or `expect()` in non-test code.** |
| 214 | + * Enforced by clippy rules and `src/lib.rs` configuration. |
| 215 | +* Use `Result` types with explicit error propagation. |
| 216 | +* Prefer `thiserror` for custom error types with rich context. |
| 217 | +* Optionally use `anyhow::Context` style patterns for additional context at call sites. |
| 218 | +* Convert domain-level errors to appropriate LSP-facing errors at the application boundary. |
| 219 | + |
| 220 | +### 4.4 Dependency Injection via `ComponentFactory` |
| 221 | + |
| 222 | +The `ComponentFactory` trait centralizes creation of major application components and supports testing: |
| 223 | + |
| 224 | +* Receives configuration (e.g. `api_url`, `api_token`) from the client. |
| 225 | +* Produces `Components` such as: |
| 226 | + * `ImageScanner` implementations. |
| 227 | + * `ImageBuilder` implementations. |
| 228 | +* `ConcreteComponentFactory` wires real components in production. |
| 229 | +* Tests can provide mock factories to inject fake scanners/builders for deterministic behavior. |
| 230 | + |
| 231 | +### 4.5 Async / Await & Concurrency |
| 232 | + |
| 233 | +All I/O operations, including scanning, building, and LSP communication, are asynchronous using the `tokio` runtime. |
| 234 | + |
| 235 | +* Shared state within the LSP server uses `RwLock` (or similar primitives) to support concurrent reads with controlled writes. |
| 236 | + |
| 237 | +--- |
| 238 | + |
| 239 | +## 5. Testing Strategy & Guidelines |
| 240 | + |
| 241 | +### 5.1 Testing Strategy |
| 242 | + |
| 243 | +* Integration tests live in the `tests/` directory, using real fixtures (e.g. Dockerfiles, sample scan results). |
| 244 | +* Fixtures are stored under `tests/fixtures/`. |
| 245 | +* **`serial_test`** is used to prevent parallel execution conflicts (e.g. sharing global resources or temporary directories). |
| 246 | +* **`mockall`** is used for mocking traits like `ImageScanner` in unit tests. |
| 247 | +* `rstest` can be used for parameterized tests. |
| 248 | +* Environment: tests may require `SECURE_API_TOKEN` for scenarios that depend on authenticated scanning. |
| 249 | + |
| 250 | +### 5.2 Testing Guidelines |
| 251 | + |
| 252 | +* Primary test runner is `cargo nextest` (via `just test`). |
| 253 | +* Add integration coverage in `tests/*.rs` and reuse fixtures in `tests/fixtures/`. |
| 254 | +* Name tests descriptively (`should_*` or behavior-oriented names). |
| 255 | +* Avoid direct network calls inside tests; prefer fixture-based or mocked interactions instead. |
| 256 | +* Add focused unit tests alongside modules using `#[cfg(test)]` for local behavior. |
| 257 | +* Broader flows and end-to-end LSP interactions belong in `tests/general.rs`. |
| 258 | +* For debugging, `cargo test -- --nocapture` can be used to see all test output. |
| 259 | +* Some tests, such as `infra::sysdig_image_scanner::tests::it_scans_popular_images_correctly_test`, are slow because they scan real container images. It is recommended to run them in a focused way or skip them in local development to speed up the feedback loop. |
| 260 | + |
| 261 | +--- |
| 262 | + |
| 263 | +## 6. Configuration, Security & Runtime Usage |
| 264 | + |
| 265 | +### 6.1 LSP Initialization & Client Configuration |
| 266 | + |
| 267 | +Clients configure Sysdig LSP via `initializationOptions` in the LSP initialize request, for example: |
| 268 | + |
| 269 | +```json |
| 270 | +{ |
| 271 | + "sysdig": { |
| 272 | + "api_url": "https://secure.sysdig.com", |
| 273 | + "api_token": "optional, falls back to SECURE_API_TOKEN env var" |
| 274 | + } |
| 275 | +} |
| 276 | +``` |
| 277 | + |
| 278 | +Key points: |
| 279 | +* `api_url` should be validated and not hard-coded to environment-specific endpoints in code. |
| 280 | +* `api_token` is optional; if absent, the server falls back to the `SECURE_API_TOKEN` environment variable. |
| 281 | + |
| 282 | +### 6.2 Security & Secrets |
| 283 | + |
| 284 | +* Do **not** commit API tokens or other secrets to the repository. |
| 285 | +* Prefer environment variables (e.g. `SECURE_API_TOKEN`) or editor initialization options (`sysdig.api_token`). |
| 286 | +* Always validate URLs provided via configuration (`sysdig.api_url`). |
| 287 | + |
| 288 | +### 6.3 Supported Usage Pattern |
| 289 | + |
| 290 | +* The `sysdig-lsp` binary is not meant to be run manually; it is launched and driven by an LSP client (such as VS Code, Helix, or Neovim) that speaks the Language Server Protocol. |
| 291 | + |
| 292 | +--- |
| 293 | + |
| 294 | +## 7. Commit & Pull Request Guidelines |
| 295 | + |
| 296 | +To keep history clean and reviews manageable: |
| 297 | + |
| 298 | +* Use conventional-style commits similar to existing history, e.g.: |
| 299 | + * `feat(scope): message` |
| 300 | + * `fix(scope): message` |
| 301 | + * `refactor: message` |
| 302 | +* Before opening a commit, run at least: |
| 303 | + * `just fmt` |
| 304 | + * `just lint` |
| 305 | + * `just test` |
| 306 | + * Any relevant `nix build` invocations when touching build tooling. |
| 307 | + * (You can assume they are executed before the commit is created, see Section 3.4) |
| 308 | +* Keep commits scoped and reversible; smaller, reviewable PRs are preferred over large, monolithic changes. |
| 309 | +* You must also modify AGENTS.md and README.md if applicable for any change you create, so both files are in sync with the project and the documentation does not become obsolete. |
0 commit comments