Embedding MQL

How to embed MQL queries in CLI scripts and Go code.

You can run MQL queries outside the interactive shell by embedding them in shell scripts or directly in application code. This lets you integrate Mondoo's query capabilities into CI pipelines, custom tooling, and automation workflows.

There are two approaches:

  • CLI: Invoke cnquery run from any script and consume JSON output.
  • Code embedding: Import the MQL execution engine as a Go library.

CLI

Run cnquery run with the --json flag to get machine-readable output you can pipe into other tools:

cnquery run local --json \
  -c "processes.where(command == /long test/).map(pid)"

You can pipe the JSON output to tools like jq for further processing:

cnquery run local --json \
  -c "packages.where(name == /ssl/i) { name version }" | jq '.[]'

Exit codes for shell scripting

By default, cnquery run and cnspec run exit with code 0 even if a query fails. To return a non-zero exit code on failure, add the --exit-1-on-failure flag:

cnspec run local -c "users.none(name == 'root')" --exit-1-on-failure
echo $?  # 1 if the assertion failed, 0 if it passed

This is essential for shell scripts and CI pipelines where you need to branch on query results:

#!/bin/bash

# Fail the pipeline if any SSH key is using a weak algorithm
if ! cnspec run local \
  -c "sshd.config.params['HostKeyAlgorithms'] != empty" \
  --exit-1-on-failure; then
  echo "SSH hardening check failed"
  exit 1
fi

For full policy scans with cnspec scan, the --risk-threshold flag controls exit codes. The scan returns exit code 1 if any asset's risk score meets or exceeds the threshold:

# Fail if any risk score is 90 or above (high/critical)
cnspec scan local --risk-threshold 90

# Always pass as long as the scan itself succeeds
cnspec scan local --risk-threshold 100
CommandFlagExit 0Exit 1
cnquery run / cnspec run--exit-1-on-failureAll queries passOne or more queries fail
cnspec scan--risk-threshold NAll risk scores below NAny risk score >= N

For more on CI/CD integration, see the CI/CD overview.

Code embedding

You can import the MQL execution engine as a Go dependency and run queries programmatically. This is useful for building custom security tools, integrating MQL into existing applications, or running queries as part of a larger Go workflow.

Here is a small example that uses MQL on top of JSON-data to run custom queries:

// Wrap the data in a llx.Data object for input and output.
// In MQL all data is typed. JSON-like data has the Dict type.
rawData := llx.DictData(jdata)

// Create a structure to attach the input data into the engine.
// This is optional, you could also just run MQL queries that retrieve
// data via providers.
// In our example we attach it to the `data` prop which is accessed via `props.data`
props := mqlc.SimpleProps{
	"data": rawData.Result().Data,
}

// We then formulate our full query relative to the input data
fullquery := "props.data." + query

// Providers create a runtime for us. This is used in the context of all
// queries and follows a given schema.
runtime := providers.DefaultRuntime()
conf := mqlc.NewConfig(runtime.Schema(), cnquery.DefaultFeatures)

// MQLc is the compiler for queries. Since MQL is strongly typed you can
// catch errors early.
bundle, err := mqlc.Compile(fullquery, props, conf)
if err != nil {
	log.Fatal().Err(err).Msg("failed to run query")
}

// Finally we execute the compiled code. You also have access to other lower-
// level functions for the execution, e.g. to increase concurrency and scalability.
res, err := mql.ExecuteCode(runtime, bundle, props, cnquery.DefaultFeatures)
if err != nil {
	log.Fatal().Err(err).Msg("failed to run query")
}

// MQL also comes with builtin reporter that can generate JSON or output data.
jres := strings.Builder{}
w := iox.IOWriter{Writer: &jres}
reporter.CodeBundleToJSON(bundle, res, &w)

On this page