Files
glint/ROADMAP.md
T
k3nny f79c64cd44
ci / vet, staticcheck, test, build (push) Failing after 2m32s
release / Build and publish release (push) Successful in 1m17s
feat(security): security hardening, proxy support, and GL045 HTTP include warning
Security fixes:
- Path traversal guard in include: local: — paths with ../ that escape
  the repo root are rejected instead of reading arbitrary host files
- HTTP timeout (30 s) on all fetcher requests to prevent indefinite hangs
- Response size cap (10 MiB) via io.LimitReader to prevent memory exhaustion
- Cache directory and file permissions tightened to 0700/0600
- LSP Content-Length cap (64 MiB) to guard against DoS from a malicious client

New feature:
- --proxy flag on check, graph, and lsp subcommands; also proxy: key in
  .glint.yml; overrides system HTTP_PROXY / HTTPS_PROXY env vars when set;
  cmdGraph and cmdLSP now also load .glint.yml for proxy/token/url fallbacks

New lint rule:
- GL045 (Warning): include: remote: using plain http:// instead of https://

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 21:52:57 +02:00

14 KiB
Raw Blame History

Roadmap

This document tracks planned improvements to glint. Items are grouped by theme, roughly in priority order within each group. Nothing here is a commitment — the tool is experimental and the list will shift as real usage surfaces better priorities.


Context simulation

Pass --branch, --tag, --source, or --var to glint check or glint graph to evaluate rules:if: expressions and only/except filters against a specific pipeline event.

  • Single-context simulation — shipped v0.2.0; --branch, --tag, --source, --var flags on both subcommands; jobs classified as active / manual / skipped
  • workflow:rules:variables: propagation — shipped post-v0.2.0; variables from the matching workflow rule entry injected into the evaluation context before job rules:if: expressions are evaluated
  • Expression evaluator: multi-line if: values — shipped post-v0.2.0; newlines in block-scalar and folded YAML if: values treated as whitespace
  • Expression evaluator: ${VAR} syntax — shipped post-v0.2.0; ${CI_COMMIT_BRANCH} equivalent to $CI_COMMIT_BRANCH everywhere
  • Expression evaluator: regex flags — shipped post-v0.2.0; /pattern/i, /pattern/m, /pattern/s supported
  • Expression evaluator: variable as regex RHS — shipped post-v0.2.0; $BRANCH =~ $PATTERN where $PATTERN holds a /regex/ string evaluates correctly
  • Expression evaluator: bare true/false and integer literals — shipped post-v0.2.0; $FLAG == true, $COUNT == 4 compare as decimal strings matching GitLab CI behaviour
  • Implicit default context — shipped v0.2.11; defaults to --branch main --source push when no context flag is given, so rules:if: is always evaluated
  • --list-vars debug flag — shipped v0.2.11; prints sorted KEY=VALUE of all collected pipeline variables (root file + includes + workflow-rule union + effective context) to stderr
  • Variable expansion — shipped v0.2.13; $VAR/${VAR} references within variable values expanded after all sources are merged; transitive chains resolved; visible in --list-vars
  • Non-string scalar variables — shipped v0.2.13; BUILD: true, RETRIES: 3 rendered and injected correctly instead of being silently dropped
  • YAML \/ escape in double-quoted strings — shipped v0.2.13; regex patterns like /^us\// in double-quoted if: blocks no longer cause a parse error
  • Workflow rule strict evaluation — shipped v0.2.14; unparseable if: skips the rule instead of matching everything; prevents wrong variables being injected
  • Single = operator — shipped v0.2.14; bare = accepted as alias for == in rules:if: expressions
  • rules:changes: evaluation — shipped v0.2.21; --changes PATH and --changes-from REF flags; doublestar glob matching (* within segment, ** across segments); permissive when no file list provided
  • Multi-context simulation — shipped v0.2.22; --context KEY=VALUE[,...]; repeatable; prints a comparison table of active/manual/skipped/blocked per job across all contexts
  • Context-scoped linting — shipped v0.2.23; jobs evaluated as JobSkipped in the supplied context are excluded from needs:/dependencies: cross-checks (GL027GL031) to eliminate false-positive errors for conditionally-gated jobs

Lint coverage

