def test_pades_dss_object_clobber(requests_mock): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_TWO_FIELDS)) meta1 = signers.PdfSignatureMetadata( field_name='Sig1', validation_context=live_testing_vc(requests_mock), subfilter=PADES, embed_validation_info=True, ) dummy_ref = w.add_object(generic.pdf_string("Hi there")).reference out = signers.sign_pdf(w, meta1, signer=FROM_CA, timestamper=DUMMY_TS) w = IncrementalPdfFileWriter(out) # We're going to reassign the DSS object to another object ID, namely # one that clobbers the dummy_ref object. This should be ample cause # for suspicion. dss = w.root['/DSS'] w.objects[(dummy_ref.generation, dummy_ref.idnum)] = dss w.root['/DSS'] = generic.IndirectObject(idnum=dummy_ref.idnum, generation=dummy_ref.generation, pdf=w) w.update_root() out = BytesIO() w.write(out) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' val_trusted_but_modified(s)
def test_no_refs_in_obj_stm(): w = writer.PdfFileWriter(stream_xrefs=True) obj_stm = w.prepare_object_stream() with pytest.raises(TypeError, match='Stream obj.*references'): w.add_object( generic.IndirectObject(2, 0, w), obj_stream=obj_stm )
def test_historical_read(): reader = PdfFileReader(BytesIO(MINIMAL_ONE_FIELD)) assert reader.total_revisions == 2 # if this test file is ever replaced, the test will probably have to # be rewritten root_ref = generic.IndirectObject(1, 0, reader) acroform_ref = generic.IndirectObject(6, 0, reader) # current value current_root = reader.get_object(root_ref.reference, revision=1) assert current_root == reader.root reader.get_object(acroform_ref.reference, revision=1) previous_root = reader.get_object(root_ref.reference, revision=0) assert '/AcroForm' not in previous_root with pytest.raises(misc.PdfReadError): reader.get_object(acroform_ref.reference, revision=0) assert Reference(6, 0) in reader.xrefs.explicit_refs_in_revision(1) assert Reference(2, 0) in reader.xrefs.explicit_refs_in_revision(0) assert Reference(2, 0) not in reader.xrefs.explicit_refs_in_revision(1)
def add_object(self, obj, obj_stream: Optional[ObjectStream] = None, idnum=None) -> generic.IndirectObject: """ Add a new object to this writer. :param obj: The object to add. :param obj_stream: An object stream to add the object to. :param idnum: Manually specify the object ID of the object to be added. This is only allowed for object IDs that have previously been allocated using :meth:`allocate_placeholder`. :return: A :class:`~.generic.IndirectObject` instance referring to the object just added. """ if idnum is not None: if idnum not in self._allocated_placeholders: raise PdfWriteError( "Manually specifying idnum is only allowed for " "references previously allocated using " "allocate_placeholder()." ) preallocated = True else: preallocated = False idnum = self._lastobj_id + 1 if obj_stream is None: self.objects[(0, idnum)] = obj elif obj_stream in self.object_streams: obj_stream.add_object(idnum, obj) self.objs_in_streams[idnum] = obj else: raise PdfWriteError( f'Stream {repr(obj_stream)} is unknown to this PDF writer.' ) if preallocated: self._allocated_placeholders.remove(idnum) else: self._lastobj_id += 1 return generic.IndirectObject(idnum, 0, self)
def fieldmdp_reference_dictionary(field_mdp_spec: FieldMDPSpec, md_algorithm: str, data_ref: generic.Reference): data_ref = generic.IndirectObject(data_ref.idnum, data_ref.generation, data_ref.pdf) # this is part of the /Reference entry of the signature object. return generic.DictionaryObject({ pdf_name('/Type'): pdf_name('/SigRef'), pdf_name('/TransformMethod'): pdf_name('/FieldMDP'), pdf_name('/Data'): data_ref, pdf_name('/DigestMethod'): pdf_name('/' + md_algorithm.upper()), pdf_name('/TransformParams'): field_mdp_spec.as_transform_params() })
def allocate_placeholder(self) -> generic.IndirectObject: """ Allocate an object reference to populate later. Calls to :meth:`get_object` for this reference will return :class:`~.generic.NullObject` until it is populated using :meth:`add_object`. This method is only relevant in certain advanced contexts where an object ID needs to be known before the object it refers to can be built; chances are you'll never need it. :return: A :class:`~.generic.IndirectObject` instance referring to the object just allocated. """ idnum = self._lastobj_id + 1 self._allocated_placeholders.add(idnum) self._lastobj_id += 1 return generic.IndirectObject(idnum, 0, self)
return generic.ArrayObject([generic.NumberObject(7)]) path_test_obj = generic.DictionaryObject({ pdf_name('/Blah'): generic.DictionaryObject({ pdf_name('/Bleh'): generic.ArrayObject([generic.NumberObject(5), pdf_name('/Foo')]), pdf_name('/Null'): generic.NullObject(), }), pdf_name('/WithRefs'): generic.DictionaryObject({ pdf_name('/Arr'): generic.IndirectObject(1, 0, PathMockHandler()), pdf_name('/String'): generic.IndirectObject(0, 0, PathMockHandler()) }) }) @pytest.mark.parametrize('path, result', [(RawPdfPath('/Blah', '/Bleh', 1), '/Foo'), (RawPdfPath('/Blah', '/Bleh', 0), 5), (RawPdfPath('/Blah', '/Null'), generic.NullObject()), (RawPdfPath('/WithRefs', '/Arr', 0), 7), (RawPdfPath('/WithRefs', '/String'), 'OK')]) def test_path_access(path, result): assert path.access_on(path_test_obj) == result