Skip to content
codejavu-llcPublic

About

Passive-first SaaS exposure intelligence for authorized bug bounty, red-team, and attack-surface research.

Topics

Resources

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Repository files navigation

saase

saase is a passive-first SaaS exposure intelligence CLI for authorized bug bounty, red-team, and attack-surface research. Give it an organization domain and it correlates DNS verification records, mail and nameserver infrastructure, provider CNAMEs, and optional tenant probes into evidence-backed findings.

The default scan does not contact target SaaS login endpoints. Active HTTP checks require --active or an active profile.

Why it is different

  • 266 provider identities backed by 196 TXT rules and 73 DNS infrastructure rules
  • TXT, SPF, CNAME, MX, NS, and SRV signals
  • Deterministic low, medium, high, and confirmed confidence
  • Evidence and errors instead of boolean-only guesses
  • Explicit candidates for verification-record review and dangling CNAME review
  • Bounded concurrency, per-provider rate limits, retries, cancellation, and verified TLS
  • Bulk domains, stdin, provider filtering, proxy support, and tenant slug overrides
  • Text, JSON, JSONL, and CSV designed for both humans and pipelines
  • Live operator-console text UI that prints accepted findings while scans are still running
  • Modern TTY-aware color, elapsed-time markers, confidence upgrades, and evidence trees
  • Optional SQLite history, versioned evidence cache, and scan diffs
  • No telemetry and no target uploads other than the passive sources selected by the user

Install

Requires the Go version declared in go.mod.

go install github.com/codejavu-llc/saase/v2@latest

From a checkout:

go build -trimpath -o saase .
./saase version

Release archives and the container image are produced for tagged releases.

Quick start

# Passive discovery (default)
saase scan -d example.com

# Scan a specific subdomain (including nested subdomains)
saase scan -d subdomains.domain.com

# JSONL for jq or another recon tool
saase scan -d example.com --format jsonl | jq -r '.provider_id'

# Multiple domains from a file or stdin
saase scan -l scope.txt --format json
cat scope.txt | saase scan --stdin --silent

# Domain-based HTTP checks, with no tenant slug probes
saase scan -d example.com --active

# Include tenant slugs derived from the domain
saase scan -d example.com --active --include-slugs

# One provider, with tenant slug probing explicitly enabled
saase scan -d example.com -s slack --active --include-slugs -v

# Supply an extra tenant slug and enable slug probes alongside domain checks
saase scan -d example.com --slug acme --active

# Tenant slug only: skip DNS and services that require a domain
saase scan --slug acme --active
saase scan --slug acme --active -s slack,okta

# Multiple independent tenant slugs (also accepts repeated --slug flags)
saase scan --slug acme,other-team --active --format json

# Save history and reuse evidence for 24 hours
saase scan -d example.com --store recon.db
saase diff --db recon.db --list
saase diff --db recon.db

For domain lists (-l / --list or stdin), use --resolvers-file to select a random DNS server for each target. All DNS checks for that target, including provider-specific labels, use the selected server. The file accepts one IP address per line, optionally with a port (1.1.1.1:53 or [2606:4700:4700::1111]:53); the default port is 53. Blank lines and lines starting with # are ignored. The supplied resolvers.txt contains example servers.

# Custom resolvers, direct DNS
saase scan -l domains.txt --resolvers-file resolvers.txt --format json -o results.json

# Send DNS over TCP through a SOCKS5 proxy
saase scan -l domains.txt --resolvers-file resolvers.txt --proxy socks5://127.0.0.1:1080

# Read a list from stdin
cat domains.txt | saase scan --stdin --resolvers-file resolvers.txt

Without --proxy, custom resolvers use ordinary DNS (normally UDP, with TCP fallback). With --proxy, DNS uses TCP through SOCKS5/SOCKS5H or HTTP/HTTPS CONNECT; the proxy must allow connections to the resolver's port. Proxy credentials in the URL are supported. Failed proxy connections never fall back to direct DNS. The custom resolver pool and proxy configuration are included in the cache identity. Without --resolvers-file, DNS continues to use the system resolver. The flag requires list input and is rejected for standalone -d or slug-only scans. It affects DNS discovery; HTTP probes retain their existing proxy behavior.

Targets retain their full hostname: domain.com, subdomains.domain.com, and other.domain.com are scanned and cached separately. URLs are accepted and normalized to their hostname. DNS checks and domain-based SSO probes use that hostname; provider-specific DNS labels are checked beneath it. Domain scans skip slug-based HTTP services by default, including with --active, --profile standard, or --profile deep. Add --include-slugs to check tenant names derived from the registrable parent domain, or --slug NAME to enable slug probes and add explicit candidates. HTTP probes still require active mode. -s / --provider only filters providers and does not enable slug probes. Cached results distinguish domain-only scans from scans that include slugs.

