qpdf-compress 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -2
- package/package.json +1 -1
- package/scripts/prebuilds.json +9 -9
- package/src/content.cc +17 -6
package/CHANGELOG.md
CHANGED
|
@@ -6,11 +6,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/), and this
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
## [0.8.
|
|
9
|
+
## [0.8.2] - 2026-09-28
|
|
10
10
|
|
|
11
11
|
### Fixed
|
|
12
12
|
|
|
13
|
-
- **The
|
|
13
|
+
- **The content-stream minifier could hang on a form feed or vertical tab.** `minifyContentStreams` skipped only four whitespace bytes (space, tab, CR, LF) in its dispatcher, while its token loops stopped on `std::isspace`, which also counts form feed (`0x0C`) and vertical tab (`0x0B`) — both legal PDF whitespace. A form feed between two operators (a valid content stream such as `q\fQ`) therefore reached the token branch, which consumed nothing, so the cursor never advanced and the loop appended a space forever until the process ran out of memory. It runs on a libuv worker thread, so a caller's `try`/`catch` cannot interrupt it — a single crafted document is a denial of service. The dispatcher now skips whatever `std::isspace` recognises, and the token branch guarantees forward progress on any byte no branch consumes (a stray unbalanced `)` stalled it the same way), so no input can wedge the loop.
|
|
14
|
+
|
|
15
|
+
- **Correction to the 0.8.1 release notes.** 0.8.1 shipped with an entry claiming that 0.8.0 had been published without its prebuilt binaries and should be skipped. That was wrong. The REST `assets` array reads empty for releases flagged immutable, and an empty array was mistaken for missing files. Every 0.8.0 tarball is attached and downloadable, each one's SHA-256 matches the pin shipped inside the npm package, and `npm install qpdf-compress@0.8.0` installs the prebuilt binary without compiling anything. **0.8.0 is fine to use and is not deprecated.**
|
|
16
|
+
|
|
17
|
+
## [0.8.1] - 2026-09-23
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- The release workflow attaches the prebuilt tarballs to a draft release and publishes it afterwards, instead of publishing first and uploading after: immutable releases reject an upload to an already-published release, so the draft ordering keeps the upload clear of that edge. A following step then confirms every tarball is downloadable from the release before the job succeeds.
|
|
14
22
|
|
|
15
23
|
### Added
|
|
16
24
|
|
package/package.json
CHANGED
package/scripts/prebuilds.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
|
-
"darwin-arm64": "
|
|
3
|
-
"darwin-x64": "
|
|
4
|
-
"linux-arm": "
|
|
5
|
-
"linux-arm64": "
|
|
6
|
-
"linux-musl-arm64": "
|
|
7
|
-
"linux-musl-x64": "
|
|
8
|
-
"linux-x64": "
|
|
9
|
-
"win32-arm64": "
|
|
10
|
-
"win32-x64": "
|
|
2
|
+
"darwin-arm64": "a2fda84bd3569abf5a39b7ce30a58652b8189751cedbaf943e391c9f0b45646c",
|
|
3
|
+
"darwin-x64": "a908f0fe57b78a10e7dfa461a395da050da5499715f5aae2cf793dfe7a982e5f",
|
|
4
|
+
"linux-arm": "39531172aa069e69e3b7db3ffc2462572fddf2a725d2f9ac9abca06513111184",
|
|
5
|
+
"linux-arm64": "1216bea0d7a82fe47bcf3f77f3219f6f3a1620480ed42b3f3a9d0d688f88dbd3",
|
|
6
|
+
"linux-musl-arm64": "d0fe61e513147dced9454a590fe4c17a8f39c0ed9ed858b3f9d44d7b33ec0e86",
|
|
7
|
+
"linux-musl-x64": "865748574c08bdd74decdb7c9acd91b7a0c7e4269e3cb48811d8a9b8c654ff17",
|
|
8
|
+
"linux-x64": "2e766fd34ee2ff86d0f4e8f02008b08507b4d59081bea68185dd78e1341be91f",
|
|
9
|
+
"win32-arm64": "c6793d96feb0a272ced9ba18ec97b29dce6075fcef9692f095defa699252f657",
|
|
10
|
+
"win32-x64": "fcc1c299080e87a5932d5dbe7fcb2ec5c849cf98a094eff55f8408ed1ccce524"
|
|
11
11
|
}
|
package/src/content.cc
CHANGED
|
@@ -90,8 +90,10 @@ void minifyContentStreams(QPDF &qpdf) {
|
|
|
90
90
|
while (pos < raw.size()) {
|
|
91
91
|
char ch = raw[pos];
|
|
92
92
|
|
|
93
|
-
// skip whitespace
|
|
94
|
-
|
|
93
|
+
// skip whitespace — use the same predicate the token loops below use
|
|
94
|
+
// (std::isspace), so a form-feed or vertical-tab can never fall through
|
|
95
|
+
// to the token branch and stall the tokenizer.
|
|
96
|
+
if (std::isspace(static_cast<unsigned char>(ch))) {
|
|
95
97
|
if (!minified.empty())
|
|
96
98
|
needSpace = true;
|
|
97
99
|
++pos;
|
|
@@ -203,10 +205,6 @@ void minifyContentStreams(QPDF &qpdf) {
|
|
|
203
205
|
|
|
204
206
|
// regular token (number, operator)
|
|
205
207
|
{
|
|
206
|
-
if (needSpace) {
|
|
207
|
-
minified += ' ';
|
|
208
|
-
needSpace = false;
|
|
209
|
-
}
|
|
210
208
|
size_t start = pos;
|
|
211
209
|
while (pos < raw.size() &&
|
|
212
210
|
!std::isspace(static_cast<unsigned char>(raw[pos])) &&
|
|
@@ -215,6 +213,19 @@ void minifyContentStreams(QPDF &qpdf) {
|
|
|
215
213
|
raw[pos] != ')')
|
|
216
214
|
++pos;
|
|
217
215
|
|
|
216
|
+
// forward-progress guard: a byte that starts no token — a stray ')'
|
|
217
|
+
// or any whitespace the dispatcher above did not catch — leaves
|
|
218
|
+
// pos == start. Without this the outer loop spins forever, appending
|
|
219
|
+
// to `minified` without bound. Skip the byte so progress is guaranteed.
|
|
220
|
+
if (pos == start) {
|
|
221
|
+
++pos;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (needSpace) {
|
|
226
|
+
minified += ' ';
|
|
227
|
+
needSpace = false;
|
|
228
|
+
}
|
|
218
229
|
std::string token(raw, start, pos - start);
|
|
219
230
|
|
|
220
231
|
// trim numeric formatting
|