Skip to main content

Using the CLI

Just looking for the code?
Modsurfer CLI is open-source, checkout the code on GitHub.

Modsurfer CLI is used to interact with the HTTP API or validate modules offline.

Download

Modsurfer CLI can be downloaded via:

  • the latest GitHub Release
  • the GitHub container registry (for Docker environments)
    • docker pull ghcr.io/dylibso/modsurfer:latest

Usage Overview

$ modsurfer -h
Usage: modsurfer [COMMAND]

Commands:
create Create a new entry for a module.
delete Delete a module and its versions.
get Get a module by its ID.
list List all modules, paginated by the `offset` and `limit` parameters or their defaults.
search Search for modules matching optional parameters.
validate Validate a module using a module checkfile.
yank Mark a module version as yanked (unavailable).
audit Return a list of modules which violate requirements in the provided checkfile.
generate Generate a starter checkfile from the given module.
diff Compare two modules.
help Print this message or the help of the given subcommand(s)

Options:
-h, --help Print help
-V, --version Print version

Environment Variables

Modsurfer CLI defaults can be overridden using environment variables. The following are available to use:

Variable NameDescriptionDefault Value
MODSURFER_BASE_URLUsed to connect to the Modsurfer HTTP API.http://localhost:1739
MODSURFER_RISK_LOWDefines the maximum value of the low risk cyclomatic complexity score used in validation.2500
MODSURFER_RISK_MEDIUMDefines the maximum value of the medium risk cyclomatic complexity score used in validation.50000
MODSURFER_RISK_HIGHDefines the maximum value of the high risk cyclomatic complexity score used in validation.4_294_967_295

Commands

Output Format

modsurfer get --id 1 --output-format=json

NOTE: for commands that print some output related to your modules, you can optionally pass an --output-format=json argument to override the default "table" output with JSON. This can be useful if you're using Modsurfer CLI within a script or pipeline.


create

Create a new entry for a module.

$ modsurfer create
modsurfer create -p path/to/module.wasm -l s3://where/module/lives/module.wasm \
-m user_id=12345 -m other=something

create options

-p, --path <path>                    a path on disk to a valid WebAssembly module
-m, --metadata <metadata> a repeatable key=value metadata entry, to add arbitrary context to a module
-l, --location <location> a valid URL to where this module should be located
-c, --check <check> a path on disk to a YAML checkfile which declares validation requirements
--output-format <output-format> set the output format of any command, supports `json` or `table` (default)
-h, --help Print help

You can provide metadata which is arbitrary key=value records. These will be indexed and searchable so you can tag and retrieve modules based on reference data according to your needs.

Validation

Validation can be run prior to creating a module in Modsurfer by passing another optional argument, -c with the path to a checkfile (e.g. mod.yaml). If validation fails, the module will not be inserted into Modsurfer and the failing properties will be printed to stdout along with some context:

┌────────┬──────────────────────────────────────────────────┬──────────┬──────────┬───────────────────┬────────────┐
│ Status │ Property │ Expected │ Actual │ Classification │ Severity │
╞════════╪══════════════════════════════════════════════════╪══════════╪══════════╪═══════════════════╪════════════╡
│ FAIL │ allow_wasi │ false │ true │ ABI Compatibility │ |||||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ complexity.max_risk │ <= low │ medium │ Resource Limit │ | │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ exports.exclude.main │ excluded │ included │ Security │ ||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ exports.include.bar │ included │ excluded │ ABI Compatibility │ |||||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ exports.max │ <= 100 │ 151 │ Security │ |||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.include.http_get │ included │ excluded │ ABI Compatibility │ |||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.include.log_message │ included │ excluded │ ABI Compatibility │ |||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.namespace.exclude.wasi_snapshot_preview1 │ excluded │ included │ ABI Compatibility │ |||||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.namespace.include.env │ included │ excluded │ ABI Compatibility │ |||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ size.max │ <= 4MB │ 4.4 MiB │ Resource Limit │ | │
└────────┴──────────────────────────────────────────────────┴──────────┴──────────┴───────────────────┴────────────┘

For an example mod.yaml, please see the validate command docs below.


delete

Delete a module and its versions.

$ modsurfer delete
# any number of --id arguments can be provided
modsurfer delete --id 1 --id 2 --id 3

delete options

--id <id>                        the numeric ID of a module entry in Modsurfer
--output-format <output-format> set the output format of any command, supports `json` or `table` (default)
-h, --help Print help

get

Get a module by its ID.

$ modsurfer get
modsurfer get --id 1

get options

--id <id>                        the numeric ID of a module entry in Modsurfer
--output-format <output-format> set the output format of any command, supports `json` or `table` (default)
-h, --help Print help

list

List all modules, paginated by the offset and limit parameters or their defaults.

$ modsurfer list
# by default, offset=0 and limit=50
modsurfer list --offset 1 --limit 20

list options

List all modules, paginated by the `offset` and `limit` parameters or their defaults.

Usage: modsurfer list [OPTIONS]

Options:
--offset <offset> the pagination offset by which modules are listed [default: 0]
--limit <limit> the maximum number of modules in a list of results [default: 50]
--output-format <output-format> set the output format of any command, supports `json` or `table` (default)
-h, --help Print help

Search for modules matching optional parameters.

$ modsurfer search
# find all modules that reference `fd_write` and are written in Rust
modsurfer search --function-name fd_write --source-language Rust

search options

