Skip to content

Bump blastdns to 2.0.4 - #3631

Merged
liquidsec merged 1 commit into
devfrom
blastdns-2.0.4
Nov 10, 2026
Merged

liquidsec merged 1 commit into
devfrom
blastdns-2.0.4

Conversation

@liquidsec

Copy link
Copy Markdown
Contributor

Raises the blastdns floor to 2.0.4 and re-locks.

Changes in 2.0.4:

@codecov

codecov Bot commented Nov 10, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91%. Comparing base (3085baa) to head (32f3915).
⚠️ Report is 5 commits behind head on dev.

Additional details and impacted files
@@          Coverage Diff          @@
##             dev   #3631   +/-   ##
=====================================
- Coverage     91%     91%   -0%     
=====================================
  Files        465     465           
  Lines      51927   51927           
=====================================
- Hits       47048   47040    -8     
- Misses      4879    4887    +8     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@liquidsec liquidsec mentioned this pull request Nov 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📊 Performance Benchmark Report

Comparing dev (3085baacf) (baseline) vs blastdns-2.0.4 (690c4dd6a) (current)

📈 Detailed Results (All Benchmarks)

📋 Complete results for all benchmarks - includes both significant and insignificant changes

🧪 Test Name 📏 Base 📏 Current 📈 Change 🎯 Status
Bloom Filter Dns Mutation Tracking Performance 3.92ms 3.83ms -2.4% ⚪ ✅
Bloom Filter Large Scale Dns Brute Force 15.93ms 15.05ms -5.5% ⚪ ✅
Large Closest Match Lookup 352.15ms 352.10ms -0.0% ⚪ ✅
Realistic Closest Match Workload 194.04ms 190.24ms -2.0% ⚪ ✅
Event Memory Medium Scan 1402 B/event 1402 B/event +0.0% ⚪ ✅
Event Memory Large Scan 1527 B/event 1527 B/event +0.0% ⚪ ✅
Event Validation Full Scan Startup Small Batch 29.55ms 31.10ms +5.2% ⚪ ✅
Event Validation Full Scan Startup Large Batch 175.58ms 163.74ms -6.7% ⚪ ✅
Make Event Autodetection Small 26.73ms 26.22ms -1.9% ⚪ ✅
Make Event Autodetection Large 268.32ms 269.56ms +0.5% ⚪ ✅
Make Event Explicit Types 11.70ms 11.51ms -1.6% ⚪ ✅
Excavate Single Thread Small 3.733s 3.714s -0.5% ⚪ ✅
Excavate Single Thread Large 9.796s 9.844s +0.5% ⚪ ✅
Excavate Parallel Tasks Small 3.913s 3.959s +1.2% ⚪ ✅
Excavate Parallel Tasks Large 6.329s 6.232s -1.5% ⚪ ✅
Intercept Throughput Small 380.39ms 379.37ms -0.3% ⚪ ✅
Intercept Throughput Medium 377.66ms 376.73ms -0.2% ⚪ ✅
Dns Throughput Quiet 2.134s 2.183s +2.3% ⚪ ✅
Dns Throughput Loaded 1.166s 1.168s +0.1% ⚪ ✅
Dns Throughput Inherited 1.913s 1.935s +1.2% ⚪ ✅
Is Ip Performance 2.24ms 2.22ms -1.1% ⚪ ✅
Make Ip Type Performance 234.75µs 228.36µs -2.7% ⚪ ✅
Mixed Ip Operations 2.34ms 2.31ms -1.2% ⚪ ✅
Memory Use Web Crawl 1.1 MB 1.1 MB +1.8% ⚪ ✅
Memory Use Subdomain Enum 28.4 MB 28.4 MB +0.0% ⚪ ✅
Memory Use Deep Chain 5.1 MB 5.1 MB +0.0% ⚪ ✅
Memory Use Parallel Chains 13.5 MB 12.1 MB -10.4% 🟢🟢 🚀
Scan Throughput 100 2.468s 2.401s -2.7% ⚪ ✅
Scan Throughput 1000 20.628s 21.146s +2.5% ⚪ ✅
Typical Queue Shuffle 5.44µs 5.39µs -0.9% ⚪ ✅
Priority Queue Shuffle 25.98µs 25.85µs -0.5% ⚪ ✅

