Introduction: Why Command-Line DAST Matters

Welcome to the second lesson of our DAST course. In the previous lesson, we discussed the theory behind Dynamic Application Security Testing (DAST) and how it differs from static code analysis. Now, we are moving from theory to practice. In this lesson, we will use the OWASP Zed Attack Proxy (ZAP), one of the world's most popular security tools, to scan a vulnerable application.

While ZAP has a graphical user interface (GUI) that looks like a standard desktop program, security engineers often run it from the command line. This is crucial for automation. When you integrate security testing into a CI/CD pipeline (Continuous Integration/Continuous Deployment), you cannot manually click buttons. You need a script that can start the scanner, run the tests, and report the results automatically. Today, you will learn how to control ZAP entirely through its API using terminal commands.

Understanding ZAP's REST API Architecture

OWASP ZAP is designed to run as a daemon (a background process) that exposes a powerful REST API. This means you can control every aspect of the scanner — from spidering a website to generating reports — by sending simple HTTP requests. This architecture allows developers to write scripts in Python, Bash, or JavaScript to trigger security scans without ever opening the ZAP application window.

Starting ZAP in Daemon Mode on Your Own Device

If you are following along on your own machine, you need to launch ZAP in daemon mode before issuing any API commands. Daemon mode starts ZAP as a headless background process with no GUI. Locate the ZAP startup script in your installation directory and run the appropriate command for your operating system:

# Linux / macOS
./zap.sh -daemon -port 8080 -host 127.0.0.1

# Windows
zap.bat -daemon -port 8080 -host 127.0.0.1

The -daemon flag tells ZAP to run without a user interface, and -port 8080 sets the port its API server will listen on. Once this process is running, you can drive it entirely through curl commands exactly as shown throughout this lesson.

Note: In the CodeSignal environment, ZAP is already running in daemon mode in the background, so you do not need to start it yourself.

Anatomy of a ZAP API URL

To interact with the API, we will use curl, a command-line tool for transferring data with URLs. Every ZAP API request follows a consistent URL pattern. Before we make our first call, it is worth understanding what each segment of the URL means:

http://127.0.0.1:8080 / JSON / core / view / version /
|_____________________| |____| |____| |____| |_______|
        ①                 ②      ③      ④       ⑤
SegmentExampleMeaning
① Host & Porthttp://127.0.0.1:8080The address of the locally running ZAP API server
② FormatJSONThe response format — can be JSON, XML, or HTML
③ ComponentcoreThe ZAP module being addressed (e.g., spider, ascan, core)
④ Operation typeviewview reads information without changing state; action triggers a change in ZAP
⑤ Endpoint nameversionThe specific operation to invoke within that component

Since the ZAP API returns data in JSON format (JavaScript Object Notation), we will often pipe the output into Python's JSON tool (python3 -m json.tool) to make it readable.

First, let's verify that the ZAP server is running and reachable:

# Check if ZAP is reachable by asking for its version
curl -s http://127.0.0.1:8080/JSON/core/view/version/ | python3 -m json.tool

Output:

{
    "version": "2.14.0"
}

This command uses the view operation type on the core component to call the version endpoint — a read-only query that changes nothing. If you receive a version number in response, the scanner is active and ready to receive instructions.

Registering the Target Application

Before ZAP can scan an application, it needs to know that the application exists. ZAP organizes its data in a Sites Tree, which is essentially a map of all the websites and URLs it has interacted with. If ZAP has never visited your target application, the Sites Tree will be empty, and subsequent scan commands might fail or target the wrong domain.

To fix this, we force ZAP to "visit" the target URL once. We do this using the accessUrl endpoint. For this lesson, our target is a vulnerable "Pastebin" application running on http://localhost:3001.

# Access the URL to register it in ZAP's Sites Tree
curl -s "http://127.0.0.1:8080/JSON/core/action/accessUrl/?url=http://localhost:3001/" | python3 -m json.tool

Notice this URL uses the action operation type (not view), because we are telling ZAP to do something — make an outbound HTTP request to the target. The accessUrl endpoint is the specific action being triggered within the core component.

Output:

{
    "accessUrl": "OK"
}

By sending this request, ZAP makes a simple HTTP connection to the target. This places localhost:3001 into ZAP's memory, making it available for the more complex scanning actions we are about to perform.

Spidering: Automated URL Discovery

Now that ZAP is aware of the target, we need to discover the application's structure. A web application is made up of many pages, forms, and API endpoints. We cannot manually list them all, so we use a Spider. A Spider (or Crawler) visits the main URL, looks for all the links on that page, visits those links, and repeats the process until it has mapped out the entire application.

