Classic confinement request: skarn

  • name: skarn

  • description: skarn is an AI coding session security scanner with built-in session search. It detects leaked credentials and the attack patterns that cause or exploit them in AI coding-assistant sessions (Claude Code, Gemini CLI, Codex CLI, Cursor, VS Code Copilot Chat), and lets you search, browse, and analyze those same sessions. It is a single static binary; sessions are scanned locally and nothing leaves the machine by default.

  • snapcraft: PRIVATE (closed-source). The snapcraft.yaml is generated per architecture from a template; the rendered manifest (amd64) is:

      name: skarn
      version: "0.20.0"
      summary: AI coding session security scanner with built-in session search
      license: Proprietary
      website: https://getskarn.com
      contact: https://getskarn.com
      grade: stable
      confinement: classic
      base: core24
      platforms:
        amd64:
          build-on: [amd64]
          build-for: [amd64]
      parts:
        skarn:
          plugin: dump
          source: payload
          organize:
            skarn: bin/skarn
      apps:
        skarn:
          command: bin/skarn
    
  • upstream: PRIVATE (closed-source, proprietary license)

  • upstream-relation: First-party. The snap publisher is the software’s developer and vendor.

  • supported-category: None of the currently listed categories fits precisely, so per the process this likely needs a senior reviewer to gather requirements. skarn is developer tooling that reads the local session and configuration files the developer’s AI coding assistants write under $HOME; the nearest listed category is “tools for local, non-root user driven configuration of development environments,” but the fit is imperfect - skarn audits that per-workspace assistant state rather than configuring it.

  • reasoning: skarn’s entire input surface is hidden files under the user’s home directory - the AI assistants’ local session stores and config: Claude Code under ~/.claude, Codex under ~/.codex, Cursor under ~/.cursor plus its state.vscdb under ~/.config, GitHub Copilot CLI under ~/.copilot, VS Code Copilot Chat’s state.vscdb under ~/.config, Gemini under ~/.gemini, and skarn’s own ~/.skarn.json, ~/.config/skarn, ~/.cache/skarn. Tested empirically under strict confinement: I built skarn as a strict-confinement snap with the home interface (core24), seeded a Claude Code session at ~/.claude/projects/, and ran it. With the home interface connected, “skarn search” and “skarn assess” scan 0 sessions (0 B) - the snap cannot see the session at all - while the identical binary run unconfined reads it (1 match, 1 session). Under strict confinement $HOME is remapped to the snap’s private directory and the snapd home interface excludes hidden dotfiles by design, so no assistant session store is reachable.

    personal-files could enumerate the default paths above, but it does not solve the problem: the set is user-extensible at runtime through documented overrides (SKARN_CLAUDE_DIRS, CLAUDE_CONFIG_DIR, COPILOT_HOME, and per-store sessions_path in ~/.skarn.json) and grows with every newly supported assistant, so a fixed personal-files allowlist is permanently incomplete and would silently miss sessions - an unacceptable failure mode for a security auditor whose whole purpose is to find leaked credentials in exactly those files - while requiring a fresh store review per assistant and per new override path.

    skarn is a read-only auditor over files the user already owns: it writes none of them, and its scan is offline by default (the only network calls are an explicit opt-in rule-feed fetch and an explicit license renewal, both user-initiated). The snap is closed-source (license: Proprietary).

YES I understand that strict confinement is generally preferred over classic.

YES I’ve tried the existing interfaces to make the snap to work under strict confinement.

This request has been added to the queue for review by the @reviewers team.

1 Like

Hey @msrdjan

I believe the provided reasoning does not justify the use of classic confinement, as personal-files will cover most users setups. Your application could possibly handle the remaining cases by detecting when these documented overrides are in place and asking the user to grant access to the directory via the FileChooser portal.

Thanks!

1 Like

Hi @jslarraz,

Thanks for the review, and for naming a concrete alternative. Two parts below: where you are right, and the three things that block the alternative.

