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
83 changes: 83 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,86 @@
## 1.2.0

### Added

- **Much wider command coverage** across the knowledge base. New `infrastructure`
category and `IacKnowledge` plugin (`terraform`, `tofu`, `terragrunt`, `pulumi`,
`packer`, `ansible`/`-playbook`/`-galaxy`/`-vault`, `salt`, `puppet`, `chef`,
`vault`, `consul`, `nomad`). New `VcsKnowledge` plugin (`hg`, `svn`, `bzr`,
`fossil`, `cvs`, `jj`, `git-lfs`, `glab`, `tig`, `lazygit`) — the
version-control category previously held only `git`. New `ModernCliKnowledge`
plugin (`jq`, `yq`, `fd`, `rg`/`ripgrep`, `bat`, `eza`, `delta`, `fzf`, `sd`,
`gron`, …). Existing plugins gained many entries: network inspection/transfer
(`nmap`, `ip`, `ss`, `netstat`, `tcpdump`, `mtr`, `socat`, `lftp`, `tailscale`,
`wg`, `rclone`, `mc`, …) and more cloud CLIs (`doctl`, `flyctl`, `heroku`,
`vercel`, `wrangler`, `gsutil`, `bq`, `eksctl`, …); build/test tooling (`cmake`,
`ninja`, `bazel`, `just`, `pytest`, `jest`, `tsc`, `vite`, …); package/version
managers (`bun`, `pipx`, `poetry`, `uv`, `conda`, `rustup`, `asdf`, `nvm`, `nix`,
`snap`, `flatpak`, `twine`, …); containers/orchestration (`buildah`, `skopeo`,
`oc`, `minikube`, `kind`, `kubectx`, `k9s`, …); databases (`pgcli`, `duckdb`,
`cockroach`, `mongodump`, `dropdb`/`dropuser` → `highRisk`, …); editors (`code`,
`helix`, `kak`, `man`/`info`, …); crypto/secrets (`age`, `sops`, `cosign`,
`pass`, `certbot`, macOS `security`, …); more archive/compression tools; and
process/hardware inspection (`lsof`, `vmstat`, `free`, `lscpu`, `dmesg`, …).
- **Security-critical disk/system commands** that previously classified as
unknown (empty capabilities → "safe"). A new `DiskKnowledge` plugin covers
disk-format/wipe tools (`mkfs`, `mkfs.*`, `mke2fs`, `mkswap`, `wipefs`,
`blkdiscard` → `critical`), partitioners (`fdisk`, `gdisk`, `sgdisk`,
`cfdisk`, `parted` → `highRisk`), macOS `diskutil` (erase verbs refined to
`critical`) and secure-delete tools (`srm`, `wipe`). `SystemConfigKnowledge`
gained kernel-module (`modprobe`, `insmod`, `rmmod`, `kextload`/`kextunload`),
power/run-level (`shutdown`, `reboot`, `halt`, `poweroff`, `init`, `telinit`)
and privileged-config tools (`visudo`, `nvram`, `spctl`, `csrutil` — `disable`
refined to `critical`), plus more routine config names.
- `dd` now refines `of=/dev/...` (writing directly to a block device) to
`critical` with a `systemConfiguration` capability.

### Changed