🎯 Performance Summary

+ 1 improvement 🚀
  30 unchanged ✅

🔍 Significant Changes (>10%)

  • Memory Use Parallel Chains: 10.4% 🚀 less memory

🐍 Python Version 3.11.18

@en0f en0f left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 32f3915.

🟢 Clean, reproducible lock bump

Re-locking the base with uv lock --upgrade-package blastdns==2.0.4 (uv 0.13.7) reproduces this uv.lock byte for byte, so only the blastdns entry and its specifier move. All 71 locked artifacts match PyPI's hashes and carry PyPI provenance from blastdns publish.yml at 58976d2d (the blastdns#179 merge), the wheel tags are exactly the 70 the base locked, and the sdist's src/, blastdns/ and Cargo files equal that commit.

Reproduce this review
# save the script below as verify-3631.sh and the patch as control-3631.patch, then:
bash verify-3631.sh blacklanternsecurity/bbot 32f39151518c37598c4379e56bbb3a2df4c29715
# expected: SUMMARY 12 PASS, 0 FAIL, 0 SKIP, 0 BROKEN
# control:  bash verify-3631.sh blacklanternsecurity/bbot 32f39151518c37598c4379e56bbb3a2df4c29715 control-3631.patch   -> V01 V02 V03 V04 V05 V12 FAIL
#!/usr/bin/env bash
# verify-3631.sh: reproducible checks for blacklanternsecurity/bbot#3631
# Usage: bash verify-3631.sh <owner/repo> <sha> [<control-patch>]
# Changes nothing outside a mktemp directory and nothing on GitHub (PR-VF-02).
#
# CONTROL: patch control-3631.patch
# CONTROL-EXPECT: V01=FAIL V02=FAIL V03=FAIL V04=FAIL V05=FAIL V12=FAIL
# AUTHOR-CODE: none
set -uo pipefail

REPO="${1:?owner/repo}"; SHA="${2:?sha}"; PATCH="${3:-}"
BASE_SHA=3085baacf1f812fcf446d73f673aa2454b199ce4        # PR base (dev)
BLASTDNS_REL=58976d2d2f42300bd040f9f615ed3277bb92969c    # blastdns#179 merge into stable (2.0.4)
PASS=0; FAIL=0; SKIP=0; BROKEN=0
pass()   { echo "PASS $1 $2"; PASS=$((PASS+1)); }
fail()   { echo "FAIL $1 $2 -- $3"; FAIL=$((FAIL+1)); }
skip()   { echo "SKIP $1 $2 -- $3"; SKIP=$((SKIP+1)); }
broken() { echo "BROKEN $1 $2 -- $3"; BROKEN=$((BROKEN+1)); }
summary() { echo "SUMMARY $PASS PASS, $FAIL FAIL, $SKIP SKIP, $BROKEN BROKEN"; [ "$FAIL" -eq 0 ] && [ "$BROKEN" -eq 0 ]; }

echo "script_sha256 $(shasum -a 256 "$0" | cut -d' ' -f1)"
echo "git $(git --version | cut -d' ' -f3)"
echo "python3 $(python3 -c 'import sys; print(sys.version.split()[0])')"

