Skip to content

simonkowallik/irulescan

Repository files navigation

irulescan
security analyzer for iRules


tests container image size releases

irulescan is a tool to scan iRules for unexpected/unsafe expressions that may have undesirable effects like double substitution and additional issues.

irulescan would not exist without tclscan.

It is available as a docker/container image as well as a Github Action irulescan-action.

Usage

It is easiest to use irulescan as a container to scan your iRules, available via docker hub as well as ghcr.io.

The container will recursively scan files in the /scandir directory within the container and return results in JSON format. Only files with the (case insensitive) file extensions .tcl, .irul and .irule will be scanned unless overwritten by environment variable IRULESCAN_FILE_EXTENSIONS.

Command line

Scanning all iRules in a directory recursively ($PWD/tests/basic):

docker run --rm -v "$PWD/tests/basic:/scandir" simonkowallik/irulescan
[
  {
    "filepath": "dangerous.tcl",
    "warning": [
      "Unquoted expr at `1` in `expr 1 + $one`",
      "Unquoted expr at `+` in `expr 1 + $one`"
    ],
    "dangerous": ["Dangerous unquoted expr at `$one` in `expr 1 + $one`"]
  },
  {
    "filepath": "ok.tcl",
    "warning": [],
    "dangerous": []
  },
  {
    "filepath": "warning.tcl",
    "warning": [
      "Unquoted expr at `1` in `expr 1 + 1`",
      "Unquoted expr at `+` in `expr 1 + 1`",
      "Unquoted expr at `1` in `expr 1 + 1`"
    ],
    "dangerous": []
  }
]

If you plan to use irulescan frequently, consider adding a shell function to wrap the container execution and add it in your shell, eg. in your .bashrc or similar.

irulescan(){
  docker run --rm -iv "$PWD:$PWD" -w "$PWD" simonkowallik/irulescan:latest "${@:--help}";
}
# invoke ephemeral (--rm) container interactively (-i)
# bind mount (-v) PWD to the same path and use it as workdir (-w)
# pass any parameters, if none/empty, pass "help" ("${@:--help}")

Note

For brevity and readability the below examples use jq and yq to format the json output.

irulescan check --exclude-empty-findings ./tests/basic/ | yq -pjson -oyaml
- filepath: tests/basic/dangerous.tcl
  warning:
    - message: unsafe expression, use braces `{ .. }`
      issue_location: "1"
      context: expr 1 + $one
      line: 2
    - message: unsafe expression, use braces `{ .. }`
      issue_location: +
      context: expr 1 + $one
      line: 2
  dangerous:
    - message: dangerous unsafe expression, use braces `{ .. }`
      issue_location: $one
      context: expr 1 + $one
      line: 2
- filepath: tests/basic/warning.tcl
  warning:
    - message: unsafe expression, use braces `{ .. }`
      issue_location: "1"
      context: expr 1 + 1
      line: 1
    - message: unsafe expression, use braces `{ .. }`
      issue_location: +
      context: expr 1 + 1
      line: 1
    - message: unsafe expression, use braces `{ .. }`
      issue_location: "1"
      context: expr 1 + 1
      line: 1
  dangerous: []

Note

Please note the differences in "filepath" between the two invocation methods. This is important to consider when comparing a new scan to existing results. --exclude-empty-findings removes the entry for the file "ok.tcl" as it has no findings. cd tests/basic; irulescan check . | jq would have provided the same results as the docker command from the previous example.

When specifying a file explicitly, irulescan will try to scan the file regardless of the file extension.

irulescan check --no-warn tests/basic/dangerous.tcl
[{"filepath":"tests/basic/dangerous.tcl","warning":[],"dangerous":[{"message":"dangerous unsafe expression, use braces `{ .. }`","issue_location":"$one","context":"expr 1 + $one","line":2}]}]
irulescan --help
irulescan is a tool to scan iRules for unexpected/unsafe expressions that may have
undesirable effects like double substitution.
home: https://github.com/simonkowallik/irulescan

Usage: irulescan <COMMAND>

Commands:
 check     Scan all iRules in a directory recursively or the specified file or - for stdin
 checkref  Scan all iRules in reference file (JSON) and compare to reference
 parsestr  Parse given string or stdin
 mcpserver Run MCP server (HTTP stream transport)
 apiserver Run HTTP API server (OpenAPI v3)
 help      Print this message or the help of the given subcommand(s)

Options:
  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

API Server

The irulescan container tag :apiserver provides a simple OpenAPI server (previously Swagger) for scanning iRule code or multiple files.

Start the API server:

docker run -t --rm -p 8000:8000 simonkowallik/irulescan:apiserver

# equivalent using the :latest container:
docker run -t --rm -p 8000:8000 simonkowallik/irulescan:latest apiserver --listen 0.0.0.0:8000

Scanning a single file / iRule code:

curl -s http://localhost:8000/scan/ \
  --data-binary '@tests/basic/dangerous.tcl' | yq -pjson -oyaml
warning:
  - message: unsafe expression, use braces `{ .. }`
    issue_location: "1"
    context: expr 1 + $one
    line: 2
  - message: unsafe expression, use braces `{ .. }`
    issue_location: +
    context: expr 1 + $one
    line: 2
dangerous:
  - message: dangerous unsafe expression, use braces `{ .. }`
    issue_location: $one
    context: expr 1 + $one
    line: 2

Scanning multiple files:

curl -s http://localhost:8000/scanfiles/ \
  -F 'file=@tests/basic/warning.tcl' \
  -F 'file=@tests/basic/ok.tcl' \
  | yq -pjson -oyaml
