pyhanko.sign.diff_analysis module¶
New in version 0.2.0: pyhanko.sign.diff_analysis
extracted from
pyhanko.sign.validation
and restructured into a more rule-based
format.
This module defines utilities for difference analysis between revisions of the same PDF file. PyHanko uses this functionality to validate signatures on files that have been modified after signing (using PDF’s incremental update feature).
In pyHanko’s validation model, every incremental update is disallowed by
default. For a change to be accepted, it must be cleared by at least one
whitelisting rule.
These rules can moreover qualify the modification level at which they accept
the change (see ModificationLevel
).
Additionally, any rule can veto an entire revision as suspect by raising
a SuspiciousModification
exception.
Whitelisting rules are encouraged to apply their vetoes liberally.
Whitelisting rules are bundled in DiffPolicy
objects for use by the
validator.
Guidelines for developing rules for use with StandardDiffPolicy
¶
Caution
These APIs aren’t fully stable yet, so some changes might still occur between now and the first major release.
In general, you should keep the following informal guidelines in mind when putting together custom diff rules.
All rules are either executed completely (i.e. their generators exhausted) or aborted.
If the diff runner aborts a rule, this always means that the entire revision is rejected. In other words, for accepted revisions, all rules will always have run to completion.
Whitelisting rules are allowed to informally delegate some checking to other rules, provided that this is documented clearly.
Note
Example:
CatalogModificationRule
ignores/AcroForm
, which is validated by another rule entirely.Rules should be entirely stateless. “Clearing” a reference by yielding it does not imply that the revision cannot be vetoed by that same rule further down the road (this is why the first point is important).
-
class
pyhanko.sign.diff_analysis.
ModificationLevel
(value)¶ Bases:
pyhanko.pdf_utils.misc.OrderedEnum
Records the (semantic) modification level of a document.
Compare
MDPPerm
, which records the document modification policy associated with a particular signature, as opposed to the empirical judgment indicated by this enum.-
NONE
= 0¶ The document was not modified at all (i.e. it is byte-for-byte unchanged).
-
LTA_UPDATES
= 1¶ The only updates are of the type that would be allowed as part of signature long term archival (LTA) processing. That is to say, updates to the document security store or new document time stamps. For the purposes of evaluating whether a document has been modified in the sense defined in the PAdES and ISO 32000-2 standards, these updates do not count. Adding form fields is permissible at this level, but only if they are signature fields. This is necessary for proper document timestamp support.
-
FORM_FILLING
= 2¶ The only updates are extra signatures and updates to form field values or their appearance streams, in addition to the previous levels.
-
ANNOTATIONS
= 3¶ In addition to the previous levels, manipulating annotations is also allowed at this level.
Note
This level is currently unused by the default diff policy, and modifications to annotations other than those permitted to fill in forms are treated as suspicious.
-
OTHER
= 4¶ The document has been modified in ways that aren’t on the validator’s whitelist. This always invalidates the corresponding signature, irrespective of cryptographical integrity or
/DocMDP
settings.
-
-
exception
pyhanko.sign.diff_analysis.
SuspiciousModification
¶ Bases:
ValueError
Error indicating a suspicious modification
-
class
pyhanko.sign.diff_analysis.
QualifiedWhitelistRule
¶ Bases:
object
Abstract base class for a whitelisting rule that outputs references together with the modification level at which they’re cleared.
This is intended for use by complicated whitelisting rules that need to differentiate between multiple levels.
-
apply_qualified
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.ReferenceUpdate]]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
-
class
pyhanko.sign.diff_analysis.
WhitelistRule
¶ Bases:
object
Abstract base class for a whitelisting rule that simply outputs cleared references without specifying a modification level.
These rules are more flexible than rules of type
QualifiedWhitelistRule
, since the modification level can be specified separately (seeWhitelistRule.as_qualified()
).-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[pyhanko.sign.diff_analysis.ReferenceUpdate]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
as_qualified
(level: pyhanko.sign.diff_analysis.ModificationLevel) → pyhanko.sign.diff_analysis.QualifiedWhitelistRule¶ Construct a new
QualifiedWhitelistRule
that whitelists the object references from this rule at the level specified.- Parameters
level – The modification level at which the output of this rule should be cleared.
- Returns
A
QualifiedWhitelistRule
backed by this rule.
-
-
pyhanko.sign.diff_analysis.
qualify
(level: pyhanko.sign.diff_analysis.ModificationLevel, rule_result: Generator[X, None, R], transform: Callable[[X], pyhanko.sign.diff_analysis.ReferenceUpdate] = <function <lambda>>) → Generator[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.ReferenceUpdate], None, R]¶ This is a helper function for rule implementors. It attaches a fixed modification level to an existing reference update generator, respecting the original generator’s return value (if relevant).
A prototypical use would be of the following form:
def some_generator_function(): # do stuff for ref in some_list: # do stuff yield ref # do more stuff return summary_value # ... def some_qualified_generator_function(): summary_value = yield from qualify( ModificationLevel.FORM_FILLING, some_generator_function() )
Provided that
some_generator_function
yieldsReferenceUpdate
objects, the yield type of the resulting generator will be tuples of the form(level, ref)
.- Parameters
level – The modification level to set.
rule_result – A generator that outputs references to be whitelisted.
transform – Function to apply to the reference object before appending the modification level and yielding it. Defaults to the identity.
- Returns
A converted generator that outputs references qualified at the modification level specified.
-
class
pyhanko.sign.diff_analysis.
ReferenceUpdate
(updated_ref: pyhanko.pdf_utils.generic.Reference, paths_checked: Union[pyhanko.pdf_utils.reader.RawPdfPath, Iterable[pyhanko.pdf_utils.reader.RawPdfPath], NoneType] = None, blanket_approve: bool = False)¶ Bases:
object
-
updated_ref
: pyhanko.pdf_utils.generic.Reference¶ Reference that was (potentially) updated.
-
paths_checked
: Optional[Union[pyhanko.pdf_utils.reader.RawPdfPath, Iterable[pyhanko.pdf_utils.reader.RawPdfPath]]] = None¶
-
blanket_approve
: bool = False¶
-
classmethod
curry_ref
(**kwargs)¶
-
-
class
pyhanko.sign.diff_analysis.
DocInfoRule
¶ Bases:
pyhanko.sign.diff_analysis.WhitelistRule
Rule that allows the
/Info
dictionary in the trailer to be updated.-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[pyhanko.sign.diff_analysis.ReferenceUpdate]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
-
class
pyhanko.sign.diff_analysis.
DSSCompareRule
¶ Bases:
pyhanko.sign.diff_analysis.WhitelistRule
Rule that allows changes to the document security store (DSS).
This rule will validate the structure of the DSS quite rigidly, and will raise
SuspiciousModification
whenever it encounters structural problems with the DSS. Similarly, modifications that remove items from the DSS also count as suspicious.-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[pyhanko.sign.diff_analysis.ReferenceUpdate]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
-
class
pyhanko.sign.diff_analysis.
MetadataUpdateRule
(check_xml_syntax=True, always_refuse_stream_override=False)¶ Bases:
pyhanko.sign.diff_analysis.WhitelistRule
Rule to adjudicate updates to the XMP metadata stream.
The content of the metadata isn’t actually validated in any significant way; this class only checks whether the XML is well-formed.
- Parameters
check_xml_syntax – Do a well-formedness check on the XML syntax. Default
True
.always_refuse_stream_override –
Always refuse to override the metadata stream if its object ID existed in a prior revision, including if the new stream overrides the old metadata stream and the syntax check passes. Default
False
.Note
In other situations, pyHanko will reject stream overrides on general principle, since combined with the fault-tolerance of some PDF readers, these can allow an attacker to manipulate parts of the signed content in subtle but significant ways.
In case of the metadata stream, the risk is significantly mitigated thanks to the XML syntax check on both versions of the stream, but if you’re feeling extra paranoid, you can turn the default behaviour back on by setting
always_refuse_stream_override
toTrue
.
-
static
is_well_formed_xml
(metadata_ref: pyhanko.pdf_utils.generic.Reference)¶ Checks whether the provided stream consists of well-formed XML data. Note that this does not perform any more advanced XML or XMP validation, the check is purely syntactic.
- Parameters
metadata_ref – A reference to a (purported) metadata stream.
- Raises
SuspiciousModification – if there are indications that the reference doesn’t point to an XML stream.
-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[pyhanko.sign.diff_analysis.ReferenceUpdate]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
class
pyhanko.sign.diff_analysis.
CatalogModificationRule
(ignored_keys=None)¶ Bases:
pyhanko.sign.diff_analysis.QualifiedWhitelistRule
Rule that adjudicates modifications to the document catalog.
- Parameters
ignored_keys –
Values in the document catalog that may change between revisions. The default ones are
/AcroForm
,/DSS
,/Extensions
,/Metadata
and/MarkInfo
.Checking for
/AcroForm
,/DSS
and/Metadata
is delegated toFormUpdatingRule
,DSSCompareRule
andMetadataUpdateRule
, respectively.
-
apply_qualified
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.pdf_utils.generic.Reference]]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
class
pyhanko.sign.diff_analysis.
ObjectStreamRule
¶ Bases:
pyhanko.sign.diff_analysis.WhitelistRule
Rule that allows object streams to be added.
Note that this rule only whitelists the object streams themselves (provided they do not override any existing objects, obviously), not the objects in them.
-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[pyhanko.pdf_utils.generic.Reference]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
-
class
pyhanko.sign.diff_analysis.
XrefStreamRule
¶ Bases:
pyhanko.sign.diff_analysis.WhitelistRule
Rule that allows new cross-reference streams to be defined.
-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[pyhanko.pdf_utils.generic.Reference]¶ Apply the rule to the changes between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
-
class
pyhanko.sign.diff_analysis.
FormUpdatingRule
(field_rules: List[pyhanko.sign.diff_analysis.FieldMDPRule], ignored_acroform_keys=None)¶ Bases:
object
Special whitelisting rule that validates changes to the form attached to the input document.
This rule is special in two ways:
it outputs
FormUpdate
objects instead of references;it delegates most of the hard work to sub-rules (instances of
FieldMDPRule
).
A
DiffPolicy
can have at most oneFormUpdatingRule
, but there is no limit on the number ofFieldMDPRule
objects attached to it.FormUpdate
objects contain a reference plus metadata about the form field it belongs to.- Parameters
field_rules – A list of
FieldMDPRule
objects to validate the individual form fields.ignored_acroform_keys – Keys in the
/AcroForm
dictionary that may be changed. Changes are potentially subject to validation by other rules.
-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.FormUpdate]]¶ Evaluate changes in the document’s form between two revisions.
- Parameters
old – The older, base revision.
new – The newer revision to be vetted.
-
class
pyhanko.sign.diff_analysis.
FormUpdate
(updated_ref: pyhanko.pdf_utils.generic.Reference, paths_checked: Optional[Union[pyhanko.pdf_utils.reader.RawPdfPath, Iterable[pyhanko.pdf_utils.reader.RawPdfPath]]] = None, blanket_approve: bool = False, field_name: Optional[str] = None, valid_when_locked: bool = False)¶ Bases:
pyhanko.sign.diff_analysis.ReferenceUpdate
Container for a reference together with (optional) metadata.
Currently, this metadata consists of the relevant field’s (fully qualified) name, and whether the update should be approved or not if said field is locked by the FieldMDP policy currently in force.
-
field_name
: Optional[str] = None¶ The relevant field’s fully qualified name, or
None
if there’s either no obvious associated field, or if there are multiple reasonable candidates.
-
valid_when_locked
: bool = False¶ Flag indicating whether the update is valid even when the field is locked. This is only relevant if
field_name
is notNone
.
-
-
class
pyhanko.sign.diff_analysis.
FieldMDPRule
¶ Bases:
object
Sub-rules attached to a
FormUpdatingRule
.-
apply
(context: pyhanko.sign.diff_analysis.FieldComparisonContext) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.FormUpdate]]¶ Apply the rule to the given
FieldComparisonContext
.- Parameters
context – The context of this form revision evaluation, given as an instance of
FieldComparisonContext
.
-
-
class
pyhanko.sign.diff_analysis.
FieldComparisonSpec
(field_type: str, old_field_ref: Optional[pyhanko.pdf_utils.generic.Reference], new_field_ref: Optional[pyhanko.pdf_utils.generic.Reference], old_canonical_path: Optional[pyhanko.pdf_utils.reader.RawPdfPath])¶ Bases:
object
Helper object that specifies a form field name together with references to its old and new versions.
-
field_type
: str¶ The (fully qualified) form field name.
-
old_field_ref
: Optional[pyhanko.pdf_utils.generic.Reference]¶ A reference to the field’s dictionary in the old revision, if present.
-
new_field_ref
: Optional[pyhanko.pdf_utils.generic.Reference]¶ A reference to the field’s dictionary in the new revision, if present.
-
old_canonical_path
: Optional[pyhanko.pdf_utils.reader.RawPdfPath]¶ Path from the trailer through the AcroForm structure to this field (in the older revision). If the field is new, set to
None
.
-
property
old_field
¶ - Returns
The field’s dictionary in the old revision, if present, otherwise
None
.
-
property
new_field
¶ - Returns
The field’s dictionary in the new revision, if present, otherwise
None
.
-
expected_paths
()¶
-
-
class
pyhanko.sign.diff_analysis.
FieldComparisonContext
(field_specs: Dict[str, pyhanko.sign.diff_analysis.FieldComparisonSpec], old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver)¶ Bases:
object
Context for a form diffing operation.
-
field_specs
: Dict[str, pyhanko.sign.diff_analysis.FieldComparisonSpec]¶ Dictionary mapping field names to
FieldComparisonSpec
objects.
-
old
: pyhanko.pdf_utils.reader.HistoricalResolver¶ The older, base revision.
-
new
: pyhanko.pdf_utils.reader.HistoricalResolver¶ The newer revision.
-
-
class
pyhanko.sign.diff_analysis.
GenericFieldModificationRule
(always_modifiable=None, value_update_keys=None)¶ Bases:
pyhanko.sign.diff_analysis.BaseFieldModificationRule
This rule allows non-signature form fields to be modified at
ModificationLevel.FORM_FILLING
.This rule will take field locks into account if the
FieldComparisonContext
includes aFieldMDPSpec
.For (invisible) document timestamps, this is allowed at
ModificationLevel.LTA_UPDATES
, but in all other cases the modification level will be bumped toModificationLevel.FORM_FILLING
.-
check_form_field
(fq_name: str, spec: pyhanko.sign.diff_analysis.FieldComparisonSpec, context: pyhanko.sign.diff_analysis.FieldComparisonContext) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.FormUpdate]]¶ Investigate updates to a particular form field. This function is called by
apply()
for every form field in the new revision.- Parameters
fq_name – The fully qualified name of the form field.j
spec – The
FieldComparisonSpec
object describing the old state of the field in relation to the new state.context – The full
FieldComparisonContext
that is currently being evaluated.
- Returns
An iterable yielding
FormUpdate
objects qualified with an appropriateModificationLevel
.
-
-
class
pyhanko.sign.diff_analysis.
SigFieldCreationRule
(approve_widget_bindings=True)¶ Bases:
pyhanko.sign.diff_analysis.FieldMDPRule
This rule allows signature fields to be created at the root of the form hierarchy, but disallows the creation of other types of fields. It also disallows field deletion.
In addition, this rule will allow newly created signature fields to attach themselves as widget annotations to pages.
The creation of invisible signature fields is considered a modification at level
ModificationLevel.LTA_UPDATES
, but appearance-related changes will be qualified withModificationLevel.FORM_FILLING
.-
apply
(context: pyhanko.sign.diff_analysis.FieldComparisonContext) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.FormUpdate]]¶ Apply the rule to the given
FieldComparisonContext
.- Parameters
context – The context of this form revision evaluation, given as an instance of
FieldComparisonContext
.
-
-
class
pyhanko.sign.diff_analysis.
SigFieldModificationRule
(always_modifiable=None, value_update_keys=None)¶ Bases:
pyhanko.sign.diff_analysis.BaseFieldModificationRule
This rule allows signature fields to be filled in, and set an appearance if desired. Deleting values from signature fields is disallowed, as is modifying signature fields that already contain a signature.
This rule will take field locks into account if the
FieldComparisonContext
includes aFieldMDPSpec
.For (invisible) document timestamps, this is allowed at
ModificationLevel.LTA_UPDATES
, but in all other cases the modification level will be bumped toModificationLevel.FORM_FILLING
.-
check_form_field
(fq_name: str, spec: pyhanko.sign.diff_analysis.FieldComparisonSpec, context: pyhanko.sign.diff_analysis.FieldComparisonContext) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.FormUpdate]]¶ Investigate updates to a particular form field. This function is called by
apply()
for every form field in the new revision.- Parameters
fq_name – The fully qualified name of the form field.j
spec – The
FieldComparisonSpec
object describing the old state of the field in relation to the new state.context – The full
FieldComparisonContext
that is currently being evaluated.
- Returns
An iterable yielding
FormUpdate
objects qualified with an appropriateModificationLevel
.
-
-
class
pyhanko.sign.diff_analysis.
BaseFieldModificationRule
(always_modifiable=None, value_update_keys=None)¶ Bases:
pyhanko.sign.diff_analysis.FieldMDPRule
Base class that implements some boilerplate to validate modifications to individual form fields.
-
compare_fields
(spec: pyhanko.sign.diff_analysis.FieldComparisonSpec) → bool¶ Helper method to compare field dictionaries.
- Parameters
spec – The current
FieldComparisonSpec
.- Returns
True
if the modifications are permissible even when the field is locked,False
otherwise. If keys beyond those invalue_update_keys
are changed, aSuspiciousModification
is raised.
-
apply
(context: pyhanko.sign.diff_analysis.FieldComparisonContext) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.FormUpdate]]¶ Apply the rule to the given
FieldComparisonContext
.- Parameters
context – The context of this form revision evaluation, given as an instance of
FieldComparisonContext
.
-
check_form_field
(fq_name: str, spec: pyhanko.sign.diff_analysis.FieldComparisonSpec, context: pyhanko.sign.diff_analysis.FieldComparisonContext) → Iterable[Tuple[pyhanko.sign.diff_analysis.ModificationLevel, pyhanko.sign.diff_analysis.FormUpdate]]¶ Investigate updates to a particular form field. This function is called by
apply()
for every form field in the new revision.- Parameters
fq_name – The fully qualified name of the form field.j
spec – The
FieldComparisonSpec
object describing the old state of the field in relation to the new state.context – The full
FieldComparisonContext
that is currently being evaluated.
- Returns
An iterable yielding
FormUpdate
objects qualified with an appropriateModificationLevel
.
-
-
class
pyhanko.sign.diff_analysis.
DiffPolicy
¶ Bases:
object
Analyse the differences between two revisions.
-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver, field_mdp_spec: Optional[pyhanko.sign.fields.FieldMDPSpec] = None, doc_mdp: Optional[pyhanko.sign.fields.MDPPerm] = None) → pyhanko.sign.diff_analysis.DiffResult¶ Execute the policy on a pair of revisions, with the MDP values provided.
SuspiciousModification
exceptions should be propagated.- Parameters
old – The older, base revision.
new – The newer revision.
field_mdp_spec – The field MDP spec that’s currently active.
doc_mdp – The DocMDP spec that’s currently active.
- Returns
A
DiffResult
object summarising the policy’s judgment.
-
review_file
(reader: pyhanko.pdf_utils.reader.PdfFileReader, base_revision: Union[int, pyhanko.pdf_utils.reader.HistoricalResolver], field_mdp_spec: Optional[pyhanko.sign.fields.FieldMDPSpec] = None, doc_mdp: Optional[pyhanko.sign.fields.MDPPerm] = None) → Union[pyhanko.sign.diff_analysis.DiffResult, pyhanko.sign.diff_analysis.SuspiciousModification]¶ Compare the current state of a file to an earlier version, with the MDP values provided.
SuspiciousModification
exceptions should be propagated.If there are multiple revisions between the base revision and the current one, the precise manner in which the review is conducted is left up to the implementing class. In particular, subclasses may choose to review each intermediate revision individually, or handle them all at once.
- Parameters
reader – PDF reader representing the current state of the file.
base_revision – The older, base revision. You can choose between providing it as a revision index, or a
HistoricalResolver
instance.field_mdp_spec – The field MDP spec that’s currently active.
doc_mdp – The DocMDP spec that’s currently active.
- Returns
A
DiffResult
object summarising the policy’s judgment.
-
-
class
pyhanko.sign.diff_analysis.
StandardDiffPolicy
(global_rules: List[pyhanko.sign.diff_analysis.QualifiedWhitelistRule], form_rule: Optional[pyhanko.sign.diff_analysis.FormUpdatingRule], reject_object_freeing=True, ignore_orphaned_objects=True)¶ Bases:
pyhanko.sign.diff_analysis.DiffPolicy
Run a list of rules to analyse the differences between two revisions.
- Parameters
global_rules – The
QualifiedWhitelistRule
objects encoding the rules to apply.form_rule – The
FormUpdatingRule
that adjudicates changes to form fields and their values.reject_object_freeing –
Always fail revisions that free objects that existed prior to signing.
Note
PyHanko resolves freed references to the
null
object in PDF, and a freeing instruction in a cross-reference section is always registered as a change that needs to be approved, regardless of the value of this setting.It is theoretically possible for a rule to permit deleting content, in which case allowing objects to be freed might be reasonable. That said, pyHanko takes the conservative default position to reject all object freeing instructions as suspect.
ignore_orphaned_objects – Some PDF writers create objects that aren’t used anywhere (tsk tsk). Since those don’t affect the “actual” document content, they can usually be ignored. If
True
, newly created orphaned objects will be cleared at levelModificationLevel.LTA_UPDATES
. Default isTrue
.
-
apply
(old: pyhanko.pdf_utils.reader.HistoricalResolver, new: pyhanko.pdf_utils.reader.HistoricalResolver, field_mdp_spec: Optional[pyhanko.sign.fields.FieldMDPSpec] = None, doc_mdp: Optional[pyhanko.sign.fields.MDPPerm] = None) → pyhanko.sign.diff_analysis.DiffResult¶ Execute the policy on a pair of revisions, with the MDP values provided.
SuspiciousModification
exceptions should be propagated.- Parameters
old – The older, base revision.
new – The newer revision.
field_mdp_spec – The field MDP spec that’s currently active.
doc_mdp – The DocMDP spec that’s currently active.
- Returns
A
DiffResult
object summarising the policy’s judgment.
-
review_file
(reader: pyhanko.pdf_utils.reader.PdfFileReader, base_revision: Union[int, pyhanko.pdf_utils.reader.HistoricalResolver], field_mdp_spec: Optional[pyhanko.sign.fields.FieldMDPSpec] = None, doc_mdp: Optional[pyhanko.sign.fields.MDPPerm] = None) → Union[pyhanko.sign.diff_analysis.DiffResult, pyhanko.sign.diff_analysis.SuspiciousModification]¶ Implementation of
DiffPolicy.review_file()
that reviews each intermediate revision between the base revision and the current one individually.
-
pyhanko.sign.diff_analysis.
DEFAULT_DIFF_POLICY
= <pyhanko.sign.diff_analysis.StandardDiffPolicy object>¶ Default
DiffPolicy
implementation.This policy includes the following rules, all with the default settings. The unqualified rules in the list all have their updates qualified at level
LTA_UPDATES
.FormUpdatingRule
, with the following field rules:
-
pyhanko.sign.diff_analysis.
NO_CHANGES_DIFF_POLICY
= <pyhanko.sign.diff_analysis.StandardDiffPolicy object>¶ DiffPolicy
implementation that does not provide any rules, and will therefore simply reject all changes.
-
class
pyhanko.sign.diff_analysis.
DiffResult
(modification_level: pyhanko.sign.diff_analysis.ModificationLevel, changed_form_fields: Set[str])¶ Bases:
object
Encodes the result of a difference analysis on two revisions.
Returned by
DiffPolicy.apply()
.-
modification_level
: pyhanko.sign.diff_analysis.ModificationLevel¶ The strictest modification level at which all changes pass muster.
-
changed_form_fields
: Set[str]¶ Set containing the names of all changed form fields.
Note
For the purposes of this parameter, a change is defined as any
FormUpdate
whereFormUpdate.valid_when_locked
isFalse
.
-