-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathpyproject.toml
More file actions
159 lines (147 loc) · 6.13 KB
/
Copy pathpyproject.toml
File metadata and controls
159 lines (147 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "pdf-edit-engine"
version = "0.2.0"
description = "Format-preserving PDF text editing — edit text in existing PDFs while preserving fonts, layout, and visual fidelity"
readme = "README.md"
license = "MIT"
requires-python = ">=3.12"
authors = [{ name = "Aryan B V", email = "aryansalian5678@gmail.com" }]
classifiers = [
"Development Status :: 4 - Beta",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Topic :: Text Processing :: General",
"Topic :: Software Development :: Libraries :: Python Modules",
"Typing :: Typed",
]
keywords = ["pdf", "edit", "text", "replace", "format-preserving", "pikepdf", "font"]
dependencies = [
# F-A-RANGE: pikepdf>=10 closes the v9 CMap-parser allocation bug
# that surfaced on adversarial fuzzing; lxml<7 because pikepdf 10
# is not yet validated against the lxml 7.x line.
"pikepdf>=10,<11",
# F-A-RANGE: fontTools>=4.60.2 ships the TTFont composite-graph
# bound that prevents the deep-glyph-chain DoS class addressed by
# commit 05's MAX_COMPOSITE_DEPTH cap.
"fonttools>=4.60.2,<5",
# F-A-RANGE: pdfminer.six>=20251230 corrects an incremental-update
# parser regression that mis-extracted text from PDFs with linearized
# xref tables.
"pdfminer.six>=20251230,<20270000",
# CVE-2026-41066: lxml < 6.1.0 has default-on XXE that lets untrusted
# XML/XMP input read local files. pikepdf is a transitive consumer
# of lxml when parsing XMP metadata streams, so a hostile PDF could
# exploit this against this library. Pin the floor explicitly to
# document the security requirement; pikepdf itself does not pin it.
# Upper cap <7 keeps us on the validated lxml 6.x line for pikepdf 10.
"lxml>=6.1.0,<7",
]
[project.optional-dependencies]
dev = [
# CVE-2025-71176: pytest <9.0.3 has a /tmp/pytest-of-{user} race
# that allows local DoS / privilege escalation. Dev-only.
"pytest>=9.0.3",
"pytest-cov",
"ruff>=0.4.0",
"mypy>=1.10",
"reportlab>=4.0",
"psutil>=5.9",
"pip-audit>=2.7",
# Differential-render proving harness (tests/harness/diff_render.py).
# pypdfium2 (Apache/BSD) rasterizes pages; numpy vectorizes the pixel
# diff. Both are optional: the harness guards on import availability and
# the ``render`` marker so ``pytest --collect-only`` succeeds without them.
"pypdfium2>=4",
"numpy>=1.26",
# F-A-PIP: pip <25 has 4 CVEs in the cache-tar-extraction path
# (path traversal on malicious source distributions). Dev-only:
# the engine itself does not invoke pip at runtime, but contributors
# using ``pip install -e ".[dev]"`` should be on >=25.
"pip>=25",
]
[project.urls]
Homepage = "https://github.com/AryanBV/pdf-edit-engine"
Repository = "https://github.com/AryanBV/pdf-edit-engine"
Issues = "https://github.com/AryanBV/pdf-edit-engine/issues"
Changelog = "https://github.com/AryanBV/pdf-edit-engine/blob/main/CHANGELOG.md"
[tool.hatch.build.targets.sdist]
exclude = [
"tests/",
"docs/",
".github/",
"Makefile",
"CLAUDE.md",
"experiments/",
"marketing/",
"plans/",
".diagnostic-venv/",
".venv/",
]
[tool.ruff]
line-length = 100
target-version = "py312"
src = ["src"]
[tool.ruff.lint]
select = ["E", "F", "W", "I", "UP", "B", "SIM", "TCH"]
[tool.ruff.lint.per-file-ignores]
# Tests need pragmatic relaxations:
# SLF001 — accessing _private from tests (e.g. _has_symlink_in_path).
# PLR2004 — magic numbers in fixtures.
# BLE001 — broad except in defensive try-blocks that just skip the test.
"tests/**/*.py" = ["SLF001", "PLR2004", "BLE001"]
# F-C-03 / INV-W0-9 — exception-bytes hygiene (no native ruff rule).
# User-visible sinks (raise messages, EditResult.warnings entries,
# Degradation.detail) MUST NOT interpolate ``{exc}`` or ``str(exc)`` —
# pikepdf / fontTools / OS exception bodies can carry attacker-controlled
# bytes (file paths, struct offsets, partial PDF object snippets) and
# leaking them is a passive information-disclosure vector. The convention
# is ``f"...{type(exc).__name__}"`` for the user-visible string and a
# ``logger.error("...", exc_info=True)`` BEFORE the raise/warn so the
# forensic traceback survives in logs. Forensic ``logger.error/.warning/
# .debug/.info`` calls themselves are exempt.
#
# Ruff has no native lint for "f-string interpolates a specific identifier"
# without a custom plugin, and adding a plugin is out of scope. Enforcement
# lives in ``tests/invariants/test_w0_9_no_exc_bytes_in_user_text.py`` —
# an AST probe that walks every ``.py`` under ``src/pdf_edit_engine/`` and
# fails on any user-visible sink that interpolates ``exc``. New code paths
# triggering the probe must either (a) replace ``{exc}`` with
# ``{type(exc).__name__}`` or (b) demote the sink to ``logger.*``.
[tool.mypy]
strict = true
packages = ["pdf_edit_engine"]
[tool.coverage.run]
source = ["pdf_edit_engine"]
branch = true
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"raise NotImplementedError",
"if TYPE_CHECKING:",
]
# Audit-findings-v0.1.2.md reports 88% line coverage on a developer
# machine with all metric-equivalent system fonts (Carlito etc.) and
# the real-world corpus PDFs (Chrome / Google-Docs export / resume)
# present. CI runners lack the resume + Chrome fixtures (gitignored)
# and only Linux gets Carlito installed, so CI coverage lands ~80%.
# 75 gives ~5pp headroom under the realistic CI baseline while still
# catching meaningful regressions.
fail_under = 75
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
# Default-exclude `slow` so per-PR runs stay fast (the two clean-install
# probes in test_stress.py rebuild the wheel and add ~60s). Opt back in
# with `pytest -m slow` (run by one matrix cell of CI; see ci.yml).
addopts = "-m 'not slow'"
markers = [
"benchmark: performance benchmark tests",
"slow: slow tests (clean install, etc.)",
"stress: ultimate stress tests",
"render: differential-render visual-fidelity probes (needs pypdfium2 + numpy)",
]