- filepath: warning.tcl
  warning:
    - message: unsafe expression, use braces `{ .. }`
      issue_location: "1"
      context: expr 1 + 1
      line: 1
    - message: unsafe expression, use braces `{ .. }`
      issue_location: +
      context: expr 1 + 1
      line: 1
    - message: unsafe expression, use braces `{ .. }`
      issue_location: "1"
      context: expr 1 + 1
      line: 1
  dangerous: []
- filepath: ok.tcl
  warning: []
  dangerous: []

Here is a demo of the apiserver UI:

simonkowallik/irulescan:apiserver

MCP Server

Start the MCP server:

docker run -t --rm -p 8000:8000 simonkowallik/irulescan:mcpserver

# equivalent using the :latest container:
docker run -t --rm -p 8000:8000 simonkowallik/irulescan:latest mcpserver --listen 0.0.0.0:8000

With VSCode and Copilot, follow Use MCP servers in VS Code (Preview).

The below is a - per workspace - config example:

mkdir .vscode
cat <<'EOF' > .vscode/mcp.json
{
    "servers": {
        "irulescan": {
            "url": "http://localhost:8000"
        }
    }
}
EOF

Here is a demo of the mcpserver using VSCode:

simonkowallik/irulescan:apiserver

Additional resources

For safer authoring the VS Code iRules Extension is highly recommended:

Advanced topics

Using irulescan in CI

The container uses irulescan as the entry point with the default CMD set to check . which will scan /scandir recursively.

When using a CI system with custom mount points (eg. /custom/path), this can be easily supported by passing a new CMD like below. Note that, like outlined above, this will change the prefix of "filepath" for each iRule file.

docker run --rm \
  -v "$PWD/tests/tcl/:/custom/path" \
  simonkowallik/irulescan check /custom/path

Also have a look at GitHub Action: irulescan-action, which can be used when using GitHub.

Alternatives are using the API Server (see below) or invoke the irulescan container as outlined above. Consider creating a scan result file to compare against known findings and known iRules. Any change in findings could then be used to fail the CI run.

Check results against a reference (previous scan result)

The checkref command can be used to check each file listed in that reference file. The reference file is just a saved scan. See examples below.

irulescan/ $ cd ./tests/basic

Using a previous scan result (irulescan.json) and passing it to checkref via STDIN. If the check is successful, the exist code will be 0.

$ cat irulescan.json | irulescan checkref -
OK

$ echo $?
0

Re-checking irulescan_nowarn.json with no additional options.

$ irulescan checkref irulescan_nowarn.json
Failed reference check!
Extra in scan_results: .[1].warning.[0].({"message":"unsafe expression, use braces `{ .. }`","issue_location":"+","context":"expr 1 + 1","line":1})
Extra in scan_results: .[1].warning.[1].({"message":"unsafe expression, use braces `{ .. }`","issue_location":"1","context":"expr 1 + 1","line":1})
Extra in scan_results: .[1].warning.[2].({"message":"unsafe expression, use braces `{ .. }`","issue_location":"1","context":"expr 1 + 1","line":1})
Extra in scan_results: .[2].warning.[0].({"message":"unsafe expression, use braces `{ .. }`","issue_location":"+","context":"expr 1 + $one","line":2})
Extra in scan_results: .[2].warning.[1].({"message":"unsafe expression, use braces `{ .. }`","issue_location":"1","context":"expr 1 + $one","line":2})

$ echo $?
1

The above example produces a failed check as the scan results produce additional findings. The exit code is 1.

The previous scan result irulescan_nowarn.json does not contain any warnings, using the --no-warn option the check succeeds.

$ irulescan checkref --no-warn irulescan_nowarn.json
OK

$ echo $?
0

CLI exit codes

When using the irulescan check command:

  • Always exits with code 0 no matter what findings are produced
  • Errors lead to a non-zero exit code > 1 (eg. a crash)

When using the irulescan checkref command:

  • Exits with code 0 when the reference scan matches the current results
  • Exits with code 1 when the reference scan does NOT match the current results
  • Errors lead to a non-zero exit code > 1 (eg. a crash)

Environment Variables

irulescan supports several environment variables to customize its behavior across different usage modes (command line, API server, and MCP server).

Command Line and Container

For command line and container usage, you can use IRULESCAN_FILE_EXTENSIONS to specify which file extensions to scan. The default value is .tcl,.irul,.irule. File extensions are always case insensitive. File extensions should be comma-separated without spaces. This is particularly useful when working with custom file naming conventions or when you want to scan files with non-standard extensions.

export IRULESCAN_FILE_EXTENSIONS=".f5rule,.f5irule,.txt"
irulescan check ./my-rules/

# Use container with custom file extensions
docker run --rm -v "$PWD:/scandir" -e IRULESCAN_FILE_EXTENSIONS=".f5rule,.custom" simonkowallik/irulescan

API Server and MCP Server

For API server and MCP server modes, you can configure the listen address and logging level using IRULESCAN_LISTEN and IRULESCAN_LOG respectively.

The IRULESCAN_LISTEN variable defaults to 0.0.0.0:8000 and controls the address and port the server binds to. The environment variable has the highest priority and will overwrite the default listen address as well as any listen address specified as a command line option to either mcpserver or apiserver.

The IRULESCAN_LOG variable defaults to info and accepts standard log levels: trace, debug, info, warn, and error, where trace provides the most verbose output.

Packages

 
 
 

Contributors 2

  •  
  •  

Languages