You are right that personal-files covers the default layout. On a machine where nobody has moved anything, an enumerated declaration over ~/.claude, ~/.codex, ~/.cursor, ~/.copilot, ~/.gemini, ~/.kimi-code, ~/.config/Cursor/User, ~/.config/Code/User plus Skarn’s own ~/.skarn.json, ~/.config/skarn and ~/.cache/skarn does reach every session store Skarn parses today. I withdraw the “personal-files cannot work” framing from my original post. It can, for the default case. What follows is about the portal specifically.

  1. Skarn has no interactive context to show a portal dialog in.

Two of its three execution contexts have no user attached at all.

skarn guard is a pre-execution hook. It is not run by a person: it is spawned by the assistant itself (Claude Code, Codex CLI, Cursor, Copilot CLI, Gemini CLI) on every tool call, reads the pending call as JSON on stdin, and writes a verdict as JSON on stdout, inside a host-imposed timeout of 5 to 10 seconds. Gemini CLI’s hook protocol additionally requires that nothing but the final JSON object appears on stdout. Blocking that process on a FileChooser dialog either exceeds the host timeout, in which case the host fails the hook open and the guard silently stops protecting, or, on a headless or SSH session, never returns at all.

skarn check is a CI gate. It ships as a GitHub composite Action, an Azure Pipelines template, a pre-commit hook and container images. There is no session bus, no portal and no display in any of those, so a portal call is unreachable code on that path.

The interactive terminal case is the minority one, and Skarn’s Linux users are largely on servers, containers and WSL over SSH.

  1. The portal rewrites the paths, and the paths are the data.

Files returned through FileChooser arrive as document-portal handles under /run/user/$UID/doc/<id>/. Skarn does not only read those files, it reports and keys on their real locations. A finding that carries no secret (a prompt-injection or supply-chain finding) is fingerprinted as a digest of the rule id and the session file path, and that fingerprint is exactly what a user accepts into a baseline and what SARIF partialFingerprints publishes to a code-scanning dashboard. Skarn also resolves which repository a session belongs to by following the working directory recorded inside the session and probing the .git pointer beside it. Under document-portal handles the reported path is a per-invocation alias, so accepted findings stop matching on the next run and project attribution breaks. That is the difference between a suppression that holds and a scanner that re-raises everything the user already triaged.

  1. personal-files does not auto-connect, and a disconnected security scanner reports a clean machine.

The interface documentation is explicit that auto-connect is no, and that its purpose is read-only access to a directory where the snap is the clear owner of the target directory. Skarn is by definition never the owner: every directory it reads belongs to somebody else’s assistant. The shipping shape is therefore an install followed by roughly a dozen snap connect commands against other vendors’ dot-directories, and an unconnected plug is indistinguishable from an assistant that is not installed. A user who skips one gets “no findings” over a session store that was never opened.

One correction to my original post, which described Skarn as read-only. That is true of the session data, but skarn setup also wires the guard hook into the assistants’ own configuration: ~/.claude/settings.json, ~/.cursor/hooks.json, ~/.codex/hooks.json, ~/.gemini/settings.json and ~/.copilot/hooks/skarn.json. That is local, non-root, user-driven configuration of a development environment, which is one of the listed supported categories, and it is a write into five other applications’ directories rather than a read.

What I am asking for, and what I will do if the answer is still no.

If classic is grantable on points 1 and 2, that is my preference. Skarn stays a single static binary with no daemon, no background service and no network call by default; the only outbound requests it ever makes are an explicit opt-in detection-rule feed fetch and an explicit license renewal, both user-invoked.

If it is not, I will ship strict rather than abandon the channel, with the personal-files set enumerated above plus system-files for the four managed-policy paths skarn doctor reads (/etc/claude-code/managed-settings.json, /etc/codex/requirements.toml, /etc/cursor/hooks.json, /etc/github-copilot/policy.d/). I will drop the runtime root overrides (SKARN_CLAUDE_DIRS, CLAUDE_CONFIG_DIR, CODEX_HOME, COPILOT_HOME, KIMI_CODE_HOME and the sessions_path config key) under the snap rather than route them through a portal, document the snap as covering default layouts only, and make a denied root report as a coverage gap naming the exact snap connect command instead of being counted as an absent assistant.

In that case I would ask you two things:

  • whether the enumerated personal-files set is grantable as declared, and
  • whether auto-connection is available for it, since the value of the alternative depends on the user not having to connect a dozen plugs by hand.

Thanks.