- **Per-subcommand/argument refinement** for high-traffic multi-mode tools.
`kubectl`/`oc` split into read (`get`/`describe`) vs write (`apply`) vs
`delete` (highRisk) vs `exec`/`cp` (executePrograms). `terraform`/`tofu`/
`terragrunt`/`pulumi` keep `plan`/`preview` read-only while `apply` is
mediumRisk and `destroy` (or `-auto-approve`) is highRisk. `systemctl` is now
read-only for `status`/`show`/`list-*` and only attributes
`systemConfiguration` to state-changing verbs (`reboot`/`poweroff` highRisk).
`npm`/`pnpm`/`yarn`/`bun` `run`/`exec`/`dlx` attribute `executePrograms`;
`docker`/`podman` `rm`/`rmi`/`prune` attribute `deleteFilesystem`;
`aws`/`gcloud`/`az` raise risk on `rm`/`delete`/`destroy` tokens; `vault`
splits read vs write; `ansible-playbook --become` adds privilege escalation.
- `kill`/`pkill`/`killall` now flag the catastrophic forms — signalling PID 1
(init), `-1`/`0` (every process / the process group), a match-all pattern
(`pkill .`) or `-u root` — as `highRisk`, while a normal `kill <pid>` (incl.
`kill -1 <pid>` SIGHUP) stays safe.
- `find <path> -delete` is now classified by `DestructiveCommandDetector` —
`find / -delete` is `critical` (and denied), while a scoped `find . -delete`
stays a medium deletion. Recursive `chmod`/`chown`/`chgrp` on a filesystem
root or system directory (`chmod -R 777 /`) is now `highRisk`.
- `ShellExecutionDetector` now also flags **language interpreters running inline
code** — `python -c`, `node -e`/`-p`, `bun -e`, `perl -e`/`-E`, `ruby -e`,
`php -r`, `lua -e`, `osascript -e`, `Rscript -e`, `elixir -e`, `groovy -e` and
`deno eval` — as `highRisk` (matching `bash -c`), without flagging an
interpreter that merely runs a script file (`python app.py`).
- **`command -v`/`-V` lookups are no longer treated as executing their target.**
`command -v rm` (or `command -v mkfs`) resolves the name like `which`/`type` —
it now reports only `readFilesystem` instead of inheriting the target's
capabilities/risk, and `DestructiveCommandDetector` skips it (so
`command -v mkfs.ext4` is no longer a false `critical`). Actually running a
command through `command` (`command rm -rf /`) still looks through as before.
Adds a `WrapperSpec.lookupFlags` knob.
- Normalization resolves the Debian binary renames `batcat`→`bat`,
`fdfind`→`fd` and the package name `ripgrep`→`rg`.
- `DestructiveCommandDetector` now also classifies disk-format/wipe tools
(`mkfs`/`mkfs.*`/`wipefs`/…) and `dd of=/dev/...` as `critical`, looking
*through* wrapper commands (so `sudo mkfs.ext4 /dev/sda` is caught), and
treats whole-disk device nodes (`/dev/sd*`, `/dev/nvme*`, `/dev/disk*`, …) as
catastrophic targets. `CommandFamilies` gained a `diskDestructive` set and an
`isDiskDestructive` helper, and extended its `destructive` (`srm`, `wipe`,
`blkdiscard`), `downloaders` (`lftp`, `yt-dlp`, `youtube-dl`, `certutil`,
`bitsadmin`) and `privilege` (`gosu`, `run0`, `please`) sets.
- `RemoteExecDetector`'s raw-text fallback now recognises the additional
downloaders and the `pwsh`/`powershell`/`cmd` shells in `download | shell`
patterns.

## 1.1.0

