Skip to main content

file-forensics

Examine what a file claims about itself and what it actually contains — powered by Apache Tika. True content-based type detection (extensions lie), provenance claims (authors, dates, creating application), revision and tamper signals (PDF incremental updates, tracked changes, hidden slides, zip integrity), hidden and embedded content (attachments, macros), risk indicators (PDF JavaScript actions, encryption), and content digests. Evidence gathering, not verdicts: Tika reports what the file asserts and what parsing observed; it does not attribute authorship or validate signatures. Use for triaging suspicious files, provenance questions, e-discovery-style review, or "is this file what it claims to be."

Informations de source

Dépôt
apache/tika
Dernière activité de la source
7 octobre 2026 à 18:31
Langue détectée de SKILL.md
anglais
Étoiles
4 093
Forks
1 017

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Explorateur de fichiers
9 fichiers

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
file-forensics
description
Examine what a file claims about itself and what it actually contains — powered by Apache Tika. True content-based type detection (extensions lie), provenance claims (authors, dates, creating application), revision and tamper signals (PDF incremental updates, tracked changes, hidden slides, zip integrity), hidden and embedded content (attachments, macros), risk indicators (PDF JavaScript actions, encryption), and content digests. Evidence gathering, not verdicts: Tika reports what the file asserts and what parsing observed; it does not attribute authorship or validate signatures. Use for triaging suspicious files, provenance questions, e-discovery-style review, or "is this file what it claims to be."
<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> Local override: `$TIKA_SKILLS_LOCAL/file-forensics/LOCAL.md` (default `~/.tika-skills`), read after this file, wins on conflict. # File forensics with Apache Tika **What this can and cannot tell you.** Tika cannot tell you who wrote a file. It can tell you — exhaustively — what the file *claims* about itself and what it actually *contains*, and those claims are the evidence: a `dc:creator` value is an assertion some software recorded, not an identity; a template-default author, an inconsistency between claimed dates, or a conspicuously *missing* field is as much a finding as a present one. Report what the file says; let the human draw conclusions. **The one rule for reading Tika's output:** the key prefix tells you *who is asserting each fact*. - **`tk:*`** — Tika's own parse-time observations, which the file cannot forge into place: magic-byte-detected type, embedded-item structure, encryption status, digests of the actual bytes. - **Everything else** (`dc:`, `pdf:docinfo:`, `extended-properties:`, `xmp:`, ...) — the file's claims about itself, recorded by whatever software touched it, editable by anyone with a hex editor. Disagreement between the two classes is where findings live. **And one rule with no exceptions: no date in a file is an observation.** Creation, modification, and print timestamps — including the per-revision timestamps inside PDF incremental updates — are written by software under the control of whoever produced the file, and can be set to anything. Tika can observe that a revision layer *exists*, how large it is, and what it *contains*; when it was written is only ever claimed. Report timestamps as "the file records...", never "this happened at...". Setup (getting tika-app or a tika-server) is covered by the `file-to-markdown` companion skill; this skill assumes one is available and uses the same invocation shapes. **Parse suspect files in isolation — this is not optional for forensics work.** tika-server and `tika-app -f` both parse in crash-isolated forked processes; for hostile-file triage prefer the Docker route with the input mounted read-only (see `file-to-markdown-docker`) so the parse can neither modify the evidence nor touch anything else. The one-command forensics rig — the stock Tika image started with this skill's config (which IS the unlock on the server surface: none of these switches are on by default there): ```bash docker run -d --rm --name tika-forensics -p 127.0.0.1:9998:9998 \ -v "$(pwd)/file-forensics-config.json:/file-forensics-config.json:ro" \ apache/tika:latest-full -c /file-forensics-config.json curl -T suspect.file http://localhost:9998/rmeta > suspect.rmeta.json docker stop tika-forensics # when done ``` Guaranteed OCR, read-only config, process isolation, explicit named configuration — in one command. ## Workflow: capture once, then converse over the JSON This skill is **not** "run Tika and read the output into context." It's two phases: **Phase 1 — capture** (once per file): parse to disk with a digest, **extract every embedded file to disk**, and make a compact metadata-only view for the conversation: ```bash java -jar tika-app.jar --config=file-forensics-config.json -J suspect.file > suspect.rmeta.json java -jar tika-app.jar --config=file-forensics-config.json -Z --extract-dir=evidence/suspect-embedded suspect.file # (--digest=sha256 is only needed if you are NOT using the config; # the config's built-in digester already covers it) # or, against the docker rig from the isolation section above: # curl -T suspect.file http://localhost:9998/rmeta > suspect.rmeta.json # curl -T suspect.file http://localhost:9998/unpack > suspect-embedded.zip # mkdir -p evidence/suspect-embedded && unzip -q suspect-embedded.zip -d evidence/suspect-embedded jq 'map(del(."tk:content"))' suspect.rmeta.json > suspect.meta.json ls -lR evidence/suspect-embedded && sha256sum evidence/suspect-embedded/*/unpacked/* 2>/dev/null || sha256sum evidence/suspect-embedded/unpacked/* ``` `suspect.rmeta.json` is the full evidence record — a JSON array where entry 0 is the file and entries 1+ are everything embedded in it, content included. `suspect.meta.json` is the same array with the (possibly huge) extracted text stripped out: small enough to inspect freely. `evidence/suspect-embedded/` holds **the literal embedded files** — attachments, images, macro source, each prior PDF revision as an openable PDF — and the examiner needs those, not descriptions of them. Extraction is part of capture, not a follow-up; tell the human where the directory is and what's in it as soon as it exists. **Phase 2 — investigate conversationally.** Each question the user asks becomes a targeted query against the saved files, and only the answering values enter the conversation: ```bash jq 'length' suspect.meta.json # how many embedded items? jq '.[0] | keys' suspect.meta.json # what fields exist at all? jq '.[].["tk:embedded-resource-type"]' suspect.meta.json jq '.[0]."pdf:incremental-update-count"' suspect.meta.json grep -o 'Normal.dotm' suspect.meta.json # quick claim checks jq '.[2]."tk:content"' suspect.rmeta.json # content of ONE entry, # only when asked ``` Two queries worth running early on any file: ```bash # inventory: one line per entry -- index, depth, type, name, content-type jq -r 'to_entries[] | [.key, .value."tk:embedded-depth", .value."tk:embedded-resource-type", .value."tk:resource-name", .value."Content-Type"] | @tsv' suspect.meta.json # every key that appears anywhere in the array -- what there is to ask about jq '[.[] | keys[]] | unique' suspect.meta.json ``` **When the human wants to see everything**, they get three things: the extracted-files directory (open the images, the attachments, the prior revisions — with whatever tools they normally use), and both JSON files: `suspect.rmeta.json` is the complete record, content included; `suspect.meta.json` is the same thing without the text, which is what you want when the text runs to megabytes and drowns the metadata in a viewer. Point them at whatever is already on the machine: - `jq . suspect.rmeta.json | less` — pretty-printed, pageable, no install (`python3 -m json.tool` if `jq` is missing) - `jq '.[0]' suspect.meta.json` — the container entry's metadata alone, the usual first look - Open it in a browser: Firefox and Chrome render `.json` files as a collapsible tree with search - `jless`, `fx`, or `visidata` if installed (see "Showing the human the evidence" below) The size caution applies to the *agent's* context, not the human's screen. Never load the full rmeta JSON into context — a document with a large text body or many attachments makes it enormous, and the conversation only ever needs slices. The saved files also make the session auditable: the evidence the answers came from is on disk, unchanged, re-queryable. ## The forensics config: turn on what default parsing leaves off This skill ships `file-forensics-config.json` (in this skill's directory): one config for every surface, and the named, explicit parse configuration for investigation work. (Its `"server": {}` element is required for tika-server's `-c` and harmlessly ignored by tika-app; don't remove it.) Use it on every surface, every time — an examination should be able to state exactly what configuration produced its output, and "whatever that tool defaults to" is not that. It also makes results identical across surfaces, because the defaults are NOT the same everywhere: **What it changes per surface:** - **tika-server, pipes mode, or the library:** the config is the difference between seeing revision history/macros and not — none of these switches are on there. - **tika-app single-file mode:** the CLI quietly applies its own convenience config (it prints a banner saying so) that already enables the marquee items — `parseIncrementalUpdates`, `extractMacros`, `extractActions`, `extractInlineImages`. On tika-app, `file-forensics-config.json` still adds `extractFontNames`, `extractScripts`, `includeMissingRows`, deleted/moved-content for legacy `.doc` (on `.docx` the convenience config already includes it, so testing this switch on a docx shows no diff — that's expected), and pins `accessCheckMode` to `DONT_CHECK` (tika-app's convenience config sets a more restrictive mode). A bare `tika-app -J` will already show PDF revisions and macros — the config's job there is making that explicit and reproducible rather than unlocking it. The full switch list: - `pdf-parser.parseIncrementalUpdates` — each prior revision of an incrementally-updated PDF is parsed as its own embedded document, so you can read what the file said *before* the last save(s) - `pdf-parser.extractActions` — full detail of automatic actions/JavaScript - `pdf-parser.extractFontNames` — font inventory (a provenance fingerprint: fonts betray the producing toolchain) - `pdf-parser.extractInlineImages` — every image drawn in the PDF becomes an embedded entry: extractable to disk with `-Z`, OCR-able, and part of the inventory. (Repeated images are deduplicated by default — `extractUniqueInlineImagesOnly` — which is usually what you want.) - Office (`office-parser` + `ooxml-parser` — set on both; 4.x config is per-component): `extractMacros` (macro code as MACRO-typed embedded entries), `includeDeletedContent` and `includeMoveFromContent` (tracked-change deletions and moved-away text in the output), `includeMissingRows` (spreadsheet row gaps) - `jsoup-parser.extractScripts` — script bodies in HTML (including HTML email bodies and HTML attachments) become their own `MACRO`-typed embedded entries instead of being silently dropped: JavaScript in a document is something a reviewer should see. Note they do NOT appear in the page's own `tk:content`; look for the extra entries (and the extracted files) Some defaults are already forensics-friendly and are deliberately NOT changed: overlapping/duplicate text is kept (`suppressDuplicateOverlappingText: false` — text hidden under other text stays visible), annotation/AcroForm/bookmark text is extracted, and `accessCheckMode: DONT_CHECK` means Tika extracts regardless of the file's claimed copy/extract restrictions while still *recording* those claims under `access-permission:*` — the restriction flags are themselves evidence. ```bash java -jar tika-app.jar --config=file-forensics-config.json -J suspect.file > suspect.rmeta.json ``` The config also configures a SHA-256 digester, so `tk:digest:SHA-256` appears on every entry on **every** surface — no per-command flag to forget. If a parser name doesn't bind on your build, `--list-parser-names` prints the registered names. Expect bigger output and slower parses than default config — that's the point. ## Identity: is it what it claims to be? ```bash java -jar tika-app.jar -d suspect.file # content-based media type, extension ignored ``` **Anchor on `tk:content-type-magic-detected`** — what the leading bytes said, computed by Tika from content alone. The top-level `Content-Type` is the *routing* type, and on tika-server a caller-supplied `Content-Type` header influences it by design — so on the HTTP surface that field can reflect upstream input, accidental or adversarial, not just Tika's own conclusion. An extension or routed type that disagrees with `tk:content-type-magic-detected` is a classic finding (a "pdf" that is a zip; an "xlsx" that is plain-text CSV). Caller-supplied types are tracked under `tk:content-type-hint`, `tk:content-type-override`, and `tk:content-type-parser-override` (which of these appears depends on how the type was supplied) — all input, none evidence. ## Provenance claims The file's own story about its origin — read it as testimony, not fact: - `dc:creator` and related Dublin Core fields — claimed author(s) - `pdf:docinfo:creator-tool`, `pdf:docinfo:producer` — what claims to have made the PDF (e.g. a phishing PDF "authored" by a word processor that doesn't match its claimed corporate origin) - `extended-properties:*` (Office) — creating application/version, template (`Normal.dotm` = stock Word), total edit time, claimed created/modified timestamps - **`tk:orig-resource-name`** — for xlsx, the absolute path where the file was last saved (Excel records it in the workbook): drive letters, network shares, and `C:\Users\<name>\...` usernames leak here. (The `tk:` prefix here means Tika surfaced it; the *value* is still the file's own record — treat it as a claim like the rest of this section.) Findings look like: dates that precede the claimed creating tool's release; edit time of 0 on a "carefully drafted" document; a template name from an organization other than the claimed author's; identical creator strings across supposedly independent documents. ## Revision and tamper signals - **`pdf:incremental-update-count`** (emitted by default in 4.x) — a PDF above 0 has been modified after its initial write; each increment is a save layered on top of the previous file. Signed-then-modified PDFs show here. `pdf:eof-offsets` lists the byte offset of each revision's end. - **Reading the prior versions themselves:** with the forensics config's `parseIncrementalUpdates` on, each earlier revision appears as its own entry in the rmeta array, typed `tk:embedded-resource-type: VERSION`, with `pdf:incremental-update-number` giving its place in history — **0-indexed from the earliest**; the final state is entry 0 of the array and carries no update number. Each VERSION entry is the complete file as it existed at that save (the bytes up to that revision's EOF offset), so its `tk:content` is what the document said *then* — diff a version's content against entry 0's to see what changed between saves. The layer structure and content differences are observations; any timestamps inside each revision are that revision's own claims — order is established by the byte layering, timing is not. (Note: the final entry has no `pdf:incremental-update-number` key *at all* — `jq` prints `null` for a missing key and a null value alike, so "null on entry 0" is expected, not an error.) - `tk:version-count` / `tk:version-number` — the format-general spelling of the same idea (also 0-indexed earliest-first; the latest version carries none). PDFs emit `tk:version-count` alongside the pdf-specific key; other formats that retain prior versions may adopt it, but not all parsers emit these yet. - `msoffice:has-track-changes`, `msoffice:has-comments` — content the author may not have meant to ship. Parse output includes tracked-change and comment text; a "deleted" tracked change is still in the file. - `extended-properties:HiddenSlides` — slides present but not shown. - `zip:*` integrity family (zip-based formats): `zip:integrity-check-result`, `zip:duplicate-entry-names`, `zip:local-header-only-entries`, `zip:central-directory-only-entries` — structural anomalies associated with crafted or tampered archives (a duplicate entry name can make two tools see two different "same" files). ## Hidden and embedded content The rmeta array *is* the embedded-content inventory: every entry past index 0 is something inside the file — attachments, embedded objects, images, archive members, prior VERSIONs — each with its own metadata, `tk:embedded-resource-type` (`INLINE`, `ATTACHMENT`, `MACRO`, `METADATA`, `THUMBNAIL`, `VERSION`, ...), `tk:embedded-depth`, and path. **Show the human the literal files.** The metadata inventory is for the agent; the extracted bytes are for the person. Phase 1 already extracted everything to `evidence/suspect-embedded/` (`-Z --extract-dir=...`, or `/unpack` on the server) so they can open the images, hand an attachment to another tool, or open a prior PDF revision side-by-side with the final. If that step was skipped, do it now — an examination without the extracted files is incomplete. - With the forensics config, this includes **macro source** (MACRO entries) and **each prior PDF revision as a standalone, openable PDF** (VERSION entries). - **Read code from the extracted files, not from `tk:content`.** The default content handler is Markdown, which escapes underscores and other characters — VBA/script source read via `jq '.[N]."tk:content"'` comes back visually corrupted (`VB\_Name`). The `-z`/`-Z` extracted files are pristine bytes. (Alternative: capture with `-J -t` for unescaped plain text per entry.) - Extracted files are **renumbered** (`00000001.jpg`, ...); original names live in each rmeta entry's `tk:resource-name`, and `-z`/`-Z` writes a sidecar `<name>.json` metadata dump for mapping numbers back to names. - The output is a Frictionless data package (Tika 4.2+): the files sit under `<container>/unpacked/`, and `<container>/datapackage.json` already carries a SHA-256 per file. Digest what you extracted (`sha256sum evidence/suspect-embedded/*/unpacked/*`) and check it against the manifest, so each artifact is pinned the same way the container file is. - Mechanics and gotchas (Pipes-mode delay, chatty stderr) are in `file-to-markdown`; the server-side equivalent is `/unpack`, which returns the same package as one zip over HTTP. **`/unpack` names differ from `-z`/`-Z`:** plain sequential names (`unpacked/1.jpg`, `unpacked/2.pdf`, ...) and the rmeta rows live in the package's `metadata.json` rather than a sidecar — map names back via each row's `tk:resource-name`. Against the server started above (forensics config loaded): ```bash curl -T suspect.file http://localhost:9998/unpack > suspect-embedded.zip mkdir -p evidence/suspect-embedded && unzip -q suspect-embedded.zip -d evidence/suspect-embedded sha256sum evidence/suspect-embedded/unpacked/* ``` `/unpack/all` also includes the container's own text and metadata. Both
Voir sur GitHub
Ce SKILL.md est tres volumineux, SkillsMP affiche donc ici seulement la premiere section. Voir sur GitHub