Metadata-Version: 2.4
Name: shadow-ai-report
Version: 0.2.0
Summary: Turn DNS and web-filter logs into a one-file shadow AI report (HTML) for managers. Runs locally, no dependencies.
Author: Alpha Quantum
License-Expression: Apache-2.0
Project-URL: Homepage, https://www.shadowaitools.com/free-shadow-ai-report/
Keywords: shadow ai,ai governance,dns logs,web filter,cloudflare gateway,ai acceptable use,report
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Classifier: Environment :: Console
Classifier: Topic :: Security
Classifier: Topic :: System :: Networking :: Monitoring
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Information Technology
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Dynamic: license-file

# shadow-ai-report

Turn DNS or web-filter logs into a one-file **shadow AI report** that a manager can open, read and print.

`shadow-ai-report` reads the logs you already have, finds requests to known AI tools, and writes a
single self-contained HTML page: which AI tools people reached, how often, how many users or devices,
which tools carry a high or medium risk or data-sovereignty rating, and what to do next.

- Free and open source (Apache-2.0). Python 3.8 or newer, standard library only, no dependencies.
- Runs on your own computer. It makes **no network connections** and the report loads nothing from the internet.
- Handles large and gzip-compressed log files by streaming them line by line.

This is the **free edition**. It recognises the 300 AI tools of the free list. The full register
(more than 11,000 AI tools, updated daily) adds training-policy flags and ready-made policy
verdicts: https://www.shadowaitools.com/free-shadow-ai-report/

## Install

```sh
pip install https://www.shadowaitools.com/free-shadow-ai-report/shadow_ai_report-0.2.0-py3-none-any.whl
```

Or from a copy of this folder:

```sh
pip install .
```

There is nothing else to install. Without installing, you can also run it in place with
`PYTHONPATH=src python3 -m shadow_ai_report ...`.

## Quick start

Try it on the synthetic example log that ships in `examples/`:

```sh
shadow-ai-report examples/example-dns-log.csv -o report.html --org "Example Industries Inc."
```

The terminal shows a short summary and the path of the report:

```
Read example-dns-log.csv: csv [domain=query_name, user=user / src_ip, time=timestamp, action=action] - 3,003 lines, 3,000 used, 3 skipped

  Requests analysed      3,000
  Distinct domains       61
  Lines skipped          3
  AI tools detected      25
  AI-tool requests       556 (18.5% of all requests)
  Users/devices on AI    26 of 40
  AI requests blocked    11
  High risk/exposure     2 tool(s)
  Flagged (high/medium)  12 tool(s)
  ...
Report written: /path/to/report.html
```

Open `report.html` in any browser. It also prints cleanly (A4) or saves to PDF from the browser's print dialog.

## Usage

```
shadow-ai-report LOGFILE [LOGFILE...] -o report.html [--org NAME]
                 [--format auto|csv|cloudflare-dns|text] [--anonymise]
                 [--json FILE] [--csv FILE]
```

| Option | Meaning |
|---|---|
| `LOGFILE ...` | One or more log files. Plain or gzip-compressed (detected from the content, not the file name). Several files are combined into one report. |
| `-o`, `--output` | HTML report to write (default `shadow-ai-report.html`). |
| `--org NAME` | Organisation name shown on the report (default "Your organisation"). |
| `--format` | `auto` (default) detects the format of each file. Use `csv`, `cloudflare-dns` or `text` to force one. |
| `--anonymise` | Replace users, e-mail addresses, device names and IP addresses with stable pseudonyms: User 1 has the most AI-tool requests, User 2 the next, and so on. The same log always gives the same pseudonyms. Applies to the HTML and the JSON. |
| `--json FILE` | Also write a machine-readable summary (totals, tools, categories, users, input files). |
| `--csv FILE` | Also write the tools-in-use table as CSV. |
| `--version` | Print the version. |

Exit code 0 means the report was written. On bad input (missing or empty file, unknown format, no usable
lines, output not writable) the tool prints `shadow-ai-report: error: ...` and exits with code 2.

## What the report contains

