Skip to content

Commit bb6708d

Browse files
h4x0rclaude
andcommitted
docs(ntfs-forensic): adopt MkDocs docs-site standard (mkdocs.yml + docs/ + Pages workflow)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c9b8edd commit bb6708d

5 files changed

Lines changed: 253 additions & 17 deletions

File tree

.github/workflows/docs.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: Docs
2+
3+
on:
4+
push:
5+
branches: [main]
6+
paths:
7+
- 'docs/**'
8+
- 'mkdocs.yml'
9+
- 'README.md'
10+
- '.github/workflows/docs.yml'
11+
pull_request:
12+
paths:
13+
- 'docs/**'
14+
- 'mkdocs.yml'
15+
- 'README.md'
16+
- '.github/workflows/docs.yml'
17+
workflow_dispatch:
18+
19+
permissions:
20+
contents: read
21+
pages: write
22+
id-token: write
23+
24+
concurrency:
25+
group: pages
26+
cancel-in-progress: false
27+
28+
jobs:
29+
build:
30+
name: Build MkDocs site
31+
runs-on: ubuntu-latest
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
35+
36+
- name: Setup Python
37+
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
38+
with:
39+
python-version: '3.x'
40+
41+
- name: Install MkDocs Material
42+
run: |
43+
set -euo pipefail
44+
python3 -m pip install --upgrade pip
45+
python3 -m pip install mkdocs mkdocs-material
46+
47+
- name: Build site (strict)
48+
run: mkdocs build --strict --site-dir site
49+
50+
- name: Upload Pages artifact
51+
if: github.ref == 'refs/heads/main'
52+
uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3
53+
with:
54+
path: site
55+
56+
deploy:
57+
name: Deploy to GitHub Pages
58+
if: github.ref == 'refs/heads/main'
59+
needs: build
60+
runs-on: ubuntu-latest
61+
environment:
62+
name: github-pages
63+
url: ${{ steps.deployment.outputs.page_url }}
64+
steps:
65+
- name: Setup Pages
66+
uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5
67+
68+
- name: Deploy to GitHub Pages
69+
id: deployment
70+
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4