The current rule set covers the most common sources of broken pipelines. These are the gaps most likely to matter in practice.

  • Variable reference validation (GL032) — shipped v0.2.11; warns when a rules:if: expression references $VAR / ${VAR} not declared anywhere in pipeline YAML; predefined GitLab namespaces (CI_*, GITLAB_*, …) exempt; variables from included files are also considered
  • rules:if: static reachability (GL033) — shipped v0.2.15; warns when every rule in a job's rules: block has when: never, making the job permanently excluded from any pipeline run; no if: evaluation required
  • services: validation (GL034) — shipped v0.2.16; map form requires name; alias must be a valid DNS label
  • rules:changes / rules:exists absolute path detection (GL035) — shipped v0.2.16; warns when a path starts with /; GitLab CI paths are always relative to the repo root
  • timeout format validation (GL036) — shipped v0.2.16; validates job-level and default.timeout against recognised GitLab CI duration strings
  • id_tokens: / secrets: required-key checks (GL037, GL038) — shipped v0.2.16; id_tokens entries must have aud; secrets entries must declare a provider
  • pages:publish + artifacts.paths consistency (GL039) — shipped v0.2.16; warns when the publish directory is missing from artifacts.paths
  • Duplicate stage names (GL040) — shipped v0.2.16; warns when a stage appears more than once in stages:
  • cache:key:files must be exact paths (GL041) — shipped v0.2.16; warns when entries look like glob patterns
  • Unreachable jobs — covered by GL033 (shipped v0.2.15); every-when:never rules block is statically dead
  • inherit: completeness (GL043) — shipped v0.2.20; warns when inherit: default: is declared but there's no default: block, or list form names fields not set in default:

Include resolution

  • include: local: full resolution — shipped v0.2.0; local files are read from disk, recursively resolved, and merged before linting
  • include: remote: (URL) — shipped post-v0.2.0; plain HTTPS URLs are fetched (unauthenticated), parsed, and merged; sub-includes are resolved recursively; unreachable URLs emit [WARNING] and linting continues
  • Recursive include depth limit — shipped v0.2.17; depth capped at 100 (matching GitLab); project/component includes now tracked in visited set to prevent cross-file cycles
  • Offline mode / cache — shipped v0.2.17; --cache-dir DIR persists fetched templates; --offline serves from cache only; default cache dir (~/.cache/glint) used automatically with --offline
  • include: inputs: — shipped v0.2.17; $[[ inputs.KEY ]] and $[[ inputs.KEY | default(…) ]] placeholders in fetched component YAML are substituted from the include's with: block before parsing

Output formats

  • JSON (--format json) — shipped v0.2.18; machine-readable findings with stable schema (version 1)
  • SARIF (--format sarif) — shipped v0.2.18; SARIF 2.1.0; consumed natively by GitHub Code Scanning and GitLab SAST
  • JUnit XML (--format junit) — shipped v0.2.18; lets CI pipelines publish lint results as a test report artifact
  • GitHub annotation format (--format github) — shipped v0.2.18; emits ::error file=…,line=…,title=RULE::message lines so findings appear as inline comments in PR diffs

Pipeline graph improvements

