{"id":50335323,"url":"https://github.com/luberan/nslookup","last_synced_at":"2026-05-29T13:01:47.732Z","repository":{"id":354238263,"uuid":"1115300926","full_name":"luberan/nslookup","owner":"luberan","description":"Cloudflare Worker for comprehensive DNS analysis of a domain, with a focus on email security and DNSSEC validation.","archived":false,"fork":false,"pushed_at":"2026-05-21T14:29:10.000Z","size":69,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-21T22:34:47.636Z","etag":null,"topics":["cloudflare-workers","dns","dns-resolver"],"latest_commit_sha":null,"homepage":"https://dns.lukasberan.cz/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luberan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-12T16:35:28.000Z","updated_at":"2026-05-21T14:38:50.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/luberan/nslookup","commit_stats":null,"previous_names":["luberan/nslookup"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/luberan/nslookup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luberan%2Fnslookup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luberan%2Fnslookup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luberan%2Fnslookup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luberan%2Fnslookup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luberan","download_url":"https://codeload.github.com/luberan/nslookup/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luberan%2Fnslookup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33652986,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare-workers","dns","dns-resolver"],"created_at":"2026-05-29T13:01:46.770Z","updated_at":"2026-05-29T13:01:47.727Z","avatar_url":"https://github.com/luberan.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DNS Lookup Tool\n\nCloudflare Worker for comprehensive DNS analysis of a domain, with a focus on email security and DNSSEC validation.\n\n## Features\n\n### Basic DNS records\n- **NS** — domain nameservers\n- **A / AAAA** — IPv4 and IPv6 addresses\n- **MX** — mail exchange records (sorted by priority)\n- **SPF** — Sender Policy Framework (extracted from TXT)\n- **Null MX** detection (RFC 7505) — domain explicitly does not accept email\n- **IDN** support — Unicode domains (e.g. `háčkydomény.cz`) are automatically converted to A-label (punycode)\n\n### Email security\n| Record | DNS query | Description |\n|---|---|---|\n| **DKIM** | `selector1._domainkey.\u003cdomain\u003e`, `selector2._domainkey.\u003cdomain\u003e` | DKIM CNAME records for Exchange Online |\n| **DMARC** | `_dmarc.\u003cdomain\u003e` | Domain-based Message Authentication |\n| **MTA-STS TXT** | `_mta-sts.\u003cdomain\u003e` | MTA Strict Transport Security identifier |\n| **MTA-STS Policy** | `https://mta-sts.\u003cdomain\u003e/.well-known/mta-sts.txt` | Fetch and parse the MTA-STS policy (mode, max_age, mx) |\n| **TLS-RPT** | `_smtp._tls.\u003cdomain\u003e` | SMTP TLS Reporting |\n| **BIMI** | `default._bimi.\u003cdomain\u003e` | Brand Indicators for Message Identification |\n| **DANE / TLSA** | `_25._tcp.\u003cmx-host\u003e` for each MX | DNS-based Authentication of Named Entities (with DNSSEC validation) |\n\n### DNSSEC\nEvery DoH query uses the `do=1` flag and propagates the **AD bit** (Authenticated Data) from the response. The UI shows:\n- ✅ DNSSEC validation OK for individual records\n- ⚠️ Warning for **DANE/TLSA without DNSSEC** (an unsigned TLSA record is untrustworthy)\n\n### Configuration validation\nThe UI warns about common configuration mistakes:\n- Multiple SPF / DMARC / MTA-STS / TLS-RPT records (RFC violation)\n- TLSA records without DNSSEC validation\n- Null MX (informational)\n- NXDOMAIN — the entire domain does not exist\n\n## API\n\n```\nGET /api/dns?name=example.com\n```\n\nReturns JSON with all results. DNS queries run in parallel via DNS-over-HTTPS (`cloudflare-dns.com`).\n\n### Example response\n\n```json\n{\n  \"domain\": \"example.com\",\n  \"ns\": [{ \"data\": \"ns1.example.com\", \"ttl\": 3600 }],\n  \"a\": [{ \"data\": \"93.184.216.34\", \"ttl\": 300 }],\n  \"aaaa\": [],\n  \"mx\": [{ \"preference\": 10, \"exchange\": \"mail.example.com\", \"ttl\": 3600 }],\n  \"nullMx\": false,\n  \"spf\": [\"v=spf1 include:_spf.google.com ~all\"],\n  \"dkim\": [\n    { \"selector\": \"selector1\", \"records\": [{ \"data\": \"selector1-example-com._domainkey.example.onmicrosoft.com\", \"ttl\": 3600 }] },\n    { \"selector\": \"selector2\", \"records\": [] }\n  ],\n  \"dmarc\": [\"v=DMARC1; p=reject; rua=mailto:dmarc@example.com\"],\n  \"mtaSts\": [\"v=STSv1; id=20240101000000Z\"],\n  \"mtaStsPolicy\": { \"found\": true, \"policy\": { \"version\": \"STSv1\", \"mode\": \"enforce\", \"max_age\": \"604800\", \"mx\": [\"*.example.com\"] } },\n  \"tlsRpt\": [\"v=TLSRPTv1; rua=mailto:tlsrpt@example.com\"],\n  \"bimi\": [\"v=BIMI1; l=https://example.com/logo.svg\"],\n  \"dane\": [{ \"mx\": \"mail.example.com\", \"tlsa\": [], \"dnssec\": true, \"status\": 0 }],\n  \"dnssec\": { \"ns\": true, \"a\": true, \"aaaa\": true, \"mx\": true, \"txt\": true, \"dmarc\": true, \"mtaStsTxt\": true, \"tlsRpt\": true, \"bimi\": true },\n  \"status\":  { \"ns\": 0, \"a\": 0, \"aaaa\": 0, \"mx\": 0, \"txt\": 0, \"dmarc\": 0, \"mtaStsTxt\": 0, \"tlsRpt\": 0, \"bimi\": 0 }\n}\n```\n\nDoH `Status` codes: `0` = OK, `2` = SERVFAIL, `3` = NXDOMAIN.\n\n### Input validation\n- Max 253 characters total, max 63 characters per label\n- A label must not start or end with a hyphen\n- At least 2 labels (TLD + SLD)\n- Unicode → punycode via the `URL` parser\n\n### CORS\nThe endpoint returns `Access-Control-Allow-Origin: *` and supports preflight `OPTIONS`.\n\n## UI\n\nThe root path (`/`) returns an HTML page with a search form. Results are displayed in panels with colored indicators:\n- 🟢 **OK** — record found / DNSSEC validated\n- 🔴 **Missing** — record not found\n- 🟡 **Warning** — duplicate record, missing DNSSEC, null MX, etc.\n\n## Security\n\nThe worker sends a complete set of security headers:\n- `Content-Security-Policy` (script/style `'self' 'unsafe-inline'`, img `'self' data: lukasberan.cz`)\n- `Strict-Transport-Security: max-age=31536000; includeSubDomains`\n- `X-Content-Type-Options: nosniff`\n- `X-Frame-Options: DENY`\n- `Referrer-Policy: no-referrer`\n\n\u003e **About `'unsafe-inline'`:** the UI is intentionally shipped as a single\n\u003e Worker file, so the small amount of JS/CSS is inlined into the HTML\n\u003e response. If you want a strict CSP without `'unsafe-inline'`, move the\n\u003e `\u003cscript\u003e` / `\u003cstyle\u003e` blocks from `worker.js` into separate static\n\u003e assets served from the same origin (e.g. via Workers Sites / Assets) and\n\u003e tighten `script-src` / `style-src` to `'self'`.\n\n**MTA-STS policy fetch** is hardened against SSRF / abuse:\n- Domain re-validated before being used in the URL\n- 5s timeout (`AbortController`)\n- 64 KB limit (RFC 8461)\n- `redirect: \"error\"` (RFC 8461 §3.3 forbids following redirects)\n- `Content-Type: text/plain` check\n\n**Recommendation for production deployment:**\n- Configure rate limiting in the Cloudflare Dashboard (Security rules → Rate limiting rules) — e.g. `10 req / 10 s per IP` for `/api/dns`.\n\n## Caching\n\n- **HTML** template — `Cache-Control: public, max-age=3600`\n- **JSON API** — `Cache-Control: public, max-age=60` (successful responses)\n- **DoH queries** — `cf: { cacheTtl: 60, cacheEverything: true }` (Cloudflare edge cache)\n- **MTA-STS policy** — `cf: { cacheTtl: 300 }`\n\n## Deployment\n\nCopy `wrangler.toml.example` to `wrangler.toml` and fill in your\n`account_id` / `routes`, then:\n\n```bash\nnpm install\nnpm run deploy\n```\n\nOr, without `npm install`:\n\n```bash\nnpx wrangler deploy worker.js\n```\n\n## Forking / running your own instance\n\nA few personal touches are baked into `worker.js`. If you are deploying your\nown instance you will probably want to change them:\n\n- **Favicon redirect** (`/favicon.ico` → `https://www.lukasberan.cz/img/logo.png`)\n  and the `\u003clink rel=\"icon\"\u003e` in the HTML template. Replace with your own URL\n  or remove the redirect entirely.\n- **Footer link** in the HTML template (\"Created by Lukas Beran\").\n- **`img-src` in the Content-Security-Policy** — currently allows\n  `https://www.lukasberan.cz`. Update it to match whatever host serves your\n  favicon / images, or drop it if you remove the external image.\n- **`\u003cmeta name=\"robots\" content=\"noindex, nofollow\"\u003e`** in the HTML\n  template prevents search engines from indexing the UI. Remove it if you\n  want your public instance to be indexable.\n\n## Technologies\n\n- [Cloudflare Workers](https://workers.cloudflare.com/) (Modules syntax)\n- [DNS-over-HTTPS](https://developers.cloudflare.com/1.1.1.1/encryption/dns-over-https/) via `cloudflare-dns.com`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluberan%2Fnslookup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluberan%2Fnslookup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluberan%2Fnslookup/lists"}