--function-name <function-name>
adds a search parameter to match on `function-name
--module-name <module-name>
adds a search parameter to match on `module-name`
--source-language <source-language>
adds a search parameter to match on `source-language`
--hash <hash>
adds a search parameter to match on `hash`
--text <text>
adds a search parameter to match on `strings` extracted from a module
--offset <offset>
the pagination offset by which modules are listed [default: 0]
--limit <limit>
the maximum number of modules in a list of results [default: 50]
--output-format <output-format>
set the output format of any command, supports `json` or `table` (default)
-h, --help
Print help

validate

Validate a module using a module checkfile.

$ modsurfer validate
modsurfer validate -p spidermonkey.wasm -c mod.yaml

Checkfile

An example mod.yaml checkfile can include these properties and values:

mod.yaml
validate:
# simply require that a module can have WASI functionality or not
allow_wasi: false

# ensure that various imports and exports are included/exlcuded such that a module
# will run properly in any host environment
imports:
include:
# ensure these named functions are in the imports of this module
- log_message
- proc_exit

# further specify the function beyond its name
- namespace: env
name: http_get
params: [I32, I32]
results: [I32]
exclude:
- fd_write
namespace:
include:
- env
exclude:
# phasing out old APIs? exclude these from acceptable namespaces/module names
- some_future_deprecated_module_name
- wasi_snapshot_preview1

exports:
# only want exactly 2 functions exported: `_start` and `bar` for the host to call:
max: 2
# secure your modules by ensuring that there is no superfluous functionality hidden inside a binary
include:
- _start
- name: bar
params: []
results:
- I32
- I32
- I32
- I32
# and/or ensuring no unwanted functions to be exported.
exclude:
- name: init
results: []
- foo

# use a human-readable module size to prevent overly large binaries from running in your environment
size:
max: 4MB

# our Cyclomatic Complexity analysis can help prevent risk of CPU exhaustion from deteriorating
# your user experience and slowing down your system
# (override these low, medium, high optional values with environment variables $MODSURFER_RISK_{LOW,MEDIUM,HIGH})
complexity:
max_risk: low

validate options

-p, --path <path>                    a path on disk to a valid WebAssembly module
-c, --check <check> a path on disk to a YAML file which declares validation requirements [default: mod.yaml]
--output-format <output-format> set the output format of any command, supports `json` or `table` (default)
-h, --help Print help

If validation fails, the module will not be inserted into Modsurfer and the failing properties will be printed to stdout along with some context:

┌────────┬──────────────────────────────────────────────────┬──────────┬──────────┬───────────────────┬────────────┐
│ Status │ Property │ Expected │ Actual │ Classification │ Severity │
╞════════╪══════════════════════════════════════════════════╪══════════╪══════════╪═══════════════════╪════════════╡
│ FAIL │ allow_wasi │ false │ true │ ABI Compatibility │ |||||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ complexity.max_risk │ <= low │ medium │ Resource Limit │ | │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ exports.exclude.main │ excluded │ included │ Security │ ||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ exports.include.bar │ included │ excluded │ ABI Compatibility │ |||||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ exports.max │ <= 100 │ 151 │ Security │ |||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.include.http_get │ included │ excluded │ ABI Compatibility │ |||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.include.log_message │ included │ excluded │ ABI Compatibility │ |||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.namespace.exclude.wasi_snapshot_preview1 │ excluded │ included │ ABI Compatibility │ |||||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ imports.namespace.include.env │ included │ excluded │ ABI Compatibility │ |||||||| │
├────────┼──────────────────────────────────────────────────┼──────────┼──────────┼───────────────────┼────────────┤
│ FAIL │ size.max │ <= 4MB │ 4.4 MiB │ Resource Limit │ | │
└────────┴──────────────────────────────────────────────────┴──────────┴──────────┴───────────────────┴────────────┘

yank

Mark a module version as yanked (unavailable).

NOTE: This command is not yet supported. Please contact hello@dylib.so for more information.

$ modsurfer yank
modsurfer yank --id 1 --version 0.2.1

yank options

--id <id>                        the numeric ID of a module entry in Modsurfer
--version <version> the version of a module entry in Modsurfer (if no version exists, this command has no effect)
--output-format <output-format> set the output format of any command, supports `json` or `table` (default)
-h, --help Print help

audit

Return a list of modules which violate requirements in the provided checkfile.

NOTE: results are paginated, and by default limited to 50. Pass the --limit and --offset parameters to adjust this based on needs.

$ modsurfer audit
modsurfer audit --outcome fail -c mod.yaml

audit options

--outcome <outcome>              which type of expected outcome the audit should verify ('pass' or 'fail') [default: fail]
-c, --check <check> a path on disk to a YAML file which declares validation requirements [default: mod.yaml]
--offset <offset> the pagination offset by which modules are listed [default: 0]
--limit <limit> the maximum number of modules in a list of results [default: 50]
--output-format <output-format> set the output format of any command, supports `json` or `table` (default)
-h, --help Print help

generate

Generate a starter checkfile from the given module.

$ modsurfer generate
modsurfer generate -p spidermonkey.wasm -o mod.yaml

generate options

-p, --path <path>      a path on disk to a valid WebAssembly module
-o, --output <output> a path on disk to write a generated YAML checkfile [default: mod.yaml]
-h, --help Print help

diff

Compare two modules, either directly in binary format or via the Modsurfer API using module IDs.

Modules are compared based on their generated checkfile contents, not by the literal binary or converted .wat format. Any information the checkfile can contain will show up in the diff.

$ modsurfer diff
modsurfer diff a.wasm b.wasm # or using two `module_id`s from Modsurfer API
-   - name: run
+ - name: run_updated
- max: 4.8 MiB
+ max: 33.1 MiB

diff options

Arguments:
[module1] first module ID or path to .wasm
[module2] second module ID or path to .wasm

--with-context retain the surrounding unchnaged lines in the diff as context
-h, --help Print help

help

Print this message or the help of the given subcommand(s).