Merge pull request #7668 from Snuffleupagus/issue-7665
Prevent an infinite loop in `XRef_fetchUncompressed` for encrypted PDF files with indirect objects in the /Encrypt dictionary (issue 7665)
This commit is contained in:
commit
ea5949f1fd
@ -31,11 +31,12 @@
|
|||||||
var PasswordException = sharedUtil.PasswordException;
|
var PasswordException = sharedUtil.PasswordException;
|
||||||
var PasswordResponses = sharedUtil.PasswordResponses;
|
var PasswordResponses = sharedUtil.PasswordResponses;
|
||||||
var bytesToString = sharedUtil.bytesToString;
|
var bytesToString = sharedUtil.bytesToString;
|
||||||
|
var warn = sharedUtil.warn;
|
||||||
var error = sharedUtil.error;
|
var error = sharedUtil.error;
|
||||||
|
var assert = sharedUtil.assert;
|
||||||
var isInt = sharedUtil.isInt;
|
var isInt = sharedUtil.isInt;
|
||||||
var stringToBytes = sharedUtil.stringToBytes;
|
var stringToBytes = sharedUtil.stringToBytes;
|
||||||
var utf8StringToString = sharedUtil.utf8StringToString;
|
var utf8StringToString = sharedUtil.utf8StringToString;
|
||||||
var warn = sharedUtil.warn;
|
|
||||||
var Name = corePrimitives.Name;
|
var Name = corePrimitives.Name;
|
||||||
var isName = corePrimitives.isName;
|
var isName = corePrimitives.isName;
|
||||||
var isDict = corePrimitives.isDict;
|
var isDict = corePrimitives.isDict;
|
||||||
@ -1932,6 +1933,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
|||||||
var cfDict = dict.get('CF');
|
var cfDict = dict.get('CF');
|
||||||
var streamCryptoName = dict.get('StmF');
|
var streamCryptoName = dict.get('StmF');
|
||||||
if (isDict(cfDict) && isName(streamCryptoName)) {
|
if (isDict(cfDict) && isName(streamCryptoName)) {
|
||||||
|
cfDict.suppressEncryption = true; // See comment below.
|
||||||
var handlerDict = cfDict.get(streamCryptoName.name);
|
var handlerDict = cfDict.get(streamCryptoName.name);
|
||||||
keyLength = (handlerDict && handlerDict.get('Length')) || 128;
|
keyLength = (handlerDict && handlerDict.get('Length')) || 128;
|
||||||
if (keyLength < 40) {
|
if (keyLength < 40) {
|
||||||
@ -2013,7 +2015,15 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
|||||||
this.encryptionKey = encryptionKey;
|
this.encryptionKey = encryptionKey;
|
||||||
|
|
||||||
if (algorithm >= 4) {
|
if (algorithm >= 4) {
|
||||||
this.cf = dict.get('CF');
|
var cf = dict.get('CF');
|
||||||
|
if (isDict(cf)) {
|
||||||
|
// The 'CF' dictionary itself should not be encrypted, and by setting
|
||||||
|
// `suppressEncryption` we can prevent an infinite loop inside of
|
||||||
|
// `XRef_fetchUncompressed` if the dictionary contains indirect objects
|
||||||
|
// (fixes issue7665.pdf).
|
||||||
|
cf.suppressEncryption = true;
|
||||||
|
}
|
||||||
|
this.cf = cf;
|
||||||
this.stmf = dict.get('StmF') || identityName;
|
this.stmf = dict.get('StmF') || identityName;
|
||||||
this.strf = dict.get('StrF') || identityName;
|
this.strf = dict.get('StrF') || identityName;
|
||||||
this.eff = dict.get('EFF') || this.stmf;
|
this.eff = dict.get('EFF') || this.stmf;
|
||||||
@ -2041,6 +2051,7 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildCipherConstructor(cf, name, num, gen, key) {
|
function buildCipherConstructor(cf, name, num, gen, key) {
|
||||||
|
assert(isName(name), 'Invalid crypt filter name.');
|
||||||
var cryptFilter = cf.get(name.name);
|
var cryptFilter = cf.get(name.name);
|
||||||
var cfm;
|
var cfm;
|
||||||
if (cryptFilter !== null && cryptFilter !== undefined) {
|
if (cryptFilter !== null && cryptFilter !== undefined) {
|
||||||
|
@ -633,6 +633,11 @@ var XRef = (function XRefClosure() {
|
|||||||
if (encrypt) {
|
if (encrypt) {
|
||||||
var ids = trailerDict.get('ID');
|
var ids = trailerDict.get('ID');
|
||||||
var fileId = (ids && ids.length) ? ids[0] : '';
|
var fileId = (ids && ids.length) ? ids[0] : '';
|
||||||
|
// The 'Encrypt' dictionary itself should not be encrypted, and by
|
||||||
|
// setting `suppressEncryption` we can prevent an infinite loop inside
|
||||||
|
// of `XRef_fetchUncompressed` if the dictionary contains indirect
|
||||||
|
// objects (fixes issue7665.pdf).
|
||||||
|
encrypt.suppressEncryption = true;
|
||||||
this.encrypt = new CipherTransformFactory(encrypt, fileId,
|
this.encrypt = new CipherTransformFactory(encrypt, fileId,
|
||||||
this.password);
|
this.password);
|
||||||
}
|
}
|
||||||
@ -1079,11 +1084,11 @@ var XRef = (function XRefClosure() {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchIfRef: function XRef_fetchIfRef(obj) {
|
fetchIfRef: function XRef_fetchIfRef(obj, suppressEncryption) {
|
||||||
if (!isRef(obj)) {
|
if (!isRef(obj)) {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
return this.fetch(obj);
|
return this.fetch(obj, suppressEncryption);
|
||||||
},
|
},
|
||||||
|
|
||||||
fetch: function XRef_fetch(ref, suppressEncryption) {
|
fetch: function XRef_fetch(ref, suppressEncryption) {
|
||||||
@ -1201,11 +1206,11 @@ var XRef = (function XRefClosure() {
|
|||||||
return xrefEntry;
|
return xrefEntry;
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchIfRefAsync: function XRef_fetchIfRefAsync(obj) {
|
fetchIfRefAsync: function XRef_fetchIfRefAsync(obj, suppressEncryption) {
|
||||||
if (!isRef(obj)) {
|
if (!isRef(obj)) {
|
||||||
return Promise.resolve(obj);
|
return Promise.resolve(obj);
|
||||||
}
|
}
|
||||||
return this.fetchAsync(obj);
|
return this.fetchAsync(obj, suppressEncryption);
|
||||||
},
|
},
|
||||||
|
|
||||||
fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) {
|
fetchAsync: function XRef_fetchAsync(ref, suppressEncryption) {
|
||||||
|
@ -73,6 +73,7 @@ var Dict = (function DictClosure() {
|
|||||||
this.map = Object.create(null);
|
this.map = Object.create(null);
|
||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
this.objId = null;
|
this.objId = null;
|
||||||
|
this.suppressEncryption = false;
|
||||||
this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict
|
this.__nonSerializable__ = nonSerializable; // disable cloning of the Dict
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,40 +85,40 @@ var Dict = (function DictClosure() {
|
|||||||
// automatically dereferences Ref objects
|
// automatically dereferences Ref objects
|
||||||
get: function Dict_get(key1, key2, key3) {
|
get: function Dict_get(key1, key2, key3) {
|
||||||
var value;
|
var value;
|
||||||
var xref = this.xref;
|
var xref = this.xref, suppressEncryption = this.suppressEncryption;
|
||||||
if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map ||
|
if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map ||
|
||||||
typeof key2 === 'undefined') {
|
typeof key2 === 'undefined') {
|
||||||
return xref ? xref.fetchIfRef(value) : value;
|
return xref ? xref.fetchIfRef(value, suppressEncryption) : value;
|
||||||
}
|
}
|
||||||
if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map ||
|
if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map ||
|
||||||
typeof key3 === 'undefined') {
|
typeof key3 === 'undefined') {
|
||||||
return xref ? xref.fetchIfRef(value) : value;
|
return xref ? xref.fetchIfRef(value, suppressEncryption) : value;
|
||||||
}
|
}
|
||||||
value = this.map[key3] || null;
|
value = this.map[key3] || null;
|
||||||
return xref ? xref.fetchIfRef(value) : value;
|
return xref ? xref.fetchIfRef(value, suppressEncryption) : value;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Same as get(), but returns a promise and uses fetchIfRefAsync().
|
// Same as get(), but returns a promise and uses fetchIfRefAsync().
|
||||||
getAsync: function Dict_getAsync(key1, key2, key3) {
|
getAsync: function Dict_getAsync(key1, key2, key3) {
|
||||||
var value;
|
var value;
|
||||||
var xref = this.xref;
|
var xref = this.xref, suppressEncryption = this.suppressEncryption;
|
||||||
if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map ||
|
if (typeof (value = this.map[key1]) !== 'undefined' || key1 in this.map ||
|
||||||
typeof key2 === 'undefined') {
|
typeof key2 === 'undefined') {
|
||||||
if (xref) {
|
if (xref) {
|
||||||
return xref.fetchIfRefAsync(value);
|
return xref.fetchIfRefAsync(value, suppressEncryption);
|
||||||
}
|
}
|
||||||
return Promise.resolve(value);
|
return Promise.resolve(value);
|
||||||
}
|
}
|
||||||
if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map ||
|
if (typeof (value = this.map[key2]) !== 'undefined' || key2 in this.map ||
|
||||||
typeof key3 === 'undefined') {
|
typeof key3 === 'undefined') {
|
||||||
if (xref) {
|
if (xref) {
|
||||||
return xref.fetchIfRefAsync(value);
|
return xref.fetchIfRefAsync(value, suppressEncryption);
|
||||||
}
|
}
|
||||||
return Promise.resolve(value);
|
return Promise.resolve(value);
|
||||||
}
|
}
|
||||||
value = this.map[key3] || null;
|
value = this.map[key3] || null;
|
||||||
if (xref) {
|
if (xref) {
|
||||||
return xref.fetchIfRefAsync(value);
|
return xref.fetchIfRefAsync(value, suppressEncryption);
|
||||||
}
|
}
|
||||||
return Promise.resolve(value);
|
return Promise.resolve(value);
|
||||||
},
|
},
|
||||||
@ -125,7 +126,7 @@ var Dict = (function DictClosure() {
|
|||||||
// Same as get(), but dereferences all elements if the result is an Array.
|
// Same as get(), but dereferences all elements if the result is an Array.
|
||||||
getArray: function Dict_getArray(key1, key2, key3) {
|
getArray: function Dict_getArray(key1, key2, key3) {
|
||||||
var value = this.get(key1, key2, key3);
|
var value = this.get(key1, key2, key3);
|
||||||
var xref = this.xref;
|
var xref = this.xref, suppressEncryption = this.suppressEncryption;
|
||||||
if (!isArray(value) || !xref) {
|
if (!isArray(value) || !xref) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -134,7 +135,7 @@ var Dict = (function DictClosure() {
|
|||||||
if (!isRef(value[i])) {
|
if (!isRef(value[i])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
value[i] = xref.fetch(value[i]);
|
value[i] = xref.fetch(value[i], suppressEncryption);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
},
|
},
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -39,6 +39,7 @@
|
|||||||
!issue7492.pdf
|
!issue7492.pdf
|
||||||
!issue7544.pdf
|
!issue7544.pdf
|
||||||
!issue7598.pdf
|
!issue7598.pdf
|
||||||
|
!issue7665.pdf
|
||||||
!filled-background.pdf
|
!filled-background.pdf
|
||||||
!ArabicCIDTrueType.pdf
|
!ArabicCIDTrueType.pdf
|
||||||
!ThuluthFeatures.pdf
|
!ThuluthFeatures.pdf
|
||||||
|
BIN
test/pdfs/issue7665.pdf
Normal file
BIN
test/pdfs/issue7665.pdf
Normal file
Binary file not shown.
@ -1044,6 +1044,14 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue7665",
|
||||||
|
"file": "pdfs/issue7665.pdf",
|
||||||
|
"md5": "f1199d16195a61e8232e2d1e742ed46b",
|
||||||
|
"link": false,
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "load",
|
||||||
|
"about": "Encrypted file with indirect objects in the /Encrypt dictionary."
|
||||||
|
},
|
||||||
{ "id": "protectip",
|
{ "id": "protectip",
|
||||||
"file": "pdfs/protectip.pdf",
|
"file": "pdfs/protectip.pdf",
|
||||||
"md5": "676e7a7b8f96d04825361832b1838a93",
|
"md5": "676e7a7b8f96d04825361832b1838a93",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user