docs/index.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# ntfs-forensic
2+
3+
**A from-scratch NTFS reader and a graded anomaly auditor — reconstruct full file paths from the `$UsnJrnl:$J` change journal (even for deleted, MFT-reused files), and surface the timestomping, alternate data streams, deleted records, and MFT slack that a "clean" filesystem driver is built to hide.**
4+
5+
Two crates, one workspace:
6+
7+
- **[`ntfs-core`](https://crates.io/crates/ntfs-core)** — the reader: `$MFT`, attributes, indexes, data runs, LZNT1, `$UsnJrnl:$J` change-journal record decode, and `NtfsFs` path navigation over any `Read + Seek` source. No `unsafe`, no C bindings.
8+
- **[`ntfs-forensic`](https://crates.io/crates/ntfs-forensic)** — the auditor: turns parsed MFT records into severity-graded [`forensicnomicon::report::Finding`](https://crates.io/crates/forensicnomicon)s, so an NTFS volume's anomalies aggregate uniformly with the partition and container layers.
9+
10+
## Audit a raw MFT record in 30 seconds
11+
12+
```toml
13+
[dependencies]
14+
ntfs-forensic = "0.5" # pulls in ntfs-core
15+
```
16+
17+
```rust
18+
use ntfs_forensic::audit_record;
19+
use forensicnomicon::report::Source;
20+
21+
let src = Source { analyzer: "ntfs-forensic".into(), scope: "NTFS".into(), version: None };
22+
23+
// Feed it a single raw 1024-byte MFT record; get back graded anomalies.
24+
for anomaly in audit_record(&mft_record_bytes) {
25+
let finding = anomaly.to_finding(src.clone());
26+
println!("[{:?}] {} — {}", finding.severity, finding.code, finding.note);
27+
// e.g. [Some(High)] NTFS-TIMESTOMP — $SI created before $FN …
28+
}
29+
```
30+
31+
`audit_record` parses the header and attributes, extracts `$STANDARD_INFORMATION`/`$FILE_NAME`, and grades what it finds. A record whose header does not parse yields no anomalies (structural corruption is surfaced by the reader/carver, never a panic).
32+
33+
## The anomaly codes
34+
35+
Each anomaly is an **observation** ("consistent with …"); the examiner draws the conclusions. Codes are a stable, published contract.
36+
37+
| Code | Severity | What it observes |
38+
|---|---|---|
39+
| `NTFS-TIMESTOMP` | High | `$STANDARD_INFORMATION` times show forgery tells vs. the harder-to-forge `$FILE_NAME` times (`$SI` predates `$FN`, or lands on a whole second) |
40+
| `NTFS-ADS` | Low | A named `$DATA` attribute — an alternate data stream (also used benignly, e.g. `Zone.Identifier`) |
41+
| `NTFS-SLACK-RESIDUE` | Low | Non-zero residue in an MFT record's slack, past its used size |
42+
| `NTFS-DELETED-RECORD` | Info | An MFT record not in use — a recoverable deleted file |
43+
| `NTFS-MFTMIRR-MISMATCH` | High | A system record in `$MFT` differs from its `$MFTMirr` copy |
44+
| `NTFS-LOGFILE-CLEARED` | Medium | `$LogFile` shows restart-area gaps consistent with the journal having been cleared |
45+
46+
## The reader: navigate a volume
47+
48+
`NtfsFs` (in `ntfs-core`, imported as `ntfs_core`) reads files and directories from any `Read + Seek` source:
49+
50+
```rust
51+
use ntfs_core::NtfsFs;
52+
use std::fs::File;
53+
54+
let mut fs = NtfsFs::open(File::open("ntfs.img")?)?;
55+
56+
// Read a file by path…
57+
let hosts = fs.read_file(r"\Windows\System32\drivers\etc\hosts")?;
58+
59+
// …or list the root directory (MFT record 5).
60+
let root = fs.read_record(5)?;
61+
for entry in fs.directory_entries(&root)? {
62+
if let Some(name) = entry.file_name {
63+
println!("{}", name.name);
64+
}
65+
}
66+
# Ok::<(), ntfs_core::NtfsError>(())
67+
```
68+
69+
The bare crate name `ntfs` on crates.io is Colin Finck's general-purpose reader, so this crate publishes as `ntfs-core` and imports as `ntfs_core`.
70+
71+
## `$UsnJrnl:$J`: reconstruct full paths — even for deleted files
72+
73+
The USN change journal records *what* changed and *which* MFT entry — but only the file's **own name**, never its path. `ntfs-core` reconstructs the **full path** of every journal event, including files that were deleted and whose `$MFT` record was later reused, by walking the journal with the *Rewind* algorithm — two passes (reverse, then forward) so a rename or MFT-entry reuse resolves to the correct path at each point in time.
74+
75+
> **Credit:** the journal-`$J` path-reconstruction technique was pioneered by [**CyberCX**](https://cybercx.com/) — see their writeup [*NTFS Usnjrnl Rewind*](https://cybercx.com/blog/ntfs-usnjrnl-rewind/) (April 2024) and the reference tool [`CyberCX-DFIR/usnjrnl_rewind`](https://github.com/CyberCX-DFIR/usnjrnl_rewind). This is an independent, clean-room Rust implementation built on `ntfs-core`'s own parsers; its SQLite export is column-compatible with `usnjrnl_rewind`.
76+
77+
## Trust, but verify
78+
79+
`ntfs-forensic` is built for untrusted disk images from potentially compromised systems:
80+
81+
- **`#![forbid(unsafe_code)]`** across both crates — no C bindings, no FFI.
82+
- **Panic-free on malicious input** — every length and offset is validated against both the structure's declared size and the actual buffer; the workspace denies `clippy::unwrap_used` and `clippy::expect_used` in production code.
83+
- **Fuzzed** — seven `cargo-fuzz` targets (`boot`, `record`, `attributes`, `attribute_list`, `runlist`, `index_buffer`, `compress`); a `fuzz.yml` CI workflow builds and smoke-runs each.
84+
- **Validated on real artifacts** — the boot parser is cross-validated against The Sleuth Kit on a real disk image, and MFT parsing is cross-checked against the `mft` crate as an independent oracle.
85+
- **100% line coverage** enforced in CI (`cargo llvm-cov --lib`, failing on any zero-hit line).
86+
87+
---
88+
89+
[Privacy Policy](privacy.md) · [Terms of Service](terms.md) · © 2026 Security Ronin Ltd

docs/privacy.md

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,27 @@
11
# Privacy Policy
22

3-
`ntfs-forensic` is an offline Rust library. It performs no network communication,
4-
collects no telemetry, and transmits no data of any kind. All processing of disk
5-
images and filesystem artifacts happens entirely on your own machine, under your
6-
control.
3+
*Last updated: 2026-06-15*
74

8-
Because the software gathers nothing, there is nothing for us to store, share, or
9-
sell. Any data you process with it remains yours alone.
5+
## Summary
106

11-
For questions, contact [albert@securityronin.com](mailto:albert@securityronin.com).
7+
ntfs-forensic is a local Rust library. It does not collect, transmit, or store any personal data on remote servers.
128

13-
© 2026 Security Ronin Ltd
9+
## Data Access
10+
11+
ntfs-forensic reads only the file bytes you pass to it. All processing happens in memory on your local machine. Nothing is uploaded anywhere.
12+
13+
## Telemetry
14+
15+
ntfs-forensic has **no telemetry**. It makes no network requests of any kind.
16+
17+
## Open Source
18+
19+
ntfs-forensic is open source (Apache-2.0). You can audit every line of code at [github.com/SecurityRonin/ntfs-forensic](https://github.com/SecurityRonin/ntfs-forensic).
20+
21+
## Contact
22+
23+
Privacy questions: [security@securityronin.com](mailto:security@securityronin.com)
24+
25+
---
26+
27+
[Terms of Service](terms.md) · [Home](index.md) · © 2026 Security Ronin Ltd.

docs/terms.md

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
11
# Terms of Service
22

3-
`ntfs-forensic` is provided under the [MIT License](https://github.com/SecurityRonin/ntfs-forensic/blob/main/LICENSE).
4-
The software is provided "as is", without warranty of any kind, express or
5-
implied. In no event shall the authors or copyright holders be liable for any
6-
claim, damages, or other liability arising from its use.
3+
*Last updated: 2026-06-15*
74

8-
You are responsible for ensuring that your use of this software — including the
9-
acquisition and analysis of any disk image or filesystem data — complies with all
10-
applicable laws and that you have authorisation to examine the material in
11-
question.
5+
## Licence
126

13-
© 2026 Security Ronin Ltd
7+
ntfs-forensic is released under the [Apache License 2.0](https://github.com/SecurityRonin/ntfs-forensic/blob/main/LICENSE). You may use, modify, and distribute it subject to the terms of that licence.
8+
9+
## Acceptable Use
10+
11+
You agree to use ntfs-forensic only for lawful purposes, including but not limited to:
12+
13+
- Digital forensics investigations on systems you own or are authorised to examine
14+
- Incident response activities within your organisation
15+
- Security research and CTF competitions
16+
- Academic or educational use
17+
18+
## No Warranty
19+
20+
ntfs-forensic is provided "as is" without warranty of any kind. Security Ronin Ltd is not liable for any damages arising from use of this software.
21+
22+
## Contact
23+
24+
[security@securityronin.com](mailto:security@securityronin.com)
25+
26+
---
27+
28+
[Privacy Policy](privacy.md) · [Home](index.md) · © 2026 Security Ronin Ltd.

mkdocs.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
site_name: ntfs-forensic
2+
site_url: https://securityronin.github.io/ntfs-forensic/
3+
repo_url: https://github.com/SecurityRonin/ntfs-forensic
4+
repo_name: SecurityRonin/ntfs-forensic
5+
edit_uri: edit/main/docs/
6+
7+
theme:
8+
name: material
9+
palette:
10+
- scheme: default
11+
primary: indigo
12+
accent: blue
13+
toggle:
14+
icon: material/brightness-7
15+
name: Switch to dark mode
16+
- scheme: slate
17+
primary: indigo
18+
accent: blue
19+
toggle:
20+
icon: material/brightness-4
21+
name: Switch to light mode
22+
features:
23+
- content.code.copy
24+
- navigation.tabs
25+
- navigation.sections
26+
- navigation.top
27+
- search.highlight
28+
29+
markdown_extensions:
30+
- admonition
31+
- attr_list
32+
- md_in_html
33+
- pymdownx.superfences
34+
- pymdownx.tabbed:
35+
alternate_style: true
36+
- pymdownx.highlight:
37+
anchor_linenums: true
38+
- tables
39+
- toc:
40+
permalink: true
41+
42+
nav:
43+
- Home: index.md
44+
- Privacy Policy: privacy.md
45+
- Terms of Service: terms.md
46+
47+
plugins:
48+
- search

0 commit comments

Comments
 (0)