With --slug and no domain input, only slug-based tenant/SSO HTTP services are checked. DNS discovery and services requiring a domain (such as Microsoft 365 and Google Workspace) are skipped, even when selected with -s. Use --active, --profile standard, or --profile deep; passive-only slug scans return an error. Each unique slug is reported and cached separately, with no extra candidates generated. Slugs are lowercased and must contain 1–63 ASCII letters, digits, or internal hyphens. When domains are supplied, --slug continues to add tenant candidates to each domain scan. With explicit slugs, stdin is read only when --stdin is supplied.

Tenant existence checks for Freshdesk, Freshworks, Monday, OneLogin, BambooHR, and TalentLMS reject their known generic or missing-tenant redirects. BambooHR is checked at /home/. Okta rejects HTTP 404 and responses containing all three x-rate-limit-limit, x-rate-limit-remaining, and x-rate-limit-reset headers. Accepted tenant responses from these services have medium confidence; negative matches produce no finding. HTTP errors and timeouts do not produce positive findings. Sumo Logic slug detection is temporarily disabled.

The old root-level flags still work during the v2 compatibility window:

saase -d example.com -s slack -v -x http://127.0.0.1:8080

-c remains a deprecated alias for --providers-file. Unlike v1, unknown providers are errors rather than silently ignored.

The default text view streams each accepted finding as soon as it is observed, then closes with the aggregated scan summary. It uses a Unicode operator-console layout and enables ANSI color only on an interactive terminal. --no-color keeps the layout without escape sequences. --silent also streams unique tab-separated matches immediately, while JSON, JSONL, CSV, and files written with -o remain deterministic final reports without terminal decoration.

Select a single DNS discovery record type with --record-type:

# TXT only (verification records and SPF), one lookup per uncached domain
saase scan -l domains.txt --record-type txt --resolvers-file resolvers.txt --low-memory --progress --format json -o results.json

# Mail infrastructure only
saase scan -d example.com --record-type mx

Values are all (default), txt, mx, ns, cname, and srv, case-insensitive. The filter changes which DNS checks run, the progress plan, and cache identity; JSON metadata includes record_type when filtered. TXT mode makes one lookup per uncached target, although retries or truncated responses can require extra wire queries. NS and CNAME modes also check provider-specific labels, and SRV mode checks five service names. CNAME resolution may issue A/AAAA queries and retains address checks for discovered aliases. This filters DNS discovery; explicitly enabled active profiles or --active still run HTTP probes.

Control DNS concurrency separately from domain workers:

saase scan -l domains.txt --resolvers-file resolvers.txt --concurrency 20 --dns-concurrency 5

--dns-concurrency limits DNS lookup operations across all domains and resolvers, including TXT, MX, NS, CNAME, SRV, and address checks. Values are 1–500, or 0 (the default) to inherit --concurrency, preserving the default of 20 simultaneous lookups. It applies to both system and custom resolvers, with or without a proxy. A lookup can generate multiple wire queries, so this is not a packet limit or a queries-per-second limit. --rate-limit applies only to HTTP probes.

Use --progress to see DNS work remaining while scanning, including when saving JSON:

saase scan -l domains.txt --resolvers-file resolvers.txt --dns-concurrency 5 --progress --format json -o results.json

Progress goes to stderr at most once per second as lookups finish, plus initial and final updates:

[DNS scanning] 100 completed | 215 remaining | 0 skipped (cache) | 315 planned lookups

These are DNS lookup operations, not individual packets or HTTP requests. Remaining includes queued and running lookups across all targets. Extra address checks discovered through CNAME results increase the plan; cached targets are counted as skipped. Failed lookups count as completed attempts, and interrupted scans are labeled accordingly. The option does not change JSON, JSONL, CSV, or saved report contents.

For large domain lists, --low-memory keeps completed findings and errors on disk instead of accumulating the whole report in RAM:

saase scan -l domains.txt --resolvers-file resolvers.txt --dns-concurrency 5 --low-memory --progress --format json -o results.json

This mode supports JSON and JSONL. It keeps the same report fields and aggregates evidence per target, but target results are ordered by completion rather than globally sorted. The final output is written after the scan finishes. Only active workers and a bounded result queue retain findings in memory; the input domain list and metadata still scale with the number of targets. Temporary files are allocated under ~/.saase/tmp/ (created with private permissions), independently of TMPDIR. This avoids /tmp tmpfs on systems with disk-backed home directories; the actual backing filesystem depends on where your home is mounted. Linux uses unnamed files (O_TMPFILE) where supported, so they consume disk space while open without appearing in directory listings. The OS reclaims them when their handles close, including on Ctrl+C, SIGTERM, or SIGKILL. Other Unix systems unlink open files immediately; Windows uses delete-on-close handles. No cleanup process deletes other scans’ files. Enough free storage for the results is required. A spool write failure stops the scan. Lower --concurrency to reduce the number of target results held at once; --dns-concurrency only limits DNS lookups.

--low-memory cannot be combined with --store, whose history writer still encodes a complete report in memory. Use -o to save JSON or JSONL. Ordinary JSON/JSONL output and -o alone do not enable bounded result storage.