case "$PATCH" in ""|/*) ;; *) PATCH="$(cd "$(dirname "$PATCH")" && pwd)/$(basename "$PATCH")";; esac
WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT
export GIT_CONFIG_GLOBAL=/dev/null GIT_CONFIG_NOSYSTEM=1 GIT_TERMINAL_PROMPT=0
GIT=(git -c credential.helper= -c 'credential.helper=!gh auth git-credential')
"${GIT[@]}" clone --quiet "https://github.com/$REPO.git" "$WORK/src" --no-checkout || { broken V00 "clone" "clone failed"; summary; exit 1; }
cd "$WORK/src"
"${GIT[@]}" cat-file -e "$SHA^{commit}" 2>/dev/null || "${GIT[@]}" fetch --quiet origin "+refs/pull/*/head:refs/remotes/pr/*" || true
"${GIT[@]}" checkout --quiet --detach "$SHA" || { broken V00 "checkout $SHA" "checkout failed"; summary; exit 1; }
[ "$(git rev-parse HEAD)" = "$SHA" ] || { broken V00 "HEAD is $SHA" "HEAD mismatch"; summary; exit 1; }
if [ -n "$PATCH" ]; then git apply "$PATCH" || { broken V00 "apply control patch" "git apply failed"; summary; exit 1; }; fi

# ---- pinned uv, checksum-verified, with wrong-hash control (PR-VF-03) --------
UV_URL=https://github.com/astral-sh/uv/releases/download/0.13.7/uv-aarch64-apple-darwin.tar.gz
UV_SHA256=81b811b57e4624df7ed3aac148e0359100d9962f3d9dce00aec533dc2e086731
# UV_NO_CONFIG isolates uv; XDG_CONFIG_HOME is not exported because it hides gh auth
export UV_CACHE_DIR="$WORK/uv/cache" UV_PYTHON_INSTALL_DIR="$WORK/uv/python" UV_NO_CONFIG=1
UV=""
if curl -fsSLo "$WORK/uv.tgz" "$UV_URL"; then
  if echo "$UV_SHA256  $WORK/uv.tgz" | shasum -a 256 -c - >/dev/null 2>&1; then
    pass V10 "uv 0.13.7 tarball matches its published sha256"
    tar xzf "$WORK/uv.tgz" -C "$WORK" && UV="$WORK/uv-aarch64-apple-darwin/uv"
    echo "uv $("$UV" --version)"
  else broken V10 "uv 0.13.7 tarball matches its published sha256" "mismatch"; fi
  if echo "0000000000000000000000000000000000000000000000000000000000000000  $WORK/uv.tgz" | shasum -a 256 -c - >/dev/null 2>&1; then
    broken V11 "wrong-hash control rejects uv" "accepted a wrong hash"
  else pass V11 "wrong-hash control rejects uv"; fi
else skip V10 "uv 0.13.7 tarball matches its published sha256" "download failed"; fi

# ---- the blastdns block of uv.lock, as data ---------------------------------
lockblock() { awk '/^name = "blastdns"$/{f=1} f&&/^\[\[package\]\]$/{exit} f' "$1"; }
lockblock uv.lock > "$WORK/head_block"
git show "${BASE_SHA}:uv.lock" > "$WORK/base_lock"; lockblock "$WORK/base_lock" > "$WORK/base_block"
LOCKVER=$(sed -n 's/^version = "\(.*\)"$/\1/p' "$WORK/head_block" | head -1)
echo "lock_blastdns_version $LOCKVER"

# V01: pyproject floor, the lock's requires-dist specifier and the locked version agree on 2.0.4
PYSPEC=$(sed -n 's/^ *"blastdns\(.*\)",$/\1/p' pyproject.toml)
LKSPEC=$(sed -n 's/^ *{ name = "blastdns", specifier = "\(.*\)" },$/\1/p' uv.lock)
if [ "$PYSPEC" = ">=2.0.4,<2.1" ] && [ "$LKSPEC" = "$PYSPEC" ] && [ "$LOCKVER" = "2.0.4" ]; then
  pass V01 "pyproject requires blastdns>=2.0.4,<2.1, uv.lock records the same specifier and locks 2.0.4"
else fail V01 "pyproject requires blastdns>=2.0.4,<2.1, uv.lock records the same specifier and locks 2.0.4" "pyproject='$PYSPEC' lock-spec='$LKSPEC' locked='$LOCKVER'"; fi

# V02: re-locking the base with this tree's pyproject and pinned uv reproduces uv.lock byte for byte
if [ -z "$UV" ]; then skip V02 "uv.lock is exactly what uv 0.13.7 produces from the base for blastdns $LOCKVER" "uv unavailable"
else
  git worktree add --quiet --detach "$WORK/relock" "$BASE_SHA"
  cp pyproject.toml "$WORK/relock/pyproject.toml"
  if (cd "$WORK/relock" && "$UV" lock --upgrade-package "blastdns==$LOCKVER" >"$WORK/relock.log" 2>&1); then
    if cmp -s "$WORK/relock/uv.lock" uv.lock; then pass V02 "uv.lock is exactly what uv 0.13.7 produces from the base for blastdns $LOCKVER"
    else fail V02 "uv.lock is exactly what uv 0.13.7 produces from the base for blastdns $LOCKVER" "$(diff "$WORK/relock/uv.lock" uv.lock | grep -c '^[<>]') differing lines"; fi
  else skip V02 "uv.lock is exactly what uv 0.13.7 produces from the base for blastdns $LOCKVER" "uv lock failed: $(tail -1 "$WORK/relock.log")"; fi
fi

# V03: every blastdns artifact hash in uv.lock equals PyPI's published sha256 for that file
if curl -fsSL "https://pypi.org/pypi/blastdns/$LOCKVER/json" -o "$WORK/pypi.json"; then
  python3 - "$WORK/head_block" "$WORK/pypi.json" > "$WORK/v03" <<'PY'
import json, re, sys
block = open(sys.argv[1]).read()
pypi = {u["filename"]: u["digests"]["sha256"] for u in json.load(open(sys.argv[2]))["urls"]}
pairs = re.findall(r'url = "[^"]*/([^/"]+)", hash = "sha256:([0-9a-f]{64})"', block)
bad = [f for f, h in pairs if pypi.get(f) != h]
print(len(pairs), len(bad), " ".join(bad[:3]))
PY
  read -r NPAIRS NBAD BADF < "$WORK/v03"
  if [ "${NPAIRS:-0}" -gt 0 ] && [ "${NBAD:-1}" -eq 0 ]; then pass V03 "all $NPAIRS blastdns $LOCKVER hashes in uv.lock match PyPI"
  else fail V03 "every blastdns hash in uv.lock matches PyPI" "$NBAD of $NPAIRS differ: $BADF"; fi
else skip V03 "every blastdns hash in uv.lock matches PyPI" "PyPI JSON unavailable"; fi

# V04: the locked wheel set covers every platform/interpreter tag the base lock covered
tags() { grep -oE 'blastdns-[0-9.]+-[^"/]+\.whl' "$1" | sed -E 's/^blastdns-[0-9.]+-//' | sort -u; }
tags "$WORK/base_block" > "$WORK/base_tags"; tags "$WORK/head_block" > "$WORK/head_tags"
MISSING=$(comm -23 "$WORK/base_tags" "$WORK/head_tags" | tr '\n' ' '); EXTRA=$(comm -13 "$WORK/base_tags" "$WORK/head_tags" | tr '\n' ' ')
if [ -s "$WORK/base_tags" ] && [ -z "$MISSING" ] && [ -z "$EXTRA" ]; then pass V04 "uv.lock locks exactly the same $(wc -l < "$WORK/base_tags" | tr -d ' ') blastdns wheel tags as the base"
else fail V04 "uv.lock locks exactly the blastdns wheel tags the base locked" "missing: $MISSING extra: $EXTRA"; fi

# V05: the sdist uv.lock pins downloads at its locked hash, and its Rust and Python sources equal blastdns at the 2.0.4 release merge
SD_URL=$(sed -n 's/^sdist = { url = "\([^"]*\)", hash = "sha256:\([0-9a-f]*\)".*/\1/p' "$WORK/head_block")
SD_SHA=$(sed -n 's/^sdist = { url = "\([^"]*\)", hash = "sha256:\([0-9a-f]*\)".*/\2/p' "$WORK/head_block")
if curl -fsSLo "$WORK/sdist.tgz" "$SD_URL"; then
  if echo "$SD_SHA  $WORK/sdist.tgz" | shasum -a 256 -c - >/dev/null 2>&1; then
    mkdir -p "$WORK/sdist" && tar xzf "$WORK/sdist.tgz" -C "$WORK/sdist"
    SDIR=$(ls -d "$WORK"/sdist/blastdns-*)
    if ! { "${GIT[@]}" clone --quiet "https://github.com/blacklanternsecurity/blastdns.git" "$WORK/bd" && (cd "$WORK/bd" && git checkout --quiet --detach "$BLASTDNS_REL") && [ "$(git -C "$WORK/bd" rev-parse HEAD)" = "$BLASTDNS_REL" ]; }; then
      broken V05 "locked sdist sources equal blastdns at ${BLASTDNS_REL:0:8}" "could not check out blastdns at $BLASTDNS_REL"
    else
    DIFFS=$(diff -rq "$SDIR/src" "$WORK/bd/src"; diff -rq "$SDIR/blastdns" "$WORK/bd/blastdns"; diff -q <(grep -v '^readme = "README.md"$' "$SDIR/Cargo.toml") <(grep -v '^readme = "README.md"$' "$WORK/bd/Cargo.toml") >/dev/null || echo "Cargo.toml differs"; diff -q "$SDIR/Cargo.lock" "$WORK/bd/Cargo.lock")
    if [ -z "$DIFFS" ]; then pass V05 "locked sdist matches its hash, and src/, blastdns/, Cargo.toml (less the readme line maturin adds), Cargo.lock equal blastdns at ${BLASTDNS_REL:0:8}"
    else fail V05 "locked sdist sources equal blastdns at ${BLASTDNS_REL:0:8}" "$(echo "$DIFFS" | head -3 | tr '\n' ' ')"; fi
    fi
  else fail V05 "locked sdist downloads at its locked hash" "sha256 mismatch for $SD_URL"; fi
else skip V05 "locked sdist downloads at its locked hash" "download failed"; fi

# V06: bbot's main DNS client is always built with an explicit resolver list (blastdns's empty-list path is not taken while the OS reports resolvers)
if grep -q 'self.system_resolvers = get_system_resolvers()' bbot/core/helpers/dns/dns.py \
   && grep -q 'self.resolvers = list(self.system_resolvers)' bbot/core/helpers/dns/dns.py \
   && grep -q 'self.blastdns = Client($' bbot/core/helpers/dns/dns.py \
   && awk '/self.blastdns = Client\($/{getline; print; exit}' bbot/core/helpers/dns/dns.py | grep -q 'self.resolvers,'; then
  pass V06 "bbot builds its blastdns Client from get_system_resolvers() or dns.nameservers, never an empty list by design"
else fail V06 "bbot builds its blastdns Client from an explicit resolver list" "construction differs"; fi

# V07: in the 2.0.4 sdist, Python get_system_resolvers() returns bare ip.to_string(), and parse_resolver maps a bare IPv6 to port 53
if [ -n "${SDIR:-}" ] && grep -q 'resolver_ips.iter().map(|ip| ip.to_string()).collect()' "$SDIR/src/python.rs" \
   && awk '/fn parse_resolver/,/^}/' "$SDIR/src/utils.rs" | grep -q 'return Ok(SocketAddr::new(ip, 53));'; then
  pass V07 "blastdns $LOCKVER: Python get_system_resolvers() returns bare IPs and parse_resolver gives a bare IPv6 port 53"
elif [ -z "${SDIR:-}" ]; then skip V07 "blastdns get_system_resolvers()/parse_resolver handle bare IPv6" "sdist unavailable"
else fail V07 "blastdns get_system_resolvers()/parse_resolver handle bare IPv6" "code differs from the reviewed form"; fi

# V12: every locked blastdns artifact has PyPI (PEP 740) provenance whose subject digest is the locked hash,
# published by blastdns publish.yml, with a signing certificate naming the release commit. Trusts PyPI's served bundle.
echo "openssl $(openssl version)"
awk '/^name = "blastdns"$/{f=1} f&&/^\[\[package\]\]$/{exit} f' uv.lock \
 | grep -oE 'url = "[^"]*/(blastdns-[^"/]+)", hash = "sha256:[0-9a-f]{64}"' \
 | sed -E 's|url = "[^"]*/([^"/]+)", hash = "sha256:([0-9a-f]{64})"|\1 \2|' > "$WORK/pairs"
PN=0; PBAD=0; PFIRST=""
while read -r F H; do
  PN=$((PN+1))
  if ! curl -fsS -H 'Accept: application/vnd.pypi.integrity.v1+json' "https://pypi.org/integrity/blastdns/$LOCKVER/$F/provenance" -o "$WORK/p.json"; then PBAD=$((PBAD+1)); PFIRST="${PFIRST:-noprov $F}"; continue; fi
  if ! python3 -I - "$WORK/p.json" "$H" "$WORK/c.der" <<'PY'
import json,base64,sys
d=json.load(open(sys.argv[1])); b=d["attestation_bundles"][0]; a=b["attestations"][0]
st=json.loads(base64.b64decode(a["envelope"]["statement"]))
ok = st["subject"][0]["digest"]["sha256"]==sys.argv[2] and b["publisher"]["repository"]=="blacklanternsecurity/blastdns" and b["publisher"]["workflow"]=="publish.yml"
open(sys.argv[3],"wb").write(base64.b64decode(a["verification_material"]["certificate"]))
sys.exit(0 if ok else 1)
PY
  then PBAD=$((PBAD+1)); PFIRST="${PFIRST:-digest/publisher mismatch $F}"; continue; fi
  openssl x509 -inform der -in "$WORK/c.der" -noout -text 2>/dev/null | grep -aq "$BLASTDNS_REL" || { PBAD=$((PBAD+1)); PFIRST="${PFIRST:-cert not at release $F}"; }
done < "$WORK/pairs"
if [ "$PN" -gt 0 ] && [ "$PBAD" -eq 0 ]; then pass V12 "all $PN locked blastdns artifacts carry PyPI provenance from blastdns publish.yml at ${BLASTDNS_REL:0:8}"
else fail V12 "every locked blastdns artifact carries PyPI provenance from blastdns publish.yml at ${BLASTDNS_REL:0:8}" "$PBAD of $PN: $PFIRST"; fi

# V08/V09: CI at the head (check runs, read-only)
if gh api --method GET "repos/$REPO/commits/$SHA/check-runs?per_page=100" > "$WORK/cr.json" 2>/dev/null; then
  read -r DT DOK < <(python3 -c 'import json,sys; r=[c for c in json.load(open(sys.argv[1]))["check_runs"] if c["name"].startswith("test-distros (")]; print(len(r), sum(c["conclusion"]=="success" for c in r))' "$WORK/cr.json")
  if [ "$DT" -ge 6 ] && [ "$DT" = "$DOK" ]; then pass V08 "all $DT test-distros jobs (uv sync + e2e tests) succeeded at the head"
  else fail V08 "all test-distros jobs succeeded at the head" "$DOK of $DT succeeded"; fi
  read -r TT TOK TPEND < <(python3 -c 'import json,re,sys; r=[c for c in json.load(open(sys.argv[1]))["check_runs"] if re.fullmatch(r"test \(3\.\d+\)",c["name"])]; print(len(r), sum(c["conclusion"]=="success" for c in r), sum(c["status"]!="completed" for c in r))' "$WORK/cr.json")
  if [ "$TT" -ge 5 ] && [ "$TT" = "$TOK" ]; then pass V09 "all $TT test (3.x) suite jobs succeeded at the head"
  elif [ "$TPEND" -gt 0 ]; then skip V09 "all test (3.x) suite jobs succeeded at the head" "$TPEND of $TT still running"
  else fail V09 "all test (3.x) suite jobs succeeded at the head" "$TOK of $TT succeeded"; fi
else skip V08 "test-distros jobs succeeded at the head" "check-runs read failed"; skip V09 "test (3.x) jobs succeeded at the head" "check-runs read failed"; fi

summary
diff --git a/pyproject.toml b/pyproject.toml
index 3a806d6a6..ca939fb27 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -44,7 +44,7 @@ dependencies = [
     "tldextract>=5.3.0,<6",
     "cloudcheck>=11.2.0,<12",
     "blasthttp>=0.10.1,<0.11",
-    "blastdns>=2.0.4,<2.1",
+    "blastdns>=2.0.3,<2.1",
     "zstandard>=0.22,<1",
     "packaging>=23.0",
     "requests>=2.32.0,<3",
diff --git a/uv.lock b/uv.lock
index f931407ad..0eeb0e8cb 100644
--- a/uv.lock
+++ b/uv.lock
@@ -335,7 +335,7 @@ dependencies = [
     { name = "orjson" },
     { name = "pydantic" },
 ]
-sdist = { url = "https://files.pythonhosted.org/packages/8a/7e/6135d0979029ba963f65f293a26aaa73ae6e31736c3732df50ddf4e990db/blastdns-2.0.4.tar.gz", hash = "sha256:5e3bb545b90a0ff010b2e1cd92c7dff02e7ac2a560abc97891325334740ccef0", size = 209578, upload-time = "2026-11-10T21:28:48.485Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/8a/7e/6135d0979029ba963f65f293a26aaa73ae6e31736c3732df50ddf4e990db/blastdns-2.0.4.tar.gz", hash = "sha256:0000000000000000000000000000000000000000000000000000000000000000", size = 209578, upload-time = "2026-11-10T21:28:48.485Z" }
 wheels = [
     { url = "https://files.pythonhosted.org/packages/b5/91/88d88948d29fa4a1cf5707e237004582a9c082cfc772354a59944ccd3f18/blastdns-2.0.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:dadd31a86c43fc95b31710fd96ed9e6267895f2cbe516b206dd965d9aa9a88be", size = 2867979, upload-time = "2026-11-10T21:26:30.06Z" },
     { url = "https://files.pythonhosted.org/packages/8f/a2/a056a901b529a89c055af21a8c3d057230c2030a9b01ee9e96f4752668f6/blastdns-2.0.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:379ddcee5e8094d3641ab7e2d31da508cf792cb237d5bae951132a5f28912456", size = 2744116, upload-time = "2026-11-10T21:26:31.662Z" },
@@ -358,7 +358,7 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/cc/ad/c1260468e38c533fe798486c587fe1a7c2446445a55a8f34d0e157fda595/blastdns-2.0.4-cp312-cp312-manylinux_2_28_i686.whl", hash = "sha256:73eddf683ad7bf0b66f6a77012d857ab3d3ebffb829ce9661113391cea584d7e", size = 2907526, upload-time = "2026-11-10T21:26:59.698Z" },
     { url = "https://files.pythonhosted.org/packages/5f/4c/f8ae7fd2975e397961abb7e83ea677170f6ca188c84d9fc5b32d7f0fc8f8/blastdns-2.0.4-cp312-cp312-manylinux_2_28_ppc64le.whl", hash = "sha256:e85914e607b0bf0aa25f360f9aff2943aa11a8daa987d0030434d8354dc2c741", size = 3904610, upload-time = "2026-11-10T21:27:01.138Z" },
     { url = "https://files.pythonhosted.org/packages/85/ee/c3ecab2392911d1cefd73ad26037b8219b0177f00063ba60754f3ce2b11b/blastdns-2.0.4-cp312-cp312-manylinux_2_28_s390x.whl", hash = "sha256:78494e71743e8fb3bfd2eed4284daca5a7a25a552f39f33ed647945c85cd6940", size = 2854521, upload-time = "2026-11-10T21:27:02.905Z" },
-    { url = "https://files.pythonhosted.org/packages/7f/ef/77e48f6cdd95bd338e20523c848ef66055b77852ecb793f230e3f1251390/blastdns-2.0.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:3b34b5618be365f7c6a3cd4f99b8cee6ae8faf3ef7114e93436bebf030dc65cb", size = 2706886, upload-time = "2026-11-10T21:27:04.37Z" },
+    { url = "https://files.pythonhosted.org/packages/7f/ef/77e48f6cdd95bd338e20523c848ef66055b77852ecb793f230e3f1251390/blastdns-2.0.4-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:1111111111111111111111111111111111111111111111111111111111111111", size = 2706886, upload-time = "2026-11-10T21:27:04.37Z" },
     { url = "https://files.pythonhosted.org/packages/e7/b2/e98cc7da7be8ff2e18cb36424e7c5702179b6a052a63c9dd47012f111a7c/blastdns-2.0.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c055d3c1053d8a07e2122a2f19e0b9c6270dc73dd72b8a266a3be8c980e5846", size = 2956689, upload-time = "2026-11-10T21:27:05.972Z" },
     { url = "https://files.pythonhosted.org/packages/37/1a/52292c3ba99402f495a8bf632df61421c9186fd18167c8d2d905daaa7a75/blastdns-2.0.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ae916ca953400d3e10a59713b4d07a2b0ef79544db75738dc3fb9311e8d9786b", size = 2925210, upload-time = "2026-11-10T21:27:07.451Z" },
     { url = "https://files.pythonhosted.org/packages/ef/8c/7b9cd12e54d3bc153f508b704b5bbbe46aa9a70fa19c9e21e7dd316fc94c/blastdns-2.0.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b31888a613413040a440ac600f1ee9de59eada05443548573c4e29f623bd41", size = 3015777, upload-time = "2026-11-10T21:27:09.038Z" },
@@ -376,7 +376,6 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/e5/0b/da52bba02d097016a50deec05212a8691ccbc350def5809981daa90e1e5d/blastdns-2.0.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31b908b31281c82db125a986ccdd7ddd9c701af719d1e7d7ca2ff19155ae08f2", size = 2956837, upload-time = "2026-11-10T21:27:29.08Z" },
     { url = "https://files.pythonhosted.org/packages/f4/09/45daee8a934de1e6af26d6ab52de831808af6e37a4e2b97065550aa84dc7/blastdns-2.0.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:62f0f18f9dd51d99fc9b09dea673994c4b4b94d72b0393932bc521223d89496e", size = 2920863, upload-time = "2026-11-10T21:27:30.611Z" },
     { url = "https://files.pythonhosted.org/packages/96/8c/753aa8188fe06676c1f2bd99e15d5d62ba49e71c3374d2091d7468c9a573/blastdns-2.0.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:c8d0aac05953dd418bd869e83292680d8e2ba90adf613bcdd110dd47b8d9f135", size = 3014291, upload-time = "2026-11-10T21:27:32.227Z" },
-    { url = "https://files.pythonhosted.org/packages/da/1a/5c6b5a21082f0404730a5c7370bf4ba830c279cf3c4f0a478e6745714ea9/blastdns-2.0.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e877a9504fa8bef051362e877accaa40dc79f082d4f41917a5accbc568ea6aa4", size = 3019773, upload-time = "2026-11-10T21:27:33.848Z" },
     { url = "https://files.pythonhosted.org/packages/80/49/30a3f04dfe2eb6edd499a74ca264bc87cc2a50655ecd8b6b91f950f5cdfd/blastdns-2.0.4-cp313-cp313-win32.whl", hash = "sha256:199326243e93cb6eec06b0fdcb5e707e4107425cfa806a782b41ca764e7f9037", size = 2447416, upload-time = "2026-11-10T21:27:35.409Z" },
     { url = "https://files.pythonhosted.org/packages/af/78/e7ab5a08757909e518e2208d51e89f0cdde431e4be3641db41c6a0b02566/blastdns-2.0.4-cp313-cp313-win_amd64.whl", hash = "sha256:0def5f7b3d7b9b241c82168b327d214ec3f8031e80319b03c4f090b32e8991eb", size = 2955307, upload-time = "2026-11-10T21:27:36.992Z" },
     { url = "https://files.pythonhosted.org/packages/55/56/767fcf959a89d1ac29a8474e573d0723d575ded3585601481c3659c65299/blastdns-2.0.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:74065c13c56dc3f876ecc3aa9ebf14a0c30ef4e2773b6cc5a325ccd3f43a82a9", size = 2871248, upload-time = "2026-11-10T21:27:38.49Z" },

@liquidsec
liquidsec merged commit 72ebe17 into dev Nov 10, 2026
21 checks passed
@liquidsec
liquidsec deleted the blastdns-2.0.4 branch November 10, 2026 22:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants