本文へ移動
cccskills
無料GitHub で公開

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."

インストール方法を見る

含まれるファイル(9)

  • SKILL.md24.3 KB
  • demo/budget.xlsx8.2 KB
  • demo/contract.pdf63.4 KB
  • demo/invoice.pdf456 B
  • demo/memo.docx51.2 KB
  • demo/newsletter.html202 B
  • demo/quarterly-report.docm16.9 KB
  • demo/README.md1.6 KB
  • file-forensics-config.json866 B

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

<!-- 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):

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:

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:

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:

# 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.

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?

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):

    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 are PUT; POST multipart/form-data takes a per-request config part.

Macros: Office macro code is surfaced as embedded entries typed MACRO, but only when macro extraction is enabled — it is off by default; file-forensics-config.json (above) turns it on. Read the extracted VBA as text. Presence of macros is a data point, not a verdict — plenty of legitimate spreadsheets have them.

Risk indicators

  • pdf:action-triggers, pdf:action-types, pdf:js-name — automatic actions and JavaScript wired into a PDF (open actions are a common malicious-document mechanism, and also used legitimately by forms). With the forensics config's extractActions, each JavaScript body is its own embedded entry: tk:embedded-resource-type: MACRO, Content-Type: text/javascript, pdf:action-trigger naming what fires it (PAGE_OPEN, ...), and the code in tk:content — and -Z writes it to disk as a file. A one-page PDF with no text and a PAGE_OPEN script is the classic shape; demo/invoice.pdf is one.
  • pdf:has-xfa, pdf:has-acro-form-fields — active form machinery
  • tk:encrypted — the file (or an embedded item) is encrypted; content Tika couldn't read is content nobody scanned
  • tk:exception:* — parse failures, per embedded item; a file that crashes parsers is itself a signal worth recording

Signatures — presence, not validity

pdf:has-signature-fields and the tk:signature:* family (name, date, reason, location, contact-info, filter) report that signature structures exist and what they claim. Tika does not cryptographically validate signatures — a well-formed tk:signature:name proves only that signature metadata is present, not that it verifies, and pdf:incremental-update-count > 0 alongside a signature means the file changed after some revision was signed. Use a signature-validation tool for validity; use Tika to know there's something to validate.

Digests: pin down what you examined

java -jar tika-app.jar --digest=sha256 -j suspect.file
# adds tk:digest:SHA-256 to the metadata

Record the digest alongside findings so they're tied to exact bytes — malware family lookups, dedup across an evidence set, and "is this the same file I looked at yesterday" all start here. (md5, sha1, sha384, sha512 also available; SHA3 requires configuring the BouncyCastle digester in a JSON config.)

Demo files

The demo/ directory in this skill ships six small real files with real findings — a PDF with revision history, a macro-bearing Word document, a docx with embedded content, an xlsx whose metadata records the absolute path where it was last saved, a text-free PDF with a page-open JavaScript action, and an HTML page with an embedded script — plus a README of suggested questions. They're for showing a human what this skill does on files where the default parse looks unremarkable, and for smoke-testing your setup end-to-end.

Showing the human the evidence

Terminal jq quotes are fine for single answers; for "walk me through what you found," give the human something browsable:

  • If an interactive JSON viewer is installed, use it: jless suspect.meta.json or fx, or visidata (the rmeta array is naturally tabular — rows are entries, columns are keys).
  • Better: generate a report. (Run the embedded-file extraction first if the report will link to extracted items.) Write a self-contained static HTML file next to the evidence — summary and digests up top, a claims-vs-observations table per entry, a collapsible <details> section per embedded item, relative links to the extracted files — and open it in the browser. No server, no dependencies, everything embedded (a page that phones out is not an evidence artifact). Regenerate it as the investigation deepens; it doubles as the deliverable the human keeps.
  • If your agent host supports publishing pages/artifacts, that can make the report nicely viewable — but read this first: publishing uploads the evidence to an external service. Document content, metadata, extracted file names and paths leave the machine; anything shared can be forwarded, cached, or indexed beyond your control, and "deleted" rarely means gone. For anything sensitive — client documents, case material, unreleased content, anything under privilege or NDA — keep the report as a local HTML file and do not publish it. Publish only when the human has explicitly confirmed the content is safe to leave the machine.

Evidence discipline

  • Work on a copy; mount read-only in Docker. Tika doesn't modify inputs, but the discipline should not depend on that.
  • Save the full rmeta JSON per file and record the exact command and Tika version (--version) with it — findings should be reproducible.
  • Report inconsistencies as inconsistencies ("claimed creation 2019, claimed creator tool released 2021"), not conclusions ("forged").
  • This is content analysis, not chain-of-custody forensics: no write blocking, no acquisition hashing, no court-grade custody trail. For matters likely to face legal scrutiny, treat Tika as the analysis layer inside a proper forensic process, not the process.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Ground rules for working in the Tika codebase — git policy, Maven wrapper/repo conventions, building and testing specific modules, code and test conventions, pre-commit checks. Load at session start for any Tika development task.

日本語の概要は準備中です。原文の説明を表示しています。

apache/tika4,0932026年10月10日 更新

Taking a multi-PR feature from "shape unknown" to merged without five review rounds per PR: spike until interfaces stop moving, write the contract, cut PRs along contract seams, one review per PR. Use when starting a feature that touches more than one lifecycle object or public interface, when a PR review keeps changing interfaces, or when splitting a large branch.

日本語の概要は準備中です。原文の説明を表示しています。

apache/tika4,0932026年10月10日 更新

Turn almost any file into Markdown plus metadata — PDF, Office, HTML, email, archives, images, audio/video, 1000+ formats — powered by Apache Tika, either via the tika-app CLI (zero setup, one file) or a running tika-server (curl, warm process, many calls). Leads with rmeta (structured, embedded-item-aware output) as the default operation rather than flat concatenated text, since you can't tell whether a file has embedded content from its extension. Covers metadata-only triage, type and language detection, OCR, and output-size discipline for agent context. Use whenever a task involves reading the content of a file whose format you don't want to hand-parse.

日本語の概要は準備中です。原文の説明を表示しています。

apache/tika4,0932026年10月10日 更新

Run Apache Tika as a Docker container when you need guaranteed OCR (scanned PDFs, images) or geospatial raster support with zero local install — `apache/tika:<version>-full` bundles Tesseract, GDAL, ImageMagick, and fonts. Also covers the minimal image, port/volume/memory conventions, the path-identity mount gotcha, and how to confirm OCR actually ran rather than silently returning no text. Powered by Apache Tika. Use when a local `tika-app`/`tika-server` doesn't have Tesseract installed, or you want a disposable, self-contained parsing environment. Companion to the `file-to-markdown` skill, which covers the parsing calls themselves once a server is up.

日本語の概要は準備中です。原文の説明を表示しています。

apache/tika4,0932026年10月10日 更新

Working with tika-metadata-schema, the build-gated registry of Tika metadata keys — regeneration after Property changes, gate tests, naming conventions, post-rename sweeps. Use when adding or renaming metadata keys or when the schema gate fails.

日本語の概要は準備中です。原文の説明を表示しています。

apache/tika4,0932026年10月10日 更新

oss-fuzz

無料

Run Tika's OSS-Fuzz Jazzer targets locally against a working-tree checkout — build the image, build fuzzers from local source, fuzz a target, run a corpus as a regression pass, reproduce a crash, and add seeds. Use for "fuzz the OneNote parser", "run OneNoteParserFuzzer against these files", "reproduce an OSS-Fuzz crash", "fuzz my branch before merge".

日本語の概要は準備中です。原文の説明を表示しています。

apache/tika4,0932026年10月10日 更新

apache のスキルをすべて見る

このスキルの問題を報告する