Skip to content

Commit 2fd70ab

Browse files
authored
Merge pull request #4 from SecureToolsProject/feat/sprint-3-tiff-exif-core
✨[Feat] Sprint 3 TIFF and EXIF Core
2 parents b789d06 + c7ec004 commit 2fd70ab

26 files changed

Lines changed: 2206 additions & 128 deletions

‎CHANGELOG.md‎

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,16 @@ All notable changes will be documented here. The project intends to follow seman
66

77
### Added
88

9-
- Bounded JPEG marker and length-prefixed segment traversal.
10-
- Standalone, fill-byte, restart-marker, EOI, and multi-scan handling.
11-
- JPEG APP and COM container classification.
12-
- EXIF, standard/extended XMP, ICC, Photoshop/IPTC, JFIF/JFXX, and Adobe presence detection.
13-
- Structured malformed, truncation, trailing-data, and segment-limit diagnostics.
14-
- Normalized JPEG metadata-container entries and complete/partial container status.
15-
- Bounded binary reader, no-copy input normalization, and JPEG/PNG/WebP format detection.
16-
- Binary boundary, sliced-view, format, malformed-input, and JPEG container tests.
9+
- Shared bounded little- and big-endian TIFF/EXIF decoder.
10+
- Iterative IFD0, ExifIFD, GPSIFD, and next-IFD traversal.
11+
- IFD entry/depth limits and repeated-offset cycle protection.
12+
- Inline and TIFF-relative offset value handling for common field types.
13+
- Exact RATIONAL/SRATIONAL, conservative ASCII, and full-range LONG/SLONG decoding.
14+
- Common IFD0, ExifIFD, and GPS tag normalization with deterministic source paths.
15+
- JPEG EXIF child-field inspection through bounded TIFF-only subviews.
16+
- Structured malformed header, table, pointer, value, type, rational, limit, and cycle diagnostics.
17+
- Bounded JPEG marker traversal and EXIF/XMP/ICC/IPTC container detection.
18+
- Binary boundary, JPEG container, TIFF endian, malformed, cycle, and integration tests.
1719

1820
### Foundation
1921

‎README.md‎

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ Current implementation:
88

99
- bounded binary input and endian-aware read core;
1010
- JPEG, PNG, and WebP signature detection;
11-
- bounded JPEG marker and segment traversal;
12-
- JPEG entropy-scan skipping without image decoding;
13-
- JPEG EXIF, XMP, extended XMP, ICC, Photoshop/IPTC, and comment container-presence detection.
11+
- bounded JPEG marker, segment, and entropy-scan traversal;
12+
- JPEG EXIF, XMP, ICC, Photoshop/IPTC, and comment container detection;
13+
- shared little- and big-endian TIFF/EXIF decoder;
14+
- iterative IFD0, ExifIFD, GPSIFD, and next-IFD traversal with cycle and depth protection;
15+
- common TIFF, EXIF, and GPS field decoding with exact rational values.
1416

15-
Not implemented: TIFF/EXIF or GPS field decoding, XML/IPTC/ICC payload decoding, PNG/WebP container parsing, metadata cleaning, and verification.
17+
Not implemented: MakerNote or thumbnail decoding, XMP/IPTC/ICC payload parsing, PNG/WebP container parsing, metadata cleaning, and verification.
1618

1719
## Format status
1820

19-
JPEG reports can be `container-inspected` or `container-partial`. PNG and WebP remain `format-only`. See [format support](docs/format-support.md) for the precise matrix.
20-
21-
A detected or traversed container is not necessarily a decodable image. The JPEG parser validates marker and segment boundaries, not quantization, Huffman, frame, scan-header, or entropy semantics.
21+
JPEG reports can be `container-inspected`, `container-partial`, or `metadata-partial`. `metadata-partial` means supported TIFF/EXIF fields were attempted while the wider metadata space remains intentionally incomplete. PNG and WebP remain `format-only`. See [format support](docs/format-support.md).
2222

2323
## Installation
2424

