Secure coding practices for tasks

Use secure coding practices when you write tasks and help protect your system.

The information in this topic covers basic coding practices for writing secure tasks. It is not an exhaustive list.

One of the methods attackers use to gain access to your systems is remote code execution, where by running an allowed script they gain access to other parts of the system and can make arbitrary changes. Because Bolt executes scripts across your infrastructure, it is important to be aware of certain vulnerabilities, and to code tasks in a way that guards against remote code execution.

Adding task metadata that validates input is one way to reduce vulnerability. When you require an enumerated (enum) or other non-string types, you prevent improper data from being entered. An arbitrary string parameter does not have this assurance.

For example, if your task has a parameter that selects from several operational modes that are passed to a shell command, instead of

String $mode = 'file'

use

Enum[file,directory,link,socket] $mode = file

If your task has a parameter that identifies a file on disk, ensure that a user can't specify a relative path that takes them into areas where they shouldn't be. Reject file names that have slashes.

Instead of

String $path

use

Pattern[/\A[^\/\\]*\z/] $path

In addition to these task restrictions, different scripting languages each have their own ways to validate user input.

PowerShell

In PowerShell, code injection exploits calls that specifically evaluate code. Do not call Invoke-Expression or Add-Type with user input. These commands evaluate strings as C# code.

Reading sensitive files or overwriting critical files can be less obvious. If you plan to allow users to specify a file name or path, use Resolve-Path to verify that the path doesn't go outside the locations you expect the task to access. Use Split-Path -Parent $path to check that the resolved path has the desired path as a parent.

For more information, see PowerShell Scripting and Powershell's Security Guiding Principles.

Bash

In Bash and other command shells, shell command injection takes advantage of poor shell implementations. Put quotations marks around arguments to prevent the vulnerable shells from evaluating them.

Because the eval command evaluates all arguments with string substitution, avoid using it with user input; however you can use eval with sufficient quoting to prevent substituted variables from being executed.

Instead of

eval "echo $input"

use

eval "echo '$input'"

These are operating system-specific tools to validate file paths: realpath or readlink -f.

Python

In Python malicious code can be introduced through commands like eval, exec, os.system, os.popen, and subprocess.call with shell=True. Use subprocess.call with shell=False when you include user input in a command or escape variables.

Instead of

os.system('echo '+input)

use

subprocess.check_output(['echo', input])

Resolve file paths with os.realpath and confirm them to be within another path by looping over os.path.dirname and comparing to the desired path.

For more information on the vulnerabilities of Python or how to escape variables, see Kevin London's blog post on Dangerous Python Functions.

Ruby

In Ruby, command injection is introduced through commands like eval, exec, system, backtick (``) or %x() execution, or the Open3 module. You can safely call these functions with user input by passing the input as additional arguments instead of a single string.

Instead of

system("echo #{flag1} #{flag2}")

use

system('echo', flag1, flag2)

Resolve file paths with Pathname#realpath, and confirm them to be within another path by looping over Pathname#parent and comparing to the desired path.

For more information on securely passing user input, see the blog post Stop using backtick to run shell command in Ruby.