Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

## [Unreleased]

## [1.29.0] - 2026-06-24

### Added

- **`[scan] guarddog = true` — persistent project opt-in for the local malware scan.** GuardDog (#105) was enabled only via the ephemeral `KIT_GUARDDOG=1` env var. `kit check` now also honors a `guarddog = true` flag under `[scan]` in `.kit.toml` (best-effort config read; env var still works and takes precedence in spirit — either enables it). So the choice lives in committed project config, not a per-shell env var. The skip message points at both. (Foundation for an interactive `kit setup` prompt to write the flag — a follow-up.)

## [1.28.2] - 2026-06-24

### Changed
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sandstream-kit",
"version": "1.28.2",
"version": "1.29.0",
"description": "developer kit. zero LLM, local-first, multi-vault. one command from git clone to working dev environment.",
"license": "MIT",
"funding": "https://buymeacoffee.com/sandstream",
Expand Down
17 changes: 14 additions & 3 deletions src/check-security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -678,14 +678,25 @@ async function checkSocket(): Promise<SecurityCheckResult> {
*/
async function checkGuardDog(): Promise<SecurityCheckResult> {
const base = { category: "supply-chain", name: "guarddog (malware)" } as const;
const enabled = ["1", "true", "yes", "on"].includes(
const envEnabled = ["1", "true", "yes", "on"].includes(
(process.env.KIT_GUARDDOG ?? "").trim().toLowerCase(),
);
if (!enabled) {
// Persistent project opt-in via `.kit.toml [scan] guarddog = true` (best-effort
// config read) — so the choice lives in config, not just an ephemeral env var.
let cfgEnabled = false;
try {
const { loadConfig } = await import("./config.js");
const cfg = await loadConfig(resolve(process.cwd(), ".kit.toml"));
cfgEnabled = cfg.scan?.guarddog === true;
} catch {
// no/invalid config → env var is the only switch
}
if (!envEnabled && !cfgEnabled) {
return {
...base,
status: "skip",
detail: "opt-in — set KIT_GUARDDOG=1 to run local malware heuristics (needs semgrep)",
detail:
"opt-in — set `guarddog = true` under [scan] in .kit.toml (or KIT_GUARDDOG=1) to run local malware heuristics (needs semgrep)",
};
}

Expand Down
7 changes: 5 additions & 2 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,10 @@ export interface kitConfig {
context?: ContextConfig;
/** Install-time supply-chain triage settings (`kit supply-chain`). */
supply_chain?: { internal_scopes?: string[] };
/** `kit scan` settings — a tooling Infisical project to resolve scanner tokens (SNYK_TOKEN, …) from. */
scan?: { tooling?: { project_id?: string; env?: string } };
/** `kit scan` / `kit check` scanner settings. `tooling` = an Infisical project to
* resolve scanner tokens (SNYK_TOKEN, …) from; `guarddog` = enable the local
* behavioral-malware scan in `kit check` (persistent alt to KIT_GUARDDOG=1). */
scan?: { tooling?: { project_id?: string; env?: string }; guarddog?: boolean };
/**
* No-egress / air-gapped posture (declarative; see docs/AIR_GAP.md). Equivalent
* to the `KIT_*` env vars, but checked in so the enclave config is reproducible.
Expand Down Expand Up @@ -583,6 +585,7 @@ const kitConfigSchema = z
.object({ project_id: z.string().optional(), env: z.string().optional() })
.passthrough()
.optional(),
guarddog: z.boolean().optional(),
})
.passthrough()
.optional(),
Expand Down
Loading