@@ -34,21 +34,23 @@ import {
3434
} from "secure-metadata";
3535
```
3636

37-
`inspectMetadata` accepts `Uint8Array | ArrayBuffer`, enforces relevant parse limits, and returns a deterministic report. For JPEG it inventories the container and emits one normalized entry per recognized privacy/color metadata container. Entries identify container presence only; payload values are not decoded.
37+
`inspectMetadata` accepts `Uint8Array | ArrayBuffer`, enforces relevant parser limits, and returns deterministic normalized entries. JPEG EXIF reports retain the EXIF container entry and add decoded child entries with exact TIFF tag, type, count, source offset, and path information.
38+
39+
GPS rational components remain exact numerator/denominator pairs; decimal coordinates are not derived. Unknown TIFF tags and MakerNote are represented structurally without dumping or recursively parsing their payloads.
3840

39-
`cleanMetadata` and `verifyMetadata` still throw a typed `NotImplementedError`. Node.js `Buffer` values work structurally as `Uint8Array` but are not part of the public contract.
41+
`cleanMetadata` and `verifyMetadata` still throw a typed `NotImplementedError`.
4042

4143
## Security philosophy
4244

43-
Every byte is untrusted. Binary reads use centrally checked ranges, parser input views retain their original boundaries, traversal is hard bounded, and malformed or tiny inputs are ordinary data. Unknown APP segments remain unknown and should be preserved by future cleaning. See the [security model](docs/security-model.md), [architecture](docs/architecture.md), and [cleaning policy](docs/cleaning-policy.md).
45+
Every byte is untrusted. All offsets are interpreted within bounded views, traversal is iterative and limited, repeated IFD offsets are rejected, and malformed entries recover without unchecked access. Unknown structures remain unknown and should be preserved by future cleaning. See the [security model](docs/security-model.md), [architecture](docs/architecture.md), and [cleaning policy](docs/cleaning-policy.md).
4446

4547
## Non-goals
4648

47-
The library does not perform image decoding or encoding, visual redaction, pixel-content privacy analysis, steganography detection, or malware scanning. Absence of recognized metadata containers is never proof that an image contains no private information.
49+
The library does not perform image decoding or encoding, visual redaction, pixel-content privacy analysis, steganography detection, or malware scanning. Absence of recognized or decoded metadata is never proof that an image contains no private information.
4850

4951
## Secure Tools ecosystem
5052

51-
This is an independent open-source library in the broader Secure Tools ecosystem. It has its own package, lifecycle, and repository; future Secure Tools integration will use a pinned browser artifact rather than coupling application code to this repository.
53+
This is an independent open-source library in the broader Secure Tools ecosystem. Future integration will use a pinned browser artifact rather than coupling application code to this repository.
5254

5355
## License
5456

‎docs/architecture.md‎

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,34 @@
11
# Architecture
22

3-
`secure-metadata` is organized as a side-effect-free binary library. Its current and planned flow is:
3+
`secure-metadata` is a side-effect-free binary library with format-specific containers and shared metadata decoders.
44

55
```text
6-
Input bytes implemented
7-
↓
8-
Safe binary view / bounded reads implemented
9-
↓
10-
Format detection implemented
11-
↓
12-
JPEG container parser implemented for JPEG
13-
↓
14-
Metadata container classification implemented for JPEG
15-
↓
16-
Metadata payload decoder planned
17-
↓
18-
Normalization / field classification planned
19-
↓
20-
Policy engine and cleaner planned
21-
↓
22-
Output re-inspection / verification planned
6+
JPEG APP1 Exif\0\0 ─┐
7+
PNG eXIf (future) ├──→ bounded TIFF/EXIF core
8+
WebP EXIF (future) ─┘ ↓
9+
normalized entries
2310
```
2411

25-
## Binary core
12+
The TIFF decoder receives only the TIFF byte view after the six-byte EXIF identifier. It has no JPEG marker or absolute file-offset knowledge. Every TIFF offset is relative to byte zero of that view. Integration relocates decoded source offsets and diagnostics only after parsing.
2613

27-
Input normalization returns the caller's exact `Uint8Array` view or creates a no-copy view over an `ArrayBuffer`. `ByteReader` validates offsets and lengths as non-negative safe integers and checks remaining capacity with subtraction before every read. It provides bounded unsigned 8-, 16-, and 32-bit reads, subarray views, and allocation-free signature matching.
14+
## TIFF core
2815

29-
## JPEG container layer
16+
The decoder explicitly validates `II` or `MM`, magic value 42, and the first IFD offset. `TiffReader` centralizes endian-aware unsigned 16-/32-bit and signed 32-bit access over the bounded binary core.
3017

31-
The iterative JPEG parser validates SOI and walks markers using the binary core. A central marker model distinguishes SOI, EOI, TEM, RST0–RST7, APP0–APP15, COM, SOS, common image-structure markers, and length-prefixed unknown markers. Repeated `FF` fill bytes are collapsed to one marker; declared lengths include their two-byte length field and must fit fully before offsets advance.
18+
Each IFD table is validated as a complete `2 + count × 12 + 4` byte range before entries are visited. Field sizes support BYTE, ASCII, SHORT, LONG, RATIONAL, UNDEFINED, SLONG, and SRATIONAL. Values of four bytes or fewer use the entry's inline bytes in TIFF byte order; larger values use a bounded TIFF-relative offset.
3219

33-
After SOS, the parser scans rather than decodes entropy data. `FF 00` remains stuffed data, restart markers are recorded without terminating the scan, and the next real marker resumes normal traversal. This supports multiple scans. Every marker, including SOI, EOI, SOS, and restarts, counts toward `maxSegments`.
20+
Traversal uses a FIFO work queue. Root IFD0 has depth 1; ExifIFD, GPSIFD, and next-IFD work is queued deterministically in that order. A visited-offset set rejects cycles and repeated references. `maxIfdEntries` bounds each table, `maxIfdDepth` bounds linked depth, and `maxMetadataEntries` caps total processed entries and queued IFD work.
3421

35-
APP signatures are checked within segment payload boundaries without retaining payload copies. EXIF, standard/extended XMP, ICC, Photoshop/IPTC, JFIF/JFXX, Adobe, and unknown classifications remain container-level observations.
22+
## Value and entry behavior
3623

37-
## Inspection status
24+
Supported known values are decoded without converting exact rational pairs to floating point. ASCII stops at the first NUL within its declared count and maps non-ASCII bytes conservatively. Zero rational denominators remain represented and produce diagnostics.
25+
26+
Unknown tags retain namespace, tag number, TIFF type, count, entry offset, and source path without exposing arbitrary payload bytes. Duplicate tags remain separate ordered entries. MakerNote is recognized but opaque and is never interpreted as nested standard TIFF.
3827

39-
- `format-only`: a signature was detected; no container parser ran. Currently PNG, WebP, unknown, and short arbitrary inputs.
40-
- `container-inspected`: JPEG traversal reached EOI safely.
41-
- `container-partial`: JPEG identity is known, but traversal stopped on a structural error, truncation, or limit.
42-
- `metadata-inspected`: reserved for future payload decoders.
28+
## Inspection status
4329

44-
An empty entry list means no supported metadata container was recognized during the completed portion of traversal. It does not prove metadata or private information is absent.
30+
- `format-only`: signature detection only; currently PNG, WebP, and unknown input.
31+
- `container-inspected`: JPEG reached EOI and no EXIF decode was attempted.
32+
- `container-partial`: JPEG traversal stopped on corruption, truncation, or a limit.
33+
- `metadata-partial`: JPEG container traversal completed and common TIFF/EXIF decoding was attempted; XMP/IPTC/ICC and unknown fields remain incomplete.
34+
- `metadata-inspected`: reserved for future broader decoders.

‎docs/format-support.md‎

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,30 @@
11
# Format Support
22

3-
| Capability | JPEG | PNG | WebP |
4-
| --------------------------- | --------------------------------- | --------- | --------- |
5-
| Signature detection | Supported | Supported | Supported |
6-
| Bounded container traversal | Supported | Not yet | Not yet |
7-
| APP/COM classification | Supported | N/A | N/A |
8-
| EXIF container detection | Supported | Not yet | Not yet |
9-
| XMP container detection | Supported, including extended XMP | Not yet | Not yet |
10-
| ICC container detection | Supported | Not yet | Not yet |
11-
| IPTC/Photoshop detection | Supported | Not yet | Not yet |
12-
| Metadata field decoding | Not yet | Not yet | Not yet |
13-
| Cleaning | Not yet | Not yet | Not yet |
3+
| Capability | JPEG | PNG | WebP |
4+
| --------------------------- | ---------------------- | -------------- | -------------- |
5+
| Signature detection | Supported | Supported | Supported |
6+
| Bounded container traversal | Supported | Not yet | Not yet |
7+
| EXIF container detection | Supported | Not yet | Not yet |
8+
| TIFF header and IFD0 | Supported through JPEG | Not integrated | Not integrated |
9+
| ExifIFD and GPSIFD | Supported through JPEG | Not integrated | Not integrated |
10+
| Common EXIF/GPS fields | Supported subset | Not integrated | Not integrated |
11+
| MakerNote decoding | Not supported | Not supported | Not supported |
12+
| XMP payload decoding | Not yet | Not yet | Not yet |
13+
| IPTC/ICC payload decoding | Not yet | Not yet | Not yet |
14+
| Cleaning and verification | Not yet | Not yet | Not yet |
1415

15-
## JPEG
16+
## TIFF/EXIF subset
1617

17-
JPEG detection requires `FF D8`. Container inspection validates marker boundaries and two-byte big-endian declared lengths, recognizes standalone markers and fill bytes, stops at EOI, and reports trailing bytes. SOS headers are traversed, while entropy-coded bytes are skipped without decoding; `FF 00`, RST0–RST7, and multiple scans are handled structurally.
18+
Both `II` and `MM` byte orders are supported. Traversal covers IFD0, ExifIFDPointer, GPSInfoIFDPointer, and next-IFD links with table, entry, depth, offset, and cycle checks.
1819

19-
Payload signatures identify:
20+
Decoded IFD0 tags: ImageDescription, Make, Model, Orientation, Software, DateTime, Artist, and Copyright.
2021

21-
- APP0 `JFIF\0` and `JFXX\0` as technical container data;
22-
- APP1 `Exif\0\0` as EXIF;
23-
- APP1 standard and extended Adobe XMP identifiers as XMP;
24-
- APP2 `ICC_PROFILE\0` as ICC;
25-
- APP13 `Photoshop 3.0\0` as Photoshop/IPTC;
26-
- APP14 `Adobe` as rendering/container data;
27-
- COM as comment metadata.
22+
Decoded ExifIFD tags: ExposureTime, FNumber, PhotographicSensitivity, ExifVersion, DateTimeOriginal, DateTimeDigitized, FocalLength, PixelXDimension, PixelYDimension, and FocalLengthIn35mmFilm. MakerNote is named and retained as opaque structure.
2823

29-
Unknown APP payloads remain unknown. No TIFF, EXIF, XMP XML, ICC, IPTC, thumbnail, frame, Huffman, quantization, or entropy payload is decoded.
24+
Decoded GPS tags: GPSVersionID, GPSLatitudeRef, GPSLatitude, GPSLongitudeRef, GPSLongitude, GPSAltitudeRef, GPSAltitude, GPSTimeStamp, and GPSDateStamp. Coordinates remain exact raw rational components plus reference fields; decimal coordinates are not derived.
3025

31-
## PNG and WebP
26+
Unknown tags remain structurally represented without speculative meaning or large binary values.
3227

33-
PNG requires its complete eight-byte signature. WebP requires `RIFF` at offset 0 and `WEBP` at offset 8. Their chunk structures, sizes, CRCs, metadata, and image payloads are not yet parsed.
28+
## Remaining container support
29+
30+
JPEG marker and scan traversal remains supported. XMP, ICC, and Photoshop/IPTC signatures are container-detected only. PNG requires its complete signature and WebP requires `RIFF....WEBP`; their chunks and metadata are not parsed yet.

0 commit comments

Comments
 (0)