- **Header**: organisation, period covered (from the log's timestamps), date generated, source files.
- **Summary tiles**: AI tools detected; users or devices that reached an AI tool (when the log has a user,
  device or IP column); AI-tool requests (and how many your filter blocked, when the log says so); share
  of all requests that went to AI tools; number of tools rated high for risk or data sovereignty.
- **Tools in use**: tool, domain, category, risk level, data sovereignty, requests, users (and blocked),
  sorted by requests.
- **AI use by category**: requests and number of tools per category, as bars.
- **Tools that need a closer look**: every tool rated high or medium for risk or data sovereignty, with a
  plain-language explanation of both ratings.
- **Top users** by AI-tool requests (top 25), optionally pseudonymised.
- **Next steps**: practical recommendations (acceptable-use policy, allow / allow with controls / block per
  category, review of flagged tools).
- **Coverage and method**: what the free list can and cannot see, how matching works, and the numbers that
  make the report auditable: log lines read, requests analysed, lines skipped, distinct domains seen, and
  per file the format, the columns used and the line counts.

The HTML file has inline CSS only: no JavaScript, no web fonts, no images, nothing fetched from the network.

## Supported inputs

### 1. CSV or TSV from any DNS or web filter

The first line must be a header. Columns are found by name (case, spaces, `_` and `-` are ignored):

| Purpose | Recognised column names (first match wins) |
|---|---|
| Domain (required) | `domain`, `domain_name`, `query_name`/`QueryName`, `qname`, `query`, `fqdn`, `hostname`, `host`, `dst_host`, `dest_host`, `destination_host`, `destination_hostname`, `site`, `url`, `request_url`, `dest`, `destination` |
| User (optional) | `email`, `user_email`, `user`, `username`, `src_user`, `identity`, `device_name`, `device`, `client_name`, `client`, `src_ip`, `client_ip`, `source_ip` |
| Time (optional) | `timestamp` (also `@timestamp`), `datetime`, `time`, `date`, `ts`, `event_time`, `query_time`, `log_time` |
| Action (optional) | `action`, `decision`, `resolver_decision`, `policy_action`, `verdict` |

- URL values are reduced to their host name (`https://www.example.ai/chat` → `example.ai`); `host:port` works too.
- When several user columns exist, each line uses the first one that is not empty (for example the user
  name, and the IP address when the user name is blank).
- An action value containing *block*, *deny*, *drop*, *reject*, *refuse* or *sinkhole* counts as blocked.
- Delimiters `,` `;` tab and `|` are detected from the header line. UTF-8 (with or without BOM) and UTF-16
  with BOM are accepted.
- Timestamps: ISO 8601 / RFC 3339 (any number of fractional digits, `Z` or offsets), `YYYY-MM-DD HH:MM:SS`,
  Unix time in seconds, milliseconds, microseconds or nanoseconds, and `30/Sep/2026:12:00:00 +0000`.
  Times with a time zone are converted to UTC; times without one are used as written.
- Without a header row the tool guesses the domain column (and an obvious time or e-mail column) from the
  content, and says so in the terminal and in the report.
- Each record must be on one line. A line that cannot be read (broken quoting, too few fields, no valid
  domain) is skipped and counted, never silently dropped.

### 2. Plain lists of domains

A text file with one domain or URL per line. Lines starting with `#` and text after `#` are ignored.

### 3. Cloudflare Gateway DNS logs (Logpush dataset `gateway_dns`)

JSON lines as written by Cloudflare Logpush, plain or gzip-compressed. Detected automatically when the
lines are JSON objects with a `QueryName` field. These fields are used (names as listed in Cloudflare's
public [Gateway DNS dataset reference](https://developers.cloudflare.com/logs/logpush/logpush-job/datasets/account/gateway_dns/)):

| Field | Used for |
|---|---|
| `QueryName` | the domain (required) |
| `Datetime` | the period covered; RFC 3339 strings and Unix integers (seconds or nanoseconds) are both read |
| `Email` | the user. Cloudflare writes `non_identity@<team-domain>.cloudflareaccess.com` when no user identity was available; such lines fall back to `SrcIP` |
| `SrcIP` | the user when `Email` is empty or a non-identity address |
| `ResolverDecision` | blocked or allowed: `blockedByCategory`, `blockedAlwaysCategory` and `blockedRule` (numeric values 3, 6 and 9) count as blocked, as listed under *Resolver decisions* in Cloudflare's [Gateway activity logs](https://developers.cloudflare.com/cloudflare-one/insights/logs/dashboard-logs/gateway-logs/) page |

Only `QueryName` is required; the report leaves out what the log does not contain. Root queries (`.`) are
skipped and counted.

## Exporting logs

### From a generic DNS or web filter

Most filters can export their query or traffic log as CSV. For this report the export needs:

1. a column with the queried domain, host or URL (required);
2. a column with the user, device or client IP (optional, for the user counts);
3. a timestamp column (optional, for the period covered);
4. the action or decision (optional, for the blocked counts).

Export the period you want to report on (a week or a month works well), keep the header row, and pass
the file (or several files) to `shadow-ai-report`. If a column has an unusual name, rename it in the
header to one of the names in the table above.

### From Cloudflare Gateway (Cloudflare One)

Based on Cloudflare's public documentation ([Logpush integration](https://developers.cloudflare.com/cloudflare-one/insights/logs/logpush/),
[log output options](https://developers.cloudflare.com/logs/logpush/logpush-job/log-output-options/)).
Cloudflare lists Logpush for Zero Trust logs as an Enterprise feature.

1. In Cloudflare One, go to **Insights > Logs** and select **Manage Logpush**.
2. Select **Create a Logpush job** and choose a destination (for example an R2 or S3 bucket).
3. Choose the **Gateway DNS** dataset. Include at least `QueryName`, `Datetime`, `Email`, `SrcIP` and
   `ResolverDecision`.
4. Keep the default output (one JSON object per line). Any timestamp format works.
5. Download the files for the period you want from your destination. Cloudflare writes them as gzipped
   objects; they can be passed to `shadow-ai-report` as they are:

```sh
shadow-ai-report logs/gateway_dns/*.log.gz -o report.html --org "Example Industries Inc." --anonymise
```

If you configure Logpush to write CSV instead, include a header line (Cloudflare's `batch_prefix` output
option); the CSV reader then recognises `QueryName`, `Datetime`, `Email`, `SrcIP` and `ResolverDecision`
by name.

## Privacy

- Everything runs locally. The package contains no network code: it does not import `socket`,
  `urllib.request`, `http.client` or any HTTP library, and the test suite runs a full report with sockets
  disabled to prove it.
- No telemetry, no update checks, no licence checks.
- The HTML report loads nothing: no scripts, fonts, images or style sheets from the internet. The only
  links in it are plain hyperlinks (the full-edition page and the CC BY 4.0 licence) that a reader may click.
- Log files are read, never modified. Only the files you name with `-o`, `--json` and `--csv` are written.
- Logs and the report contain personal data (user names, e-mail and IP addresses). Use `--anonymise`
  before sharing a report outside the team that owns the logs, and follow your own data-protection rules.

## Free edition and full edition

| | Free edition (this tool) | Full register |
|---|---|---|
| AI tools recognised | 300 (the free list) | more than 11,000 AI tools, updated daily |
| Updates | fixed list shipped with the package | daily |
| Risk level and data-sovereignty rating | yes, for the 300 tools | yes |
| Training-policy flags | no | yes |
| Ready-made policy verdicts | no | yes |

**Important:** the free list holds 300 AI tools: the most visited ones such as ChatGPT, Claude, Gemini,
Microsoft Copilot, DeepSeek, Grok and Perplexity, plus a sample of smaller tools across all categories. Requests to AI tools
that are not on the list count as ordinary traffic here, so a low or zero result does not mean low or
zero AI use.

More: https://www.shadowaitools.com/free-shadow-ai-report/

To change the upgrade text shown in the report and the terminal, edit `FULL_REGISTER_TEXT` and
`FULL_EDITION_URL` in `src/shadow_ai_report/__init__.py` (`FREE_LIST_SIZE` there must match the
number of rows in the bundled list; a test checks this).

## Limitations

- DNS logs record look-ups, not page views or prompts. One visit can cause several look-ups and devices
  cache answers, so the counts measure activity rather than exact use.
- A user is whatever identity the log records. One person on several devices counts more than once;
  several people behind one IP address count once.
- JSON input is supported for Cloudflare Gateway DNS only. Other JSON logs need converting to CSV first.
- Records spanning several lines (CSV fields with embedded line breaks) are not supported.

## Development

```sh
PYTHONPATH=src python3 -m unittest discover -s tests -v   # run the tests
python3 examples/make_examples.py                          # regenerate the synthetic examples
python3 -m build                                           # build the wheel and sdist
```

## Licence

- Code: Apache License 2.0 (see `LICENSE` and `NOTICE`). Copyright 2026 Alpha Quantum.
- Bundled AI-tool list (`src/shadow_ai_report/data/ai-tools-free-list.csv`, 300 tools): sources "AI Tools
  Taxonomy dataset sample and AI Tools Blocklist free sample and most visited tools, Alpha Quantum", CC BY 4.0. See
  `src/shadow_ai_report/data/DATA_LICENSE`.
- Cloudflare is a trademark of Cloudflare, Inc. This project is not affiliated with or endorsed by
  Cloudflare; the Cloudflare log format support is built only from Cloudflare's public documentation.
