diff --git a/workspace/skills/execline/SKILL.md b/workspace/skills/execline/SKILL.md new file mode 100644 index 000000000..80cc17b24 --- /dev/null +++ b/workspace/skills/execline/SKILL.md @@ -0,0 +1,273 @@ +# execline-hardening Skill + +Security hardening for agentic systems using execline instead of bash. + +## What is execline? + +execline is a minimal scripting language from [skarnet.org](https://skarnet.org/software/execline/) designed for security and simplicity. It uses **chain loading** - each command execs into the next one, rather than staying resident like a shell. + +## Installation + +```bash +# Debian/Ubuntu/Armbian +apt install execline + +# Alpine +apk add execline +``` + +## Key Concepts + +### Chain Loading +execline uses exec() heavily - each program runs, then execs into the next one: +``` +execlineb -c "nice -10 echo hello" +``` +This is more efficient than spawning a shell interpreter. + +### Whitespace is Whitespace +Newlines, spaces, and tabs are all treated the same - they're just word separators. + +### Blocks +Curly braces `{ }` create blocks to group commands with their arguments: +``` +foreground { echo hello } echo world +``` + +## Security Properties + +### What execline DOESN'T do: +- **Command substitution**: `$(cmd)`, `` `cmd` `` - passed literally, not executed +- **Shell control operators**: `&&`, `||`, `;` - these are just arguments +- **Pipes to shell**: `| sh`, `| bash` - not supported + +### What execline DOES do (differently from sh): +- **Variable substitution**: Uses a deliberate substitution mechanism, not shell-style `$VAR` +- This is a feature, not a bug - it provides predictable behavior + +## Variable Management + +execline has a deliberate variable system - no shell-style `$VAR` magic: + +### define - Define a literal substitution +```bash +define FOO hello +echo $FOO +# Output: hello +``` + +### importas - Import environment variable +```bash +importas home HOME +cd $home +ls +``` + +### backtick - Command output to variable +```bash +backtick DATE { date +%Y-%m-%d } +echo $DATE +# Output: today's date +``` + +**Note**: Unlike shell's `$(date)`, execline uses `backtick` which: +- Runs the command +- Captures stdout +- Stores it in an environment variable +- Then execs into the next command + +## Sequencing Commands + +### foreground - Run and wait +```bash +foreground { echo first } echo second +# Output: +# first +# second +``` + +### background - Run in background +```bash +background { long-running-task } echo done +# Starts task, immediately prints "done" +``` + +## Conditionals + +### if - Run if condition succeeds +```bash +if { test -f /etc/passwd } echo file exists +``` + +### if with negation +```bash +if -n { test -f /tmp/test } mkdir /tmp/test +# -n negates: if file DOESN'T exist, create it +``` + +### ifelse - If-else +```bash +ifelse { test -d $HOME } +{ echo "It's a directory" } +{ echo "Not a directory" } +``` + +## Loops + +### forx - Iterate over list +```bash +forx item { alpha beta gamma } echo $item +# Output: alpha, beta, gamma (each on separate line via foreground) +``` + +### forstdin - Read from stdin +```bash +echo -e "a\nb\nc" | forstdin line echo $line +``` + +## File Operations + +### elglob - File globbing +```bash +elglob files /etc/f* echo ${files} +# Lists all files in /etc starting with 'f' +``` + +### redirfd - Redirect file descriptors +```bash +redirfd -w 1 output.txt echo hello +# Redirect stdout (fd 1) to file + +redirfd -a 1 log.txt date +# Append to log + +redirfd -r 0 /dev/null cat +# Redirect stdin from /dev/null +``` + +### fdmove - Move file descriptors +```bash +fdmove -c 2 1 prog +# Duplicate fd 2 (stderr) to fd 1 (stdout) - stderr to stdout +``` + +## Example Scripts + +### Simple sequence +```bash +#!/bin/execlineb -P +importas home HOME +cd $home +ls +``` + +### Conditional file creation +```bash +#!/bin/execlineb -P +importas home HOME +if -n { test -d ${home}/.cache } +mkdir -p ${home}/.cache +echo "Cache directory ready" +``` + +### Loop and create files +```bash +#!/bin/execlineb -P +forx name { alpha beta gamma } +{ + touch /tmp/${name} +} +echo "Files created" +``` + +### Pipeline +```bash +#!/bin/execlineb -P +pipeline { ls /etc } +wc -l +# Count files in /etc +``` + +## Comparison: execline vs shell (sh/bash) + +| Feature | sh/bash | execline | +|---------|---------|----------| +| $VAR expansion | Yes | Yes (via substitution) | +| ${VAR} expansion | Yes | Yes | +| $(cmd) substitution | Yes | **No** - use backtick | +| `cmd` substitution | Yes | **No** | +| &&, \|\| | Yes | **No** - use if/foreground | +| ; | Yes | **No** - use foreground | +| Variable assignment | VAR=value | define VAR value | +| Command output | $(cmd) | backtick VAR { cmd } | +| Loops | for, while | forx, forstdin | +| Conditionals | if/then/else | if, ifelse | + +## Usage in picoclaw + +### Via ExeclineTool: +``` +Use the `execline` tool for commands that don't need shell features. +``` + +The ExeclineTool validates: +- No `&&`, `||` (use `if`, `foreground` instead) +- No pipes to shell (`| sh`, `| bash`) + +## Testing + +```bash +# Variable substitution works: +execlineb -c 'define FOO bar echo $FOO' +# Output: bar + +# Environment variables: +HOME=/tmp execlineb -c 'importas h HOME cd $h pwd' +# Output: /tmp + +# Command substitution is LITERAL (not executed): +execlineb -c 'echo $(whoami)' +# Output: $(whoami) +``` + +## Recommendations + +1. **Use execline** for scripts that don't need `$(cmd)` or `&&`/`||` +2. Use `if` instead of `&&`, `ifelse` instead of `if-then-else` +3. Use `backtick` instead of `$(...)` +4. Use `foreground` for sequential commands + +## When Exec Tool is Unavailable + +If the `exec` tool is disabled but you still need command execution: + +1. **Generate execline commands** for the user to execute manually +2. **Write scripts** that the user can save and run + +Example - Creating a script for the user: + +```bash +#!/usr/bin/execlineb -S0 +cd /home/infra +export HOME /home/infra +foreground { echo "Environment configured" } +``` + +The user saves this to a file and runs it. This gives you a way to help even without direct execution capabilities. + +## Positional Arguments + +execline scripts can handle command-line arguments using `-sN` flag: + +```bash +#!/usr/bin/execlineb -s0 +# $1 is first arg, $@ is all remaining args +echo "First: $1" +echo "All: $@" +``` + +- `-s0`: $1, $2, etc. work directly +- `-s1`: Shift after first argument +- `-sN`: Positionalize N arguments + +This is useful for wrapper scripts that delegate to other commands.