<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://amritesh-sec.github.io/security-engineering/feed.xml" rel="self" type="application/atom+xml" /><link href="https://amritesh-sec.github.io/security-engineering/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-08-11T02:38:42+01:00</updated><id>https://amritesh-sec.github.io/security-engineering/feed.xml</id><title type="html">Security Engineering | Amritesh</title><subtitle>Open source security engineering tools covering reconnaissance, web and API testing, DFIR forensics, detection, data protection, and zero trust — by Amritesh. United States, United Kingdom, and European Union focus.</subtitle><author><name>Amritesh</name></author><entry><title type="html">Building a Security Tool in Python: Design Principles for Defensive Security Engineering</title><link href="https://amritesh-sec.github.io/security-engineering/2026/06/building-security-tools-python-design-principles/" rel="alternate" type="text/html" title="Building a Security Tool in Python: Design Principles for Defensive Security Engineering" /><published>2026-06-15T00:00:00+01:00</published><updated>2026-06-15T00:00:00+01:00</updated><id>https://amritesh-sec.github.io/security-engineering/2026/06/building-security-tools-python-design-principles</id><content type="html" xml:base="https://amritesh-sec.github.io/security-engineering/2026/06/building-security-tools-python-design-principles/"><![CDATA[<p>Building security tools is one of the most effective ways to deepen both offensive and defensive knowledge simultaneously. A network scanner forces you to understand TCP/IP at a level that documentation alone cannot convey. An encryption vault forces you to confront key management decisions that become abstract in theory but concrete in code.</p>

<p>This article covers the design principles that differentiate well-engineered security tools from quick scripts — applicable whether you are building your first Python security tool or evaluating open source tooling for enterprise use.</p>

<hr />

<h2 id="the-core-principle-defensive-intent-by-design">The Core Principle: Defensive Intent by Design</h2>

<p>The most important decision in security tool development is not technical — it is the frame you build around the tool.</p>

<p>A network scanner and a port scanner are the same code. The difference is:</p>

<ul>
  <li><strong>Who the tool is designed for</strong> — security teams conducting authorised assessments, not attackers</li>
  <li><strong>What the tool outputs</strong> — structured, professional reports, not raw data for exploitation</li>
  <li><strong>What guardrails exist</strong> — scope limits, authorisation prompts, rate limiting built in by default</li>
</ul>

<blockquote>
  <p>A tool that requires the user to bypass its own safeguards to misuse it is a better-designed tool than one that requires no configuration at all.</p>
</blockquote>

<p>This framing matters for three reasons: legal risk, professional credibility, and visa portfolio integrity. Every tool you publish is evidence in your professional narrative.</p>

<hr />

<h2 id="python-architecture-for-security-tools">Python Architecture for Security Tools</h2>

<h3 id="project-structure">Project Structure</h3>

<p>A professional Python security tool follows a clear structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tool-name/
├── README.md              # Installation, usage, authorisation notice
├── requirements.txt       # Pinned dependencies
├── setup.py               # Package installation
├── LICENSE                # MIT (or chosen licence)
├── tool_name/
│   ├── __init__.py
│   ├── cli.py             # Command-line interface (argparse or Click)
│   ├── core.py            # Core logic — scan, analyse, collect
│   ├── output.py          # Report formatting — JSON, CSV, HTML
│   └── utils.py           # Shared utilities
└── tests/
    ├── test_core.py
    └── test_output.py
</code></pre></div></div>

<p>This structure is immediately recognisable to any security engineer evaluating your tool — it signals professionalism before a single line of core logic is read.</p>

<h3 id="cli-design-with-argparse">CLI Design with argparse</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">argparse</span>
<span class="kn">import</span> <span class="nn">sys</span>