This step is generally "passive," meaning it does not launch attacks. It simply explores the site to build a map. We trigger the Spider using the spider/action/scan endpoint.

# Start the spider scan
# recurse=true means it will follow links deeper into the directory structure
curl -s "http://127.0.0.1:8080/JSON/spider/action/scan/?url=http://localhost:3001&recurse=true" | python3 -m json.tool

Output:

{
    "scan": "0"
}

The output gives us a Scan ID (in this case, "0"). Scanning takes time, so the API does not wait for it to finish. Instead, it starts the process in the background and gives you a Scan ID so you can check on it later. You must poll the status endpoint until the progress reaches 100%.

# Check the progress of scan ID 0
curl -s "http://127.0.0.1:8080/JSON/spider/view/status/?scanId=0" | python3 -m json.tool

Output:

{
    "status": "100"
}

Once the status reaches 100%, ZAP has a complete blueprint of the application's structure. This map is essential because the scanner can only test the vulnerabilities of pages and endpoints it has successfully discovered during this phase.

Active Scanning: Testing for Vulnerabilities

Once the Spider has finished mapping the application, we can begin the Active Scan. This is the core of DAST. During an Active Scan, ZAP takes the URLs discovered by the Spider and launches simulated attacks against them. It attempts to inject malicious data, such as SQL commands or script tags, into input fields and URL parameters to see how the server responds.

This process is aggressive. Unlike spidering, active scanning can modify data or trigger errors on the server. We start it using the ascan (Active Scan) endpoint.

# Start the active scan
curl -s "http://127.0.0.1:8080/JSON/ascan/action/scan/?url=http://localhost:3001&recurse=true" | python3 -m json.tool

Output:

{
    "scan": "1"
}

Just like the Spider, this returns a Scan ID. Active scans take much longer than spidering because ZAP is trying thousands of different attack patterns. You monitor the progress using the ascan/view/status endpoint.

# Check progress (repeat this until it says 100)
curl -s "http://127.0.0.1:8080/JSON/ascan/view/status/?scanId=1" | python3 -m json.tool

Output:

{
    "status": "100"
}
Reviewing Alerts: Understanding Your Results

When the Active Scan reaches 100%, the testing is complete. ZAP stores its findings as Alerts. An alert represents a specific vulnerability found at a specific URL. It includes details such as the risk level (High, Medium, Low), the parameter that was exploited, and evidence (like an error message returned by the server).

To view these results, we query the core/view/alerts endpoint.

# Fetch all alerts for our target base URL
curl -s "http://127.0.0.1:8080/JSON/core/view/alerts/?baseurl=http://localhost:3001" | python3 -m json.tool

Output:

{
    "alerts": [
        {
            "alert": "SQL Injection",
            "risk": "High",
            "url": "http://localhost:3001/api/snippets?search=test",
            "param": "search",
            "evidence": "SQL syntax error"
        },
        {
            "alert": "Cross-Site Scripting (Reflected)",
            "risk": "High",
            "url": "http://localhost:3001/snippet/1",
            "param": "title"
        }
    ]
}

In the output above, ZAP found two high-risk issues. It detected a SQL Injection vulnerability in the search parameter, likely because the application crashed or returned a database error. It also found a Cross-Site Scripting (XSS) vulnerability, meaning it successfully injected a script that the browser would execute. These details provide the exact location a developer needs to fix the code.

Generating a Human-Readable HTML Report

While JSON output is great for automated parsers, humans often prefer a readable document. If you are running these scans as part of a job, you will likely need to share the results with a manager or a development team. ZAP can generate HTML reports that summarize the findings in a clean, professional format.

We can trigger this report generation using the core/other/htmlreport endpoint. Instead of piping this to a JSON tool, we redirect the output > to a file.

# Generate and save an HTML report
curl -s "http://127.0.0.1:8080/OTHER/core/other/htmlreport/" > zap-report.html

This command creates a file named zap-report.html in your current directory. You can open this file in any web browser to see charts, risk summaries, and detailed descriptions of every vulnerability found during the scan. This is a standard artifact to include in build pipelines to prove that security testing was performed.

Summary and What's Next

In this lesson, you performed a complete DAST workflow using nothing but the command line. You learned how to verify the ZAP API status, register a target application, and use a Spider to map out the website's structure. You then launched an Active Scan to identify vulnerabilities and reviewed the JSON Alerts to understand the security risks. Finally, you exported a human-readable HTML report.

This process — Map, Scan, Report — is the foundation of automated security testing. In the upcoming practice exercises, you will run these commands yourself against a live application to reinforce these concepts. Good luck!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal