Skip to content

Commit 7ef3758

Browse files
committed
ROB: Prevent sign command from signing already signed pdfs
1 parent 157d8e7 commit 7ef3758

3 files changed

Lines changed: 47 additions & 0 deletions

File tree

pdfly/sign.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from endesive import signer
2525
from fpdf import FPDF, get_scale_factor
2626
from pypdf import PageObject, PdfReader, PdfWriter
27+
from pypdf.generic import PdfObject
2728

2829

2930
def main(
@@ -36,6 +37,8 @@ def main(
3637
validate_output_args_or_raise(output, in_place)
3738

3839
pdf_reader = PdfReader(filename)
40+
pdf_is_unsigned_or_raise(pdf_reader)
41+
3942
output_file: Union[io.BufferedWriter, tempfile._TemporaryFileWrapper]
4043
if output:
4144
output_file = open(output, "wb")
@@ -55,6 +58,28 @@ def main(
5558
output.unlink()
5659

5760

61+
def pdf_is_unsigned_or_raise(pdf_reader: PdfReader) -> None:
62+
for page in pdf_reader.pages:
63+
if page.annotations is None:
64+
continue
65+
66+
if any(is_signature(annotation) for annotation in page.annotations):
67+
raise typer.BadParameter("PDF is already signed.")
68+
69+
70+
def is_signature(annotation: PdfObject) -> bool:
71+
resolved_annotation_object = annotation.get_object()
72+
if resolved_annotation_object is None:
73+
return False
74+
75+
subtype = resolved_annotation_object["/Subtype"]
76+
if subtype != "/Widget":
77+
return False
78+
79+
fieldtype = resolved_annotation_object["/FT"]
80+
return fieldtype == "/Sig"
81+
82+
5883
def _sign_pdf_contents(
5984
pdf_reader: PdfReader,
6085
output_file: Union[io.BufferedWriter, tempfile._TemporaryFileWrapper],

resources/sign_pkcs12.pdf

17.1 KB
Binary file not shown.

tests/test_sign.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@ def test_sign_missing_certificate_key_option(capsys, tmp_path):
1616
assert "Missing option" in captured.err
1717

1818

19+
def test_sign_already_signed_pdf(capsys, tmp_path):
20+
# Act
21+
with chdir(tmp_path):
22+
exit_code = run_cli(
23+
[
24+
"sign",
25+
str(RESOURCES_ROOT / "sign_pkcs12.pdf"),
26+
"-o",
27+
"out.pdf",
28+
"--p12",
29+
str(RESOURCES_ROOT / "signing-certificate.p12"),
30+
"--p12-password",
31+
"fpdf2",
32+
]
33+
)
34+
captured = capsys.readouterr()
35+
36+
# Assert
37+
assert exit_code == 2
38+
assert "already signed" in captured.err
39+
40+
1941
def test_sign_pkcs12(capsys, tmp_path):
2042
# Act
2143
with chdir(tmp_path):

0 commit comments

Comments
 (0)