<span class="k">def</span> <span class="nf">build_parser</span><span class="p">():</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">argparse</span><span class="p">.</span><span class="n">ArgumentParser</span><span class="p">(</span>
        <span class="n">description</span><span class="o">=</span><span class="s">'Network Scanner — for authorised security assessments only'</span><span class="p">,</span>
        <span class="n">formatter_class</span><span class="o">=</span><span class="n">argparse</span><span class="p">.</span><span class="n">RawDescriptionHelpFormatter</span><span class="p">,</span>
        <span class="n">epilog</span><span class="o">=</span><span class="s">"""
AUTHORISATION NOTICE:
  This tool must only be used against systems you own
  or have explicit written permission to test.
  Unauthorised use is illegal and unethical.

Examples:
  python scanner.py --target 192.168.1.0/24 --ports 22,80,443
  python scanner.py --target 10.0.0.1 --ports 1-1024 --output json
        """</span>
    <span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--target'</span><span class="p">,</span> <span class="n">required</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'Target IP or CIDR range'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--ports'</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="s">'1-1024'</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'Port range (e.g., 22,80,443 or 1-1024)'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--output'</span><span class="p">,</span> <span class="n">choices</span><span class="o">=</span><span class="p">[</span><span class="s">'json'</span><span class="p">,</span> <span class="s">'csv'</span><span class="p">,</span> <span class="s">'html'</span><span class="p">],</span> <span class="n">default</span><span class="o">=</span><span class="s">'json'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--timeout'</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">float</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mf">1.0</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'Connection timeout in seconds'</span><span class="p">)</span>
    <span class="n">parser</span><span class="p">.</span><span class="n">add_argument</span><span class="p">(</span><span class="s">'--threads'</span><span class="p">,</span> <span class="nb">type</span><span class="o">=</span><span class="nb">int</span><span class="p">,</span> <span class="n">default</span><span class="o">=</span><span class="mi">50</span><span class="p">,</span> <span class="n">help</span><span class="o">=</span><span class="s">'Maximum concurrent threads'</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">parser</span>

<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
    <span class="n">parser</span> <span class="o">=</span> <span class="n">build_parser</span><span class="p">()</span>
    <span class="n">args</span> <span class="o">=</span> <span class="n">parser</span><span class="p">.</span><span class="n">parse_args</span><span class="p">()</span>
    
    <span class="c1"># Authorisation prompt — built in by default
</span>    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="se">\n</span><span class="s">[!] Target: </span><span class="si">{</span><span class="n">args</span><span class="p">.</span><span class="n">target</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
    <span class="n">confirm</span> <span class="o">=</span> <span class="nb">input</span><span class="p">(</span><span class="s">"[!] Confirm you have authorisation to scan this target [y/N]: "</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">confirm</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">!=</span> <span class="s">'y'</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">"[-] Scan cancelled."</span><span class="p">)</span>
        <span class="n">sys</span><span class="p">.</span><span class="nb">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
    
    <span class="c1"># Proceed with scan
</span>    <span class="n">run_scan</span><span class="p">(</span><span class="n">args</span><span class="p">)</span>
</code></pre></div></div>

<p>The authorisation prompt is non-negotiable. It adds two seconds to the workflow and provides significant legal protection while communicating responsible design intent to anyone reviewing the code.</p>

<hr />

<h2 id="threading-for-performance">Threading for Performance</h2>

<p>Security tools frequently benefit from concurrent execution — scanning 1,000 ports sequentially is slow; scanning them with 50 concurrent threads is practical.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">import</span> <span class="nn">threading</span>
<span class="kn">from</span> <span class="nn">queue</span> <span class="kn">import</span> <span class="n">Queue</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>

<span class="o">@</span><span class="n">dataclass</span>
<span class="k">class</span> <span class="nc">ScanResult</span><span class="p">:</span>
    <span class="n">port</span><span class="p">:</span> <span class="nb">int</span>
    <span class="n">state</span><span class="p">:</span> <span class="nb">str</span>
    <span class="n">service</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="s">''</span>

<span class="k">def</span> <span class="nf">scan_port</span><span class="p">(</span><span class="n">target</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">port</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">ScanResult</span><span class="p">:</span>
    <span class="s">"""Attempt TCP connection to target:port."""</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="n">sock</span> <span class="o">=</span> <span class="n">socket</span><span class="p">.</span><span class="n">socket</span><span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">AF_INET</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">SOCK_STREAM</span><span class="p">)</span>
        <span class="n">sock</span><span class="p">.</span><span class="n">settimeout</span><span class="p">(</span><span class="n">timeout</span><span class="p">)</span>
        <span class="n">result</span> <span class="o">=</span> <span class="n">sock</span><span class="p">.</span><span class="n">connect_ex</span><span class="p">((</span><span class="n">target</span><span class="p">,</span> <span class="n">port</span><span class="p">))</span>
        <span class="n">sock</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">result</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="c1"># Attempt service banner grab
</span>            <span class="n">service</span> <span class="o">=</span> <span class="n">get_service_banner</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">timeout</span><span class="p">)</span>
            <span class="k">return</span> <span class="n">ScanResult</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">port</span><span class="p">,</span> <span class="n">state</span><span class="o">=</span><span class="s">'open'</span><span class="p">,</span> <span class="n">service</span><span class="o">=</span><span class="n">service</span><span class="p">)</span>
    <span class="k">except</span> <span class="p">(</span><span class="n">socket</span><span class="p">.</span><span class="n">timeout</span><span class="p">,</span> <span class="n">socket</span><span class="p">.</span><span class="n">error</span><span class="p">):</span>
        <span class="k">pass</span>
    <span class="k">return</span> <span class="n">ScanResult</span><span class="p">(</span><span class="n">port</span><span class="o">=</span><span class="n">port</span><span class="p">,</span> <span class="n">state</span><span class="o">=</span><span class="s">'closed'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">threaded_scan</span><span class="p">(</span><span class="n">target</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">ports</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span> 
                  <span class="n">timeout</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="n">max_threads</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">List</span><span class="p">[</span><span class="n">ScanResult</span><span class="p">]:</span>
    <span class="s">"""Thread pool scan across port list."""</span>
    <span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="n">results_lock</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Lock</span><span class="p">()</span>
    <span class="n">queue</span> <span class="o">=</span> <span class="n">Queue</span><span class="p">()</span>
    
    <span class="k">for</span> <span class="n">port</span> <span class="ow">in</span> <span class="n">ports</span><span class="p">:</span>
        <span class="n">queue</span><span class="p">.</span><span class="n">put</span><span class="p">(</span><span class="n">port</span><span class="p">)</span>
    
    <span class="k">def</span> <span class="nf">worker</span><span class="p">():</span>
        <span class="k">while</span> <span class="ow">not</span> <span class="n">queue</span><span class="p">.</span><span class="n">empty</span><span class="p">():</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="n">port</span> <span class="o">=</span> <span class="n">queue</span><span class="p">.</span><span class="n">get_nowait</span><span class="p">()</span>
                <span class="n">result</span> <span class="o">=</span> <span class="n">scan_port</span><span class="p">(</span><span class="n">target</span><span class="p">,</span> <span class="n">port</span><span class="p">,</span> <span class="n">timeout</span><span class="p">)</span>
                <span class="k">if</span> <span class="n">result</span><span class="p">.</span><span class="n">state</span> <span class="o">==</span> <span class="s">'open'</span><span class="p">:</span>
                    <span class="k">with</span> <span class="n">results_lock</span><span class="p">:</span>
                        <span class="n">results</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
                <span class="n">queue</span><span class="p">.</span><span class="n">task_done</span><span class="p">()</span>
            <span class="k">except</span> <span class="nb">Exception</span><span class="p">:</span>
                <span class="n">queue</span><span class="p">.</span><span class="n">task_done</span><span class="p">()</span>
    
    <span class="n">threads</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">min</span><span class="p">(</span><span class="n">max_threads</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">ports</span><span class="p">))):</span>
        <span class="n">thread</span> <span class="o">=</span> <span class="n">threading</span><span class="p">.</span><span class="n">Thread</span><span class="p">(</span><span class="n">target</span><span class="o">=</span><span class="n">worker</span><span class="p">,</span> <span class="n">daemon</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
        <span class="n">thread</span><span class="p">.</span><span class="n">start</span><span class="p">()</span>
        <span class="n">threads</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">thread</span><span class="p">)</span>
    
    <span class="k">for</span> <span class="n">thread</span> <span class="ow">in</span> <span class="n">threads</span><span class="p">:</span>
        <span class="n">thread</span><span class="p">.</span><span class="n">join</span><span class="p">()</span>
    
    <span class="k">return</span> <span class="nb">sorted</span><span class="p">(</span><span class="n">results</span><span class="p">,</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">r</span><span class="p">:</span> <span class="n">r</span><span class="p">.</span><span class="n">port</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="structured-output">Structured Output</h2>

<p>Professional security tools produce output that can be consumed by other tools, imported into reports, and reviewed by non-technical stakeholders.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">json</span>
<span class="kn">import</span> <span class="nn">csv</span>
<span class="kn">from</span> <span class="nn">datetime</span> <span class="kn">import</span> <span class="n">datetime</span>
<span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">List</span>

<span class="k">def</span> <span class="nf">export_json</span><span class="p">(</span><span class="n">results</span><span class="p">:</span> <span class="n">List</span><span class="p">[</span><span class="n">ScanResult</span><span class="p">],</span> <span class="n">target</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">output_path</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
    <span class="s">"""Export scan results as structured JSON."""</span>
    <span class="n">report</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s">"metadata"</span><span class="p">:</span> <span class="p">{</span>
            <span class="s">"tool"</span><span class="p">:</span> <span class="s">"network-scanner"</span><span class="p">,</span>
            <span class="s">"version"</span><span class="p">:</span> <span class="s">"1.0.0"</span><span class="p">,</span>
            <span class="s">"target"</span><span class="p">:</span> <span class="n">target</span><span class="p">,</span>
            <span class="s">"timestamp"</span><span class="p">:</span> <span class="n">datetime</span><span class="p">.</span><span class="n">utcnow</span><span class="p">().</span><span class="n">isoformat</span><span class="p">()</span> <span class="o">+</span> <span class="s">"Z"</span><span class="p">,</span>
            <span class="s">"authorisation"</span><span class="p">:</span> <span class="s">"Confirmed by operator"</span>
        <span class="p">},</span>
        <span class="s">"summary"</span><span class="p">:</span> <span class="p">{</span>
            <span class="s">"open_ports"</span><span class="p">:</span> <span class="nb">len</span><span class="p">(</span><span class="n">results</span><span class="p">),</span>
            <span class="s">"ports_scanned"</span><span class="p">:</span> <span class="s">"see configuration"</span>
        <span class="p">},</span>
        <span class="s">"findings"</span><span class="p">:</span> <span class="p">[</span>
            <span class="p">{</span>
                <span class="s">"port"</span><span class="p">:</span> <span class="n">r</span><span class="p">.</span><span class="n">port</span><span class="p">,</span>
                <span class="s">"state"</span><span class="p">:</span> <span class="n">r</span><span class="p">.</span><span class="n">state</span><span class="p">,</span>
                <span class="s">"service"</span><span class="p">:</span> <span class="n">r</span><span class="p">.</span><span class="n">service</span>
            <span class="p">}</span>
            <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span>
        <span class="p">]</span>
    <span class="p">}</span>
    <span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">output_path</span><span class="p">,</span> <span class="s">'w'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">json</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">report</span><span class="p">,</span> <span class="n">f</span><span class="p">,</span> <span class="n">indent</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
    <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"[+] Report saved: </span><span class="si">{</span><span class="n">output_path</span><span class="si">}</span><span class="s">"</span><span class="p">)</span>
</code></pre></div></div>

<p>JSON output is the minimum standard. A tool that only prints to stdout is a script; a tool that produces structured, versioned, metadata-rich output is a professional product.</p>

<hr />

<h2 id="documentation-standards">Documentation Standards</h2>

<p>A README that a CISO could hand to their legal team is a README that gets your tool taken seriously:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gh"># tool-name</span>

One sentence description. 

<span class="gs">**For authorised security assessments only.**</span>

<span class="gu">## Installation</span>
pip install tool-name

<span class="gu">## Usage</span>
python -m tool_name --target 192.168.1.1 --ports 1-1024

<span class="gu">## Authorisation</span>
This tool must only be used against systems you own or have 
explicit written authorisation to test. The operator is solely 
responsible for ensuring legal compliance in their jurisdiction.

<span class="gu">## Licence</span>
MIT — see LICENSE
</code></pre></div></div>

<hr />

<h2 id="moving-to-rust-for-performance-critical-tools">Moving to Rust for Performance-Critical Tools</h2>

<p>Python is appropriate for most security tooling. Rust becomes relevant for:</p>

<ul>
  <li><strong>DFIR triage</strong> — file system operations where memory safety is non-negotiable</li>
  <li><strong>Cryptographic tools</strong> — where undefined behaviour has serious consequences</li>
  <li><strong>High-performance scanning</strong> — where Python threading limits become bottlenecks</li>
</ul>

<p>The learning curve is significant but the output quality difference is measurable. DFIR triage collectors handling evidence must not corrupt data — a Rust implementation with compile-time memory safety guarantees is materially more trustworthy than Python in that context.</p>

<hr />

<h2 id="official-resources">Official Resources</h2>

<ul>
  <li><a href="https://owasp.org/www-project-web-security-testing-guide/">OWASP Testing Guide</a> — reference for web security tooling</li>
  <li><a href="https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-115.pdf">NIST SP 800-115 Technical Guide to Information Security Testing</a> — authoritative reference for security assessment tooling design</li>
  <li><a href="https://python.org/dev/security/">Python Security Best Practices</a> — official Python security guidance</li>
  <li><a href="https://doc.rust-lang.org/book/">The Rust Programming Language</a> — official Rust documentation</li>
</ul>

<hr />

<h2 id="conclusion">Conclusion</h2>

<p>Security tool development is applied security research — every tool teaches you something about the systems it tests or defends. The design principles that make a tool professionally credible (structured output, authorisation prompts, clear documentation, responsible framing) are the same principles that make it genuinely useful in enterprise environments.</p>

<p>The next article covers <strong>building the AD Enumerator</strong> — the first tool in this series, with full implementation walkthrough.</p>

<hr />

<p><em>Questions or contributions? <a href="https://amritesh-sec.github.io/contact/">Get in touch</a>.</em></p>]]></content><author><name>Amritesh</name></author><category term="security-engineering" /><category term="python" /><category term="security-tooling" /><category term="open-source" /><category term="defensive-security" /><summary type="html"><![CDATA[A practical guide to designing and building defensive security tools in Python — architecture decisions, responsible disclosure considerations, and engineering principles for open source security tooling.]]></summary></entry></feed>