### Tests
Expand Down
24 changes: 16 additions & 8 deletions lib/src/capabilities/command_knowledge_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,22 @@ final class CommandKnowledgeBase {
// Recurse into wrapped commands (sudo/env/xargs/...), bounded to avoid
// pathological inputs.
if (entry?.wrapper != null && depth < 4) {
final wrapped = _wrappedCommand(entry!.wrapper!, args);
if (wrapped != null) {
_collect(
wrapped.executable,
wrapped.arguments,
match,
depth: depth + 1,
);
final spec = entry!.wrapper!;
if (spec.lookupFlags.isNotEmpty && args.any(spec.lookupFlags.contains)) {
// A lookup form (`command -v foo`) resolves/prints the named command
// without executing it, so it does not inherit the target's
// capabilities — it only inspects the filesystem/PATH.
match.add(CommandCapability.readFilesystem);
} else {
final wrapped = _wrappedCommand(spec, args);
if (wrapped != null) {
_collect(
wrapped.executable,
wrapped.arguments,
match,
depth: depth + 1,
);
}
}
}
return depth == 0 ? entry : null;
Expand Down
11 changes: 11 additions & 0 deletions lib/src/capabilities/knowledge/command_knowledge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ enum KnowledgeCategory {
/// Container and orchestration tools (`docker`, `podman`, `kubectl`).
container,

/// Infrastructure-as-code and configuration-management tools (`terraform`,
/// `ansible`, `pulumi`, `vault`, …).
infrastructure,

/// Network clients and utilities (`curl`, `ssh`, `ping`, cloud CLIs).
network,

Expand Down Expand Up @@ -250,6 +254,7 @@ final class WrapperSpec {
const WrapperSpec({
this.skipLeadingFlags = true,
this.skipAssignments = false,
this.lookupFlags = const <String>{},
});

/// Whether leading `-`-prefixed option tokens are skipped before the wrapped
Expand All @@ -259,6 +264,12 @@ final class WrapperSpec {
/// Whether leading `NAME=VALUE` assignment tokens are skipped (as with
/// `env FOO=bar cmd`).
final bool skipAssignments;

/// Flags that turn the wrapper into a *lookup* rather than an execution, so
/// the named command is resolved/printed but **not** run (e.g. `command -v
/// foo` / `command -V foo`). When any of these is present the wrapped command
/// is not analysed; the invocation is treated as a read-only lookup.
final Set<String> lookupFlags;
}

/// An optional Dart hook for command logic that the declarative rules cannot
Expand Down
23 changes: 22 additions & 1 deletion lib/src/capabilities/knowledge/plugins/archive_knowledge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,28 @@ final class ArchiveKnowledge implements CommandKnowledgePlugin {

@override
List<CommandKnowledge> get entries => [
for (final tool in const ['tar', '7z', '7za', 'cpio', 'ar', 'pax'])
for (final tool in const [
'tar',
'7z',
'7za',
'7zr',
'cpio',
'ar',
'pax',
'rar',
'unrar',
'unar',
'lha',
'arj',
'zoo',
'cabextract',
'rpm2cpio',
'dpkg-deb',
'jar',
'genisoimage',
'mkisofs',
'xorriso',
])
CommandKnowledge(
executable: tool,
category: KnowledgeCategory.archive,
Expand Down
10 changes: 10 additions & 0 deletions lib/src/capabilities/knowledge/plugins/compression_knowledge.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,16 @@ final class CompressionKnowledge implements CommandKnowledgePlugin {
'uncompress',
'brotli',
'pigz',
'lzip',
'plzip',
'lrzip',
'lzop',
'lbzip2',
'pbzip2',
'pixz',
'zpaq',
'unlz4',
'unbrotli',
])
CommandKnowledge(
executable: tool,
Expand Down
118 changes: 118 additions & 0 deletions lib/src/capabilities/knowledge/plugins/container_knowledge.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import '../../../security/security_level.dart';
import '../../capability.dart';
import '../command_knowledge.dart';
import '../command_knowledge_plugin.dart';
import 'knowledge_builders.dart';

/// Knowledge about container engines and orchestration tools.
final class ContainerKnowledge implements CommandKnowledgePlugin {
Expand All @@ -10,6 +12,68 @@ final class ContainerKnowledge implements CommandKnowledgePlugin {
@override
String get name => 'container';

static const _ct = KnowledgeCategory.container;

// --- Kubernetes verb rules, shared by kubectl and oc ---
static const _k8sRead = SubcommandRule(
{
'get',
'describe',
'logs',
'top',
'explain',
'version',
'config',
'cluster-info',
'api-resources',
'api-versions',
'wait',
'auth',
'diff',
},
{CommandCapability.networkRead},
description: 'Read-only cluster query.',
);
static const _k8sWrite = SubcommandRule(
{
'apply',
'create',
'patch',
'replace',
'edit',
'scale',
'label',
'annotate',
'rollout',
'set',
'expose',
'autoscale',
'cordon',
'uncordon',
'drain',
'taint',
},
{CommandCapability.networkWrite},
risk: SecurityLevel.mediumRisk,
description: 'Mutates cluster state.',
);
static const _k8sDelete = SubcommandRule(
{'delete'},
{CommandCapability.networkWrite, CommandCapability.deleteFilesystem},
risk: SecurityLevel.highRisk,
description: 'Deletes cluster resources.',
);
static const _k8sExec = SubcommandRule(
{'exec', 'attach', 'cp', 'port-forward', 'proxy', 'run'},
{
CommandCapability.networkRead,
CommandCapability.networkWrite,
CommandCapability.executePrograms,
},
risk: SecurityLevel.mediumRisk,
description: 'Runs a process in / streams to a pod.',
);

@override
List<CommandKnowledge> get entries => [
for (final c in const ['docker', 'podman', 'nerdctl'])
Expand All @@ -28,8 +92,23 @@ final class ContainerKnowledge implements CommandKnowledgePlugin {
{CommandCapability.networkRead, CommandCapability.executePrograms},
description: 'Pulls images and/or runs containers.',
),
SubcommandRule(
{'rm', 'rmi', 'prune', 'kill', 'stop'},
{CommandCapability.deleteFilesystem},
description: 'Removes containers, images or volumes.',
),
],
),

// Kubernetes CLIs: read-only by default, escalating per verb.
for (final c in const ['kubectl', 'oc'])
CommandKnowledge(
executable: c,
category: _ct,
description: 'Kubernetes/OpenShift cluster CLI.',
baseCapabilities: const {CommandCapability.networkRead},
subcommands: const [_k8sRead, _k8sWrite, _k8sDelete, _k8sExec],
),
const CommandKnowledge(
executable: 'docker-compose',
category: KnowledgeCategory.container,
Expand All @@ -39,5 +118,44 @@ final class ContainerKnowledge implements CommandKnowledgePlugin {
CommandCapability.executePrograms,
},
),

// --- image builders / registry clients / runtimes (pull + run code) ---
...simpleEntries(
const [
'buildah',
'skopeo',
'img',
'kaniko',
'crictl',
'ctr',
'earthly',
'singularity',
'apptainer',
],
_ct,
const {CommandCapability.networkRead, CommandCapability.executePrograms},
),

// --- local cluster / VM managers (run code, manage local state) ---
...simpleEntries(
const ['minikube', 'kind', 'k3d', 'k3s', 'lima', 'colima', 'vagrant'],
_ct,
const {CommandCapability.networkRead, CommandCapability.executePrograms},
),

// --- context switchers / TUIs ---
...simpleEntries(
const ['kubectx', 'kubens'],
_ct,
const {
CommandCapability.readFilesystem,
CommandCapability.writeFilesystem,
},
),
...simpleEntries(
const ['k9s', 'dive', 'lazydocker'],
_ct,
const {CommandCapability.readFilesystem},
),
];
}
Loading