Part 5 built the Python environment and terminal stack that makes everything else composable. Shell functions, virtual environments, a clean tool hierarchy, and a terminal that doesn't fight you. That foundation matters here more than it did anywhere else in the series, because web and API security work is fundamentally iterative. You're not running one tool once. You're cycling through reconnaissance, inspection, fuzzing, and verification in tight loops, and the quality of your tooling setup determines whether those loops take ten minutes or an hour.
This part is about building the layer that sits on top of everything you've already configured: a deliberate, repeatable AppSec workflow that starts with manual inspection and ends with automated scanning. Not a list of vulnerability classes. Not a walkthrough of specific exploits. A workflow. The kind that produces consistent output regardless of whether you slept well or whether the target is an old PHP monolith or a modern GraphQL API.
Why Web and API Security Deserves Its Own Workflow
Web applications aren't just one category of target. They're the category. Bug bounty programs, internal red team engagements, compliance assessments, and freelance pentest work all converge on the same surface: HTTP traffic, authentication flows, API endpoints, and the logic that ties them together.
The Expanding Attack Surface of Modern Web Apps
A typical enterprise application in 2026 isn't a single server responding to GET requests. It's a frontend SPA talking to a REST API, which talks to a GraphQL gateway, which fans out to a dozen microservices, each with its own authentication model and its own assumptions about what inputs are valid. Third-party integrations, OAuth flows, webhook receivers, and mobile app backends all attach to the same core identity layer.
That number isn't surprising once you understand the surface. Every new endpoint is a potential authorization gap. Every new integration is a potential trust boundary violation. The attack surface isn't growing linearly. It's growing with the organizational graph.
The ad-hoc approach to this, running a scanner and reviewing whatever it flags, misses most of what matters. Automated scanners don't understand your application's business logic. They don't know that a 200 response on an endpoint that should return 403 is the actual finding. A deliberate workflow catches what automation misses, and automation handles the volume that manual work can't sustain.
Where This Part Fits in the Series
Parts 1 through 5 established the machine: the shell environment, the tool ecosystem, Docker, the Python stack, and the terminal configuration. This part uses all of it. The Docker setup from Part 4 provides local practice targets. The Python environment from Part 5 supports custom scripts and automation. The shell aliases and functions you've already built extend naturally into the request patterns and scanning pipelines covered here.
By the end of this article, you'll have a layered workflow: manual inspection with curl and HTTPie, proxy-based traffic analysis with Burp Suite, structured API testing with Postman and Insomnia, and automated fuzzing and template scanning with FFUF, Gobuster, and Nuclei. Each layer feeds the next.
Mastering curl and HTTPie as Your First-Line Recon Tools
Before a proxy, before a scanner, before anything automated, there's curl. It's the most direct line between you and an HTTP server, and the engineers who use it well get signal faster than anyone running a GUI tool. The flags aren't exotic. Most of them are things you'll use on every engagement.
curl Flags Every AppSec Engineer Should Know
-v gives you full request and response headers, including TLS handshake details. -I sends a HEAD request when you only need headers without the body. -X specifies the method: -X POST, -X PUT, -X DELETE. -H injects a header: -H "Authorization: Bearer $TOKEN". -d sends a request body, and combined with -H "Content-Type: application/json" it handles most API POST scenarios.
--cookie passes a cookie string directly. --proxy http://127.0.0.1:8080 routes the request through Burp Suite, which is how you get curl traffic into your proxy history without touching a browser. -k skips TLS verification for self-signed certs on internal targets. -o writes the response body to a file. --resolve overrides DNS for a specific host, which is useful for virtual host testing without editing /etc/hosts.
Chaining curl with jq turns raw JSON responses into something readable and scriptable:
curl -s -H "Authorization: Bearer $TOKEN" https://target.local/api/v1/users | jq '.data[] | {id, email, role}'That one-liner extracts specific fields from a paginated user list. Add a loop and you've got a quick IDOR enumeration script.
HTTPie for Human-Readable API Inspection
HTTPie is curl for humans. The syntax is cleaner, the output is color-coded, and JSON bodies don't require escaping gymnastics. Install it on Kali with pip install httpie inside your active virtual environment, or system-wide with sudo apt install httpie.
Where curl requires -H "Content-Type: application/json" -d '{"key":"value"}', HTTPie takes http POST target.local/api/endpoint key=value. For quick manual inspection of an API you haven't seen before, that readability advantage is real.
Proxy HTTPie through Burp the same way: http --proxy=http:http://127.0.0.1:8080 GET target.local/api/users.
Saving and Replaying Requests with --config and Aliases
Shell aliases and functions are where the workflow becomes repeatable. Add these to your .zshrc or .bashrc:
1alias bproxy='--proxy http://127.0.0.1:8080 -k'
2burp_curl() { curl $bproxy "$@"; }
3bearer() { curl -H "Authorization: Bearer $1" "${@:2}"; }For longer test sequences, save curl commands as .sh scripts with a header block documenting the target, date, and intent. Six weeks later, you'll know exactly what that script was testing and why.
Recommended: PlaudPro AI Voice Recorder
During active recon sessions, findings come fast. PlaudPro captures your spoken observations and turns them into organized, searchable notes so nothing gets lost between a curl response and your written report. Shop PlaudPro
Configuring Burp Suite Community Edition as Your AppSec Proxy Hub
Burp Suite is the center of gravity for web AppSec work. Everything else orbits it. curl traffic, browser sessions, Postman requests, and scanner output all become more useful once they're passing through Burp's proxy and landing in the HTTP history.
Installing and Launching Burp on Kali
Burp Suite Community Edition ships with Kali. You don't need to install it. Launch it from the terminal with burpsuite or find it under Applications in the Kali menu. The first launch takes a moment while the JVM initializes. After that, the startup time is acceptable.
The default proxy listener binds to 127.0.0.1:8080. That's correct for single-machine work. If you're testing from a VM and routing traffic from a host browser, change the binding to 0.0.0.0:8080 under Proxy > Options > Proxy Listeners. Be careful with that setting on shared networks.
Browser and System-Wide Proxy Configuration
For Firefox, go to Settings > Network Settings > Manual proxy configuration. Set HTTP Proxy to 127.0.0.1, port 8080. Check "Use this proxy server for all protocols." For Chromium, launch it with chromium --proxy-server="http://127.0.0.1:8080" to avoid touching system proxy settings globally.
A dedicated browser profile for testing keeps your personal browsing out of Burp's history. In Firefox, firefox -P opens the profile manager. Create a profile named "BurpTest" and use it exclusively for proxy sessions.
Importing the Burp CA Certificate
With Burp running and your browser proxied through it, navigate to http://burpsuite or http://127.0.0.1:8080. Download the CA certificate from the link on that page. In Firefox, go to Settings > Privacy & Security > Certificates > View Certificates > Authorities > Import. Select the downloaded .der file and trust it for identifying websites.
For system-wide trust on Kali, copy the certificate to /usr/local/share/ca-certificates/burp.crt and run sudo update-ca-certificates. This matters when you're routing curl traffic through Burp with -k removed.
Scope configuration is worth setting up early. Under Target > Scope, add your target domains. Then in Proxy > Options, set "And URL Is in target scope" to avoid filling your history with Google Analytics and CDN noise.
Note: Burp Suite Professional
Community Edition covers everything in this workflow. Teams that need the active Scanner module, Collaborator, or the full extension API should evaluate Burp Suite Professional. The upgrade path is straightforward and the Community Edition configuration transfers directly.
Building an API Security Testing Workflow with Postman and Insomnia
Burp is where you inspect and intercept. Postman and Insomnia are where you organize, chain, and automate. They're not redundant tools. They solve different problems, and using both in the same workflow is the correct approach.
Installing Postman and Insomnia on Kali
Install Postman via snap: sudo snap install postman. If snap isn't configured, install it first with sudo apt install snapd. For Insomnia, download the .deb package from the official releases page and install with sudo dpkg -i insomnia_*.deb. Both run natively on Kali without compatibility issues.
Importing OpenAPI and Swagger Specs for Instant Coverage
This is where GUI API clients earn their place in the workflow. If the target exposes an OpenAPI 3.0 or Swagger 2.0 spec at /openapi.json, /swagger.json, or /api-docs, you can import it directly into Postman and get a complete request collection in under a minute. Every documented endpoint, method, and parameter schema becomes a pre-built request ready for manual testing.
In Postman: File > Import > paste the spec URL or upload the file. The collection generator handles the rest. In Insomnia: Application > Import/Export > Import Data > From URL.
Chaining Requests and Environment Variables for Auth Flows
Environment variables in Postman handle base URLs, API keys, and tokens without hardcoding them into individual requests. Create an environment called "Target Lab" with variables like base_url, access_token, and refresh_token. Reference them in requests as {{base_url}}/api/v1/users.
For JWT-based auth flows, use a collection-level test script to extract the token from a login response and store it automatically:
const response = pm.response.json();
pm.environment.set("access_token", response.data.token);Every subsequent request in the collection picks up the updated token. Add a pre-request script to the collection root that checks token expiry and fires a refresh request when needed. This keeps long test sessions from failing silently on expired auth.
Proxy Postman through Burp by setting the proxy in Postman Settings > Proxy to 127.0.0.1:8080. Disable the built-in Postman proxy first. Now every request you send from Postman lands in Burp's HTTP history alongside your browser traffic.
Automated Web Recon: FFUF, Gobuster, and Nuclei in Your Pipeline
Manual inspection finds logic flaws. Automated recon finds the surface you didn't know existed. Directory brute-forcing, parameter discovery, virtual host enumeration, and template-based vulnerability scanning all operate at a speed and scale that manual work can't match.
Directory and Parameter Fuzzing with FFUF
FFUF (Fuzz Faster U Fool) is the current standard for web fuzzing on Kali. It's fast, flexible, and its filtering options are precise enough to avoid drowning in false positives.
Basic directory brute-force:
ffuf -u http://target.local/FUZZ -w /usr/share/wordlists/dirb/common.txt -mc 200,301,302,403 -fs 0-mc filters by status code. -fs filters by response size, which removes responses that all return the same boilerplate 404 body. Add -t 50 to set concurrency and -rate 100 to cap requests per second on sensitive targets.
For parameter fuzzing, place FUZZ in the query string or POST body:
ffuf -u "http://target.local/api/v1/user?id=FUZZ" -w /usr/share/seclists/Fuzzing/integers.txt -mc 200 -fw 0Virtual Host and DNS Brute-Forcing with Gobuster
Gobuster in vhost mode discovers virtual hosts that share an IP but respond differently based on the Host header:
gobuster vhost -u http://target.local -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domainThe --append-domain flag appends the base domain to each wordlist entry, so admin becomes admin.target.local. Combine this with DNS mode for external subdomain enumeration during bug bounty recon.
Template-Based Vulnerability Scanning with Nuclei
Nuclei uses YAML templates to define checks. Each template describes a request pattern and a condition that constitutes a finding. The community template library covers thousands of known CVEs, misconfigurations, exposed panels, and default credentials.
Update templates before every scan:
nuclei -update-templatesRun a scan against a local test app:
nuclei -u http://localhost:3000 -t ~/nuclei-templates/vulnerabilities/ -severity medium,high,critical -rate-limit 50The output is structured and filterable. Severity, template ID, matched URL, and the extracted evidence all appear inline.
"Nuclei doesn't replace manual testing. It handles the known-bad pattern matching so your manual time goes toward the logic flaws that no template will ever catch."
Concurrency and rate limiting matter. The default settings are aggressive enough to cause problems on underpowered targets or shared environments. Always use -rate-limit and -c (concurrency) flags when testing anything outside your own isolated lab.
Spinning Up Vulnerable Practice Targets with Docker
Every tool in this article needs a legal, safe target to run against. Reading about FFUF is useful. Running FFUF against a real app you control is how the concepts actually land.
DVWA, Juice Shop
Python-Powered AppSec Automation: Requests, Scrapy, and Custom Scripts
Part 5 got your Python environment dialed in: virtual environments, pip hygiene, and a clean separation between system Python and your tooling. That foundation matters here because scripting isn't a bonus feature of AppSec work. It's how you stop doing the same manual test seventeen times and start doing it once, correctly, with output you can actually use.
Building a Minimal HTTP Fuzzer with requests and argparse
A minimal fuzzer doesn't need to be clever. It needs to accept a URL and a wordlist, iterate through every entry, fire a request, and print anything that isn't a 404. That's the whole job.
1import argparse
2import requests
3
4def fuzz(url, wordlist, proxy=None):
5 proxies = {"http": proxy, "https": proxy} if proxy else None
6 session = requests.Session()
7 session.proxies = proxies
8 with open(wordlist) as f:
9 for word in f:
10 word = word.strip()
11 target = f"{url}/{word}"
12 try:
13 r = session.get(target, timeout=5, verify=False)
14 if r.status_code != 404:
15 print(f"[{r.status_code}] {target}")
16 except requests.RequestException:
17 pass
18
19if __name__ == "__main__":
20 parser = argparse.ArgumentParser(description="Minimal HTTP fuzzer")
21 parser.add_argument("url", help="Base URL to fuzz")
22 parser.add_argument("wordlist", help="Path to wordlist file")
23 parser.add_argument("--proxy", help="Proxy URL (e.g. http://127.0.0.1:8080)")
24 args = parser.parse_args()
25 fuzz(args.url, args.wordlist, args.proxy)The --proxy flag is the critical piece. Pass http://127.0.0.1:8080 and every request flows through Burp's proxy listener. You get the full HTTP history, repeater access, and the ability to modify anything mid-session. Your Python tool and Burp stop being separate things and start being one pipeline.
Parsing and Spidering Responses with BeautifulSoup
Once you're capturing responses, you want to extract meaning from them. BeautifulSoup makes form discovery straightforward. The pattern below pulls every form and its input fields from a target page, which feeds directly into automated form submission testing.
1from bs4 import BeautifulSoup
2import requests
3
4def extract_forms(url, proxy=None):
5 proxies = {"http": proxy, "https": proxy} if proxy else None
6 r = requests.get(url, proxies=proxies, verify=False)
7 soup = BeautifulSoup(r.text, "html.parser")
8 for form in soup.find_all("form"):
9 action = form.get("action", "")
10 method = form.get("method", "GET").upper()
11 inputs = [(i.get("name"), i.get("type")) for i in form.find_all("input")]
12 print(f"Form: action={action} method={method}")
13 for name, ftype in inputs:
14 print(f" Input: name={name} type={ftype}")For applications with dozens of pages, Scrapy replaces this single-page approach with a full crawling framework. You define a spider, set allowed domains, and Scrapy handles the queue, deduplication, and output serialization. It's heavier to configure but the right tool when you need to map an entire multi-page application rather than a single endpoint.
Store everything. A JSON file per target URL, keyed by timestamp, gives you something to diff across scans. SQLite works better when you're running hundreds of requests and need to query by status code or response length later.
Recommended: PlaudPro
When you're deep in a testing session, ideas and findings surface fast. PlaudPro captures your spoken notes and turns them into organized, searchable text so nothing gets lost between the terminal and your report. Shop PlaudPro
Integrating Python Scripts into Your Burp Workflow via Extensions
Burp extensions let you push custom logic directly into the proxy pipeline. The original approach uses Jython: a JAR-based Python 2.7 runtime that Burp loads, which works but carries real limitations around library support and performance. The newer Montoya API, introduced in Burp Suite 2022.8, supports Python 3 via Jython 2.7.4-beta and increasingly via compiled extensions, with a cleaner object model.
A minimal extension registers an HTTP handler, inspects every response, and flags anything matching a pattern you care about. Injection point markers, custom headers, response body signatures. The extension runs inside Burp's process, so it has access to the full request/response cycle without any proxy configuration on your end. Commit your extension to the same dotfiles repo as your fuzzer. Six months from now you'll thank yourself.
Integrating AppSec into CI/CD with GitHub Actions and DAST
Manual testing finds what you look for. Automated scanning in CI/CD finds what you forgot to look for. The DevSecOps principle is simple: run lightweight security checks on every pull request so vulnerabilities surface at the code review stage, not after a production deploy. The cost of fixing a finding in PR review is a fraction of the cost after it ships.
Running Nuclei and FFUF in a GitHub Actions Workflow
The workflow below spins up Juice Shop as a service container, waits for it to be healthy, then runs a Nuclei scan against it using the community template library.
1name: DAST Scan
2
3on:
4 pull_request:
5 branches: [main]
6
7jobs:
8 dast:
9 runs-on: ubuntu-latest
10 services:
11 juiceshop:
12 image: bkimminich/juice-shop
13 ports:
14 - 3000:3000
15 options: >-
16 --health-cmd "curl -f http://localhost:3000 || exit 1"
17 --health-interval 10s
18 --health-retries 5
19
20 steps:
21 - uses: actions/checkout@v4
22
23 - name: Install Nuclei
24 run: |
25 go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
26 nuclei -update-templates
27
28 - name: Run Nuclei scan
29 run: |
30 nuclei -u http://localhost:3000 \
31 -severity medium,high,critical \
32 -o nuclei-results.sarif \
33 -sarif-export nuclei-results.sarif
34
35 - name: Upload SARIF to GitHub Security tab
36 uses: github/codeql-action/upload-sarif@v3
37 with:
38 sarif_file: nuclei-results.sarif
39
40 - name: Save scan artifact
41 uses: actions/upload-artifact@v4
42 with:
43 name: nuclei-report
44 path: nuclei-results.sarif
45 retention-days: 30The SARIF upload step is what surfaces findings in the GitHub Security tab as code scanning alerts. Your team sees findings without leaving GitHub. Artifact retention gives you the raw report for audit purposes.
ZAP Baseline Scan as a Pull Request Gate
The OWASP ZAP GitHub Action wraps the ZAP baseline scan in a single workflow step with configurable pass/fail thresholds.
1 - name: ZAP Baseline Scan
2 uses: zaproxy/[email protected]
3 with:
4 target: 'http://localhost:3000'
5 fail_action: true
6 cmd_options: '-a'Setting fail_action: true means the workflow fails if ZAP finds issues above your configured threshold. That's the gate. PRs with new high-severity findings don't merge until someone addresses them or explicitly marks them as accepted risk.
Know the Limits
Community-edition DAST tools in CI catch common patterns: missing headers, known CVEs, basic injection probes. They don't replace authenticated testing, business logic review, or a skilled human working through an application manually. Use them as a floor, not a ceiling.
The honest limitation: free DAST in CI is a filter, not a guarantee. Enterprise scanners like Invicti or Bright Security offer authenticated crawling, API schema-aware scanning, and significantly lower false-positive rates. For a solo developer or small team, the ZAP plus Nuclei combination catches enough to be worth running on every PR.
Organizing Findings: Note-Taking, Reporting, and Evidence Management
A brilliant finding that can't be communicated clearly is a finding that doesn't get fixed. Documentation isn't administrative overhead. It's the last step of the actual work, and skipping it means the first fifty steps didn't fully count.
Structuring a Pentest Notes Folder in Obsidian or CherryTree
The folder structure below works whether you're using Obsidian, CherryTree, or plain directories on the filesystem.
1targets/
2 juice-shop/
3 2026-06-25/
4 recon/
5 nmap-initial.txt
6 ffuf-dirs.txt
7 exploitation/
8 sqli-login-bypass.md
9 xss-profile-field.md
10 evidence/
11 screenshots/
12 burp-exports/
13 curl-logs/
14 report/
15 draft.mdDate-stamped subdirectories matter. You'll run multiple sessions against the same target. Without the date layer, evidence from different sessions collides and you lose the timeline that makes reproduction steps credible.
Capturing Evidence: Screenshots, Burp Exports, and curl Logs
Flameshot gives you annotated screenshots with arrows, boxes, and text directly in the capture workflow. The command flameshot gui opens the annotation interface before saving. For headless or scripted captures, scrot -s handles region selection without a GUI.
Burp's HTTP history exports to XML or HTML via right-click on any item or selection. The XML format is better for programmatic processing later. For individual requests you want to highlight in a report, use "Copy as curl" from the repeater and paste it directly into your evidence file.
Generating a Minimal Report Template
"The report is the deliverable. Everything else is how you built it."
A minimal Markdown template covers: executive summary in plain language, scope definition, a findings table with severity and CVSS score columns, reproduction steps numbered precisely enough that someone else can follow them, and a remediation section that tells the developer what to actually do. That last part gets skipped constantly. Don't skip it.
For team-scale engagements, Ghostwriter and Plextrac both offer collaborative reporting with finding libraries, client management, and template rendering. They're worth evaluating once you're running more than two or three engagements a month.
Recommended: Protein Bars (1st Phorm)
Long testing sessions and report writing marathons both suffer from the same problem: you stop eating real food. Keep 1st Phorm protein bars in your kit bag. 20g protein, no prep required, and they don't taste like cardboard. Shop Protein Bars
Your Part 6 AppSec Workflow Checklist
Everything covered in this part adds up to a layered workflow. Use this checklist to confirm your environment is actually ready before you run a real engagement or move into Part 7.
What's Next: Part 7. Hardening, Persistence, and Making Your Setup Portable
Part 6 built something real. A layered web and API security workflow with automated scanning, Python tooling that routes through Burp, Docker-based practice targets, CI/CD integration, and an evidence management system that produces defensible reports. That's not a beginner setup. That's a working professional workflow.
Part 7 closes the series by making everything you've built durable. Full-disk encryption for your Kali install, a portable USB image you can boot on any machine, VM snapshots for clean-state testing, and dotfile sync so your configuration survives hardware changes. The goal is a setup you maintain for years, not one you rebuild from scratch every six months.
Before you move on, test the complete workflow against a legal bug bounty target or your own staging environment. Reading about it and running it against a real application are different experiences. If you've built custom Nuclei templates or extended the Python fuzzer in an interesting direction, share them in the comments. The series repository on GitHub holds all configs, Compose files, and scripts from every part. Everything there is meant to be forked, modified, and improved.
Recommended: BCAA (1st Phorm)
If you're putting in long sessions building and testing this setup, your recovery matters. 1st Phorm BCAAs keep muscle fatigue from cutting your focus short during extended work blocks. Mix them into your water and keep moving. Shop BCAA