The SVG renderer and terminal tree cover the basic layout. These would bring it closer to GitLab's full interactive view.

  • Terminal job tree — shipped v0.2.0 as glint graph tree; stages as branches, jobs as leaves, context-aware annotations
  • glint graph includes shows jobs per file — shipped post-v0.2.0; each include node shows the jobs it defines as dashed-arrow rounded nodes in a distinct style
  • Multi-job connector accuracy — shipped v0.2.25; classic mode uses a bus-bar pattern (vertical rail at the midpoint + horizontal stubs per job) instead of a single center-to-center line, so uneven columns look correct
  • Job tooltip / detail panel — shipped v0.2.25; each chip is wrapped in <g data-job="…"><title>…</title><desc>…</desc> — SVG viewers show stage, when, image, and needs on hover; HTML output uses the data for the sidebar
  • when: on_failure visual distinction — shipped v0.2.25; dashed chip border + X-mark icon + red circle (#d9534f); legend entry added; Mermaid on_failure class wired
  • Blocked / skipped state colouring — shipped v0.2.25; glint graph pipeline accepts context flags (--branch, --tag, etc.); jobs evaluated as skipped are greyed out (#868686) with dimmed text and no icon
  • Interactive HTML output — shipped v0.2.25; glint graph pipeline --format html writes a self-contained .html file with mouse pan/zoom and a click-to-open job-detail sidebar; no external dependencies
  • Mermaid pipeline output — shipped v0.2.25; glint graph pipeline --format mermaid prints a Mermaid flowchart to stdout (paste into mermaid.live)
  • Same-stage job ordering — shipped v0.2.26; jobs within a stage that have needs: between each other are placed in topological sub-columns (left-to-right by depth); stage header spans all sub-columns
  • Graph links rendered behind job chips — shipped v0.2.26; SVG connectors (Bézier curves and bus-bar stubs) are drawn before job chips so lines pass behind rectangles

Findings quality

  • File and line numbers on findings — shipped post-v0.2.0; every finding includes the source file and exact line of the job key; works across local includes, remote project templates, and fetched component templates
  • Ruff-style output format — shipped v0.2.11; findings follow file:line: RULEID [severity] message matching the convention used by ruff and other modern linters
  • needs: optional: true false-positive errors — shipped post-v0.2.0; optional missing needs are downgraded to [WARNING]
  • extends: jobs with missing script false errors — shipped post-v0.2.0; jobs using extends: that have no script after resolution emit [WARNING] (the script may come from an unfetchable remote base)
  • rules:if: static reachability (GL042) — shipped v0.2.20; warns when all rules:if: conditions evaluate to false given declared variable values (only fires when all referenced vars are declared in YAML)

CI / editor integration

  • GitLab CI template — shipped v0.2.28; templates/check.yml is a GitLab CI/CD Catalog component with spec: inputs for stage, file, version, allow_failure, and extra args; also usable as a plain local/remote include
  • GitHub Actions action — shipped v0.2.28; action.yml composite action downloads the glint Linux binary and runs glint check; mirror to GitHub as k3nny/glint to reference as uses: k3nny/glint@v0.2.28
  • Pre-commit hook — shipped v0.2.28; .pre-commit-hooks.yaml defines language: golang hook; pre-commit builds glint from source on first run and re-runs on staged .gitlab-ci.yml changes
  • LSP server — shipped v0.2.29; glint lsp runs a JSON-RPC 2.0 LSP server over stdin/stdout; textDocument/didOpen, didChange, didSave, didClose all publish diagnostics; rule IDs appear as the diagnostic code; include resolution is best-effort using env-var token and default cache dir
  • VS Code extension — shipped v0.2.30; editors/vscode/ TypeScript extension wraps glint lsp via vscode-languageclient; activates on YAML files and restricts LSP processing to **/.gitlab-ci.yml; configurable binary path via glint.executablePath; build with task ext-compile, package with task ext-package

Configuration

  • .glint.yml config file — shipped v0.2.19; ignore:, severity:, stages:, token:, url:, cache_dir:; searched from the pipeline directory up to the git root
  • Inline suppression comments — shipped v0.2.19; # glint: ignore GL007 before a job definition; comma/space-separated rules; # glint: ignore all wildcard
  • Proxy support — shipped v0.2.31; --proxy flag on check, graph, and lsp subcommands; also configurable via proxy: in .glint.yml; overrides HTTP_PROXY / HTTPS_PROXY env vars when set

Security & hardening

  • Path traversal guard for local includes — shipped v0.2.31; include: local: paths containing ../../ or similar sequences are rejected instead of reading files outside the repository root
  • HTTP timeout on remote fetches — shipped v0.2.31; all HTTP calls via the fetcher use a 30-second timeout to prevent indefinite hangs on slow or unresponsive servers
  • Unbounded response size cap — shipped v0.2.31; remote include and GitLab API responses are capped at 10 MiB; responses exceeding the limit are rejected to prevent memory exhaustion
  • Cache file permissions — shipped v0.2.31; cache directories are created with mode 0700 and cache files with mode 0600; previously used world-readable 0755/0644
  • GL045: HTTP remote include warning — shipped v0.2.31; warns when include: remote: uses a plain http:// URL; CI templates fetched unencrypted are at risk of tampering
  • LSP Content-Length DoS cap — shipped v0.2.31; glint lsp rejects messages with Content-Length exceeding 64 MiB to prevent memory exhaustion from a malicious client

Reliability and developer experience

  • Structured rule IDs — shipped post-v0.2.0; GL001GL031 assigned; GL032 added v0.2.11; GL033 added v0.2.15; GL034GL041 added v0.2.16; output formats added v0.2.18; GL042GL043 added v0.2.20; GL044 added v0.2.24; graph improvements shipped v0.2.25v0.2.26
  • glint explain <rule-id> — shipped v0.2.20; prints rule description, rationale, bad-YAML example, and fix; glint explain (no arg) lists all rules
  • Semantic versioning and first release — shipped v0.1.0 (2026-06-07)
  • Subcommand CLI — shipped v0.2.0 (2026-06-11); glint check / glint graph [mode] with ruff-style --help
  • Changelog automation — shipped v0.2.27; cliff.toml configures git-cliff to produce Keep-a-Changelogcompatible release notes from Conventional Commits; task changelog regenerates CHANGELOG.md, task changelog-next previews unreleased entries
  • Fuzz testing — shipped v0.2.27; FuzzParseBytes and FuzzSanitizeYAMLEscapes in internal/model/fuzz_test.go; seeds run as regular tests in CI; task fuzz runs them continuously (default 30 s)