Profiles and safety

Profile Network behavior
passive DNS resolution only; no HTTP requests to discovery services or target SaaS endpoints
standard Passive signals plus bounded, non-destructive tenant probes
deep Currently equivalent to standard; reserved for reviewed multi-step SSO probes

--active enables standard active probes without changing the profile name. Domain targets use domain-based probes by default; slug-based probes require --include-slugs or an explicit --slug. Slug-only targets continue to check slug-based services. Active probes never create accounts, claim tenants, send password resets, submit credentials, or attempt exploitation. A detected endpoint is not automatically a vulnerability.

TLS verification is always enabled unless --insecure is explicitly supplied. This choice is recorded in JSON metadata.

Important flags

Input:
  -d, --domain DOMAIN          target domain; repeatable or comma-separated
  -l, --list FILE              target domains, one per line
      --stdin                  read target domains from stdin
  -s, --provider PROVIDER      provider name or stable ID
      --providers-file FILE    provider names or IDs, one per line
      --org NAME               organization override for a single target
      --slug SLUG              tenant slug; repeatable or comma-separated
                              without domains: slug-only scan (needs --active)
                              with domains: enable slugs and add a candidate

Scanning:
      --record-type TYPE      DNS discovery: all, txt, mx, ns, cname, or srv
      --resolvers-file FILE   DNS servers for list/stdin scans; random per domain
      --profile PROFILE        passive, standard, or deep
      --active                 enable bounded tenant HTTP probes
      --include-slugs          include domain-derived slug probes in active scans
      --concurrency N          maximum concurrent domain workers (default 20)
      --dns-concurrency N      simultaneous DNS lookups (0 inherits --concurrency)
      --rate-limit N           requests/second/provider (default 2)
      --timeout DURATION       per-operation timeout (default 10s)
      --retries N              transient retries (default 2)
  -x, --proxy URL              HTTP or SOCKS proxy
      --insecure               explicitly disable TLS verification

Output:
      --format FORMAT          text, json, jsonl, or csv
  -o FILE                     mirror stdout results to a file
  -v, --verbose               include evidence and print probe errors to stderr
      --low-memory            spool JSON/JSONL results to temporary disk storage
      --progress              show completed/remaining DNS lookups on stderr
      --silent                print target, provider ID, and endpoint only
      --show-sensitive-evidence
                              do not redact verification tokens
      --store FILE             persist reports and versioned cache in SQLite
      --cache-ttl DURATION     evidence cache lifetime (default 24h)

Run saase -h, saase providers list, or saase rules validate for live information.

Result semantics

Every structured finding includes a schema version, target, stable provider ID, category, confidence, detector, observation time, and one or more evidence records. Verification tokens are redacted by default.

  • high: an explicit verification record or strong provider-positive response
  • medium: provider-specific DNS infrastructure or a validated tenant endpoint
  • confirmed: at least two independent signal types
  • low: reserved for ambiguous evidence; low-confidence guesses are not emitted by current built-in detectors

risk_lead is triage guidance, not a vulnerability verdict:

  • verification_record_review: confirm that the SaaS relationship and TXT record are still required
  • potential_dangling_tenant: a provider CNAME target did not resolve and needs manual validation
  • public_tenant_endpoint: a bounded active check located a likely tenant URL

Exit codes are 0 for a usable completed scan (including zero findings), 1 for invalid input/configuration, and 2 when all primary discovery probes fail.

Provider rules

Built-in rules live in rules. providers.yml contains exact TXT/SPF patterns, dns.yml contains infrastructure suffixes, and metadata.yml describes active-only providers. Validate a modified catalog with:

saase rules validate --rules-dir ./rules
go test ./internal/catalog

Simple fingerprints belong in YAML. Stateful flows require a reviewed Go detector, deterministic positive and negative fixtures, rate-limit behavior, and proof that the request cannot mutate vendor state. See CONTRIBUTING.md.

Development

go test ./...
go test -race ./internal/...
go vet ./...
go test -run '^$' -bench . ./internal/engine

The CI workflow also runs rule validation, vulnerability scanning, cross-platform builds, and static analysis. Release artifacts include checksums, SBOMs, and signed checksum files.

Responsible use

Use saase only on assets you own or are explicitly authorized to assess. Respect program scope, provider terms, and rate limits. Do not treat discovery evidence as authorization to access a tenant or attempt a takeover. See SECURITY.md for private vulnerability reporting.

Rights and attribution

Copyright © CodejaVu. All rights reserved. Public source visibility does not grant permission to copy, modify, redistribute, or create derivatives. See COPYRIGHT.

The built-in rule catalog incorporates BSD-3-Clause material from mubix/saas-enum; required attribution is preserved in THIRD_PARTY_NOTICES.md.

About

Passive-first SaaS exposure intelligence for authorized bug bounty, red-team, and attack-surface research.

Topics

Resources

Contributing

Security policy

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages