セキュリティ¶
セキュリティ修正の対象は、PyPIで公開されている最新リリースだけです。
脆弱性を報告する¶
GitHub Security Advisories から非公開で報告してください。公開Issueは作成しないでください。最初の応答は 1週間以内を目安にしています。
信頼できないPDFを扱う¶
pylopdfはRust製で必須Python依存もありませんが、悪意あるPDF入力の解析には 本質的なリスクがあります。
資源ポリシーを一式指定する
pylopdf.open()へlimits=pylopdf.DocumentLimits.web()を渡してください。
メモリに制約のあるWeb workerやqueue workerでユーザー入力を扱うための、
保守的な初期値です。
import pylopdf
try:
with pylopdf.open(
"upload.pdf",
limits=pylopdf.DocumentLimits.web(),
) as doc:
facts = doc.complexity
preview = doc[0].get_pixmap(dpi=144)
except pylopdf.LimitError as error:
reject_upload(error.code)
Webプロファイルは現在、次の上限を独立に適用します。
| 資源 | 上限 |
|---|---|
| 入力ファイル | 10 MiB |
| ページ数 | 200 |
| 間接オブジェクト数 | 100,000 |
| 画像のRGBA見積りを含む個々の展開ストリーム | 64 MiB |
| 個々のページ内容ストリーム | 10 MiB |
| ストリームの累積展開/見積りbyte | 128 MiB |
| 直接array/dictionaryの入れ子 | 64 |
| 解釈済みページ全体のUTF-8 glyph payload | 1 MiB |
| rendering/extractionへ渡す完全なPDF snapshot | 64 MiB |
| 解釈済みページ全体の位置付きglyph record | 65,536 |
ワークロードに合わせる場合はDocumentLimits(...)を直接作成します。None以外は
正の整数でなければなりません。従来のmax_decompressed_size=はストリーム単位の
短縮指定として残りますが、limits=とは同時指定できません。
page count/lookup、complexity、render snapshot、TOC/link、annotation、
Form import、page構造編集を含むすべての公開page indexingは、1つの反復的な
page-tree walkerを使います。Page/Pages objectの再利用とcycle、32参照を超える
間接/Kids chain、256階層を超える内部tree、間接object数を超えるedgeを拒否します。
max_pagesは上限を超えた最初のpageで停止します。pageポリシーなしのopenは互換性を
維持し、これらのpage-tree検査を最初のpage-indexing操作まで遅延します。
LimitErrorはPdfErrorのsubclassです。安定したcodeはfile_size、
page_count、object_count、object_depth、decompressed_size、
page_content_size、total_decompressed_size、text_size、
text_glyph_count、interpretation_size、embedded_file_size、
embedded_file_input_size、form_field_input_size、form_content_size、
annotation_input_size、
metadata_input_size、toc_input_size、page_label_input_size、
xmp_metadata_size、render_output_size、
markdown_output_size、svg_output_size、replacement_input_size、
replacement_output_size、pdf_output_size、image_input_size、
image_pixel_count、font_input_size、text_input_size、text_line_count、
search_input_size、search_hit_count、password_input_size、pixmap_output_size、
ocr_model_size、ocr_dictionary_entries、stream_filter_count、
decompression_unverifiableの
いずれかです。同じ値をerror.args[0]でも取得できます。
安全に上限計算できないfilter chainは、楽観的に展開せず拒否します。
pylopdfが所有する上限付きlopdf decode経路では、filter listをmaterializeする
前に16層を超える/Filter chainも拒否します。
doc.complexityはstreamを展開せずrendererも呼ばずに、ページ数、object数、
stream数、圧縮状態のstream byte数、直接objectの最大深度を返します。重い抽出へ
進む前のroutingに利用できます。構造・展開上限は開いたsourceを検査するため、
生成物が別のtrust boundaryを越えるときは同じポリシーで開き直してください。
直接深度検証とcomplexity検査の反復走査stackはfallibleに拡張されるため、
allocation refusalは部分的なfactsではなくPdfErrorになります。
max_interpretation_sizeは、hayroが保持済みinputを最初に読むときと、編集、復号、
AcroForm state選択後の現在状態をpylopdfがserializeするときに適用されます。上限付き
writerは境界を越えるwriteを拒否し、不完全なrenderer/extractor cacheを登録しません。
互換性のため既定値はNoneで、DocumentLimits.web()は64 MiBです。
max_text_sizeを設定すると、plain-text抽出は組立後の正確なUTF-8 sizeを事前検査し、
private batchをglyph payload budgetの2倍に制限します。空でないglyphはpayloadを
最低1 byte持ち、推定gapと改行の合計はglyph数を超えません。batchは最大4,096 page
entryのため、同じpageの反復でもポリシーを回避できません。拒否codeはtext_sizeの
ままで、互換性のため既定値はNoneです。
max_text_glyphsはline組立前に保持するrecord数を制限するため、構造化textが
materializeできるblock、line、span、wordの数も制限します。同じpageのtext解釈と
table解釈は1回分の累積admissionを共有し、拒否されたpageはbudgetを消費しません。
互換性のため既定値はNoneで、DocumentLimits.web()は65,536です。
寛容な読み込みが修復するのは1つの限定ケースだけです。同じ最終revisionに完全な
classic xref tableがあり、元の上限で全体parseが成功した場合に限り、誤った最終
startxrefを置き換えます。object headerの走査、xref streamの修復、以前のrevision
への巻き戻しは行いません。修復時はPylopdfWarningが発生し、
doc.is_repaired(metadata probeではrepaired)がTrueになります。保存すると
xref dataを正規化します。
- レンダリングは1ページ64メガピクセルまでです。
Document.render_page()、Page.render()、Pixmap.tobytes()のencoded PNG出力上限は既定64 MiBです。writerがPythonbytesを返す前に境界を越える writeを拒否し、max_size=Noneで明示解除できます。renderingはrender_output_size、Pixmap直接encodeはpixmap_output_sizeを返します。Document.tobytes()は通常・object/xref stream・暗号化出力すべてに512 MiBの 既定serialization上限を適用します。Rust writerがPythonbytes変換前に境界を 越えるwriteを拒否し、max_size=Noneで明示的に解除できます。save()はtargetと 同じdirectoryに安全に作成したsiblingへstreamし、完全なwrite後だけ要求pathを 原子的に置換するため、serialization/置換失敗時も既存fileを保持します。この in-memory上限の対象外です。garbage、deflate、object_streamsなどのsave optionは、その後I/Oが失敗しても文書化済みのmutation semanticsを維持します。Pixmap.save()はtarget directory内に予測不能かつ排他的に作成したsiblingへ PNG encodeを直接streamし、完全なwriteが成功した後だけ要求pathを原子的に 置換します。完成PNGをメモリにもう1つ保持しません。置換失敗時は既存outputを 保持し、一時fileを削除します。Page.insert_image()はencoded JPEG/PNG inputを既定64 MiB、decoded PNG inputを 既定64,000,000画素に制限します。filenameはGILを解放したRust境界で上限付きで 読み、PNG dimensionはdecoded storage確保前に検査します。信頼できるworkloadはmax_size=None/max_pixels=Noneで明示解除できます。insert_text、insert_textbox、set_form_field、set_fallback_fontの 明示/自動OpenType inputは既定64 MiBです。bufferはPyO3 copy前に拒否し、 filenameはGILを解放した上限付きRust pathで読みます。信頼できるworkloadはmax_font_size=Noneで明示解除できます。insert_text()とinsert_textbox()の生成text inputは既定でUTF-8 1 MiB、 物理行・折り返し後layoutは4,096行が上限です。PythonはPyO3 copy前に物理行を 検査し、Rust境界も再検査してmutation前に折り返し行を止めます。textboxのtab 展開量は展開済みstringを確保する前に計算します。信頼できる挿入inputはmax_text_size=Noneで明示解除でき、拒否codeはtext_input_sizeまたはtext_line_countです。AcroFormのtext/choice外観は固定4,096行上限を維持します。 UAX #14/grapheme index、行collection、保持する行textはfallibleに拡張され、 allocation refusalで部分layoutやdocument編集を公開しません。pylopdfが所有する 正規化、grapheme参照、shaped line/glyph collection、WinAnsi出力、入力由来の text operator拡張もfallibleです。Standard 14の幅検査はencoded text copyを 確保しません。allocation refusalはPdfErrorを返し、挿入は非mutationのまま、 form fillは原子的にrollbackします。search_for()の検索語はUTF-8 4,096 byte、返却geometryは既定4,096件が上限です。 PythonはPyO3 copy前に検索語を拒否し、Rust境界も両方の上限を再検査します。 信頼できる結果集合はmax_hits=Noneで明示解除でき、拒否codeはsearch_input_size/search_hit_countです。partial listは返しません。- open、authenticate、高速metadata probe、AES-256出力のpasswordはUTF-8 127 byteで
PyO3 copy/password KDF前に拒否します。Rust直呼びも再検査し、codeは
password_input_sizeです。保存拒否はdocument mutation/output作成より前です。 render_page_svg()とPage.render_svg()のUTF-8出力上限は既定64 MiBで、 PyO3がPython stringを作る前に超過を拒否します。max_size=Noneで明示的に 解除できます。hayro-svg 0.7が完成したStringだけを返すため、pylopdfが 制限を適用する前の内部Rust string 1つはこの境界の対象外です。- 描画挿入はcache無効化・入力decode・dependent object作成の前に、page
/Contentsのraw arrayと参照chainを検査します。raw arrayは4,096 entry、 chainは深さ32、最終arrayは一度だけ追加するq/Qisolation pair込みで 4,096 stream参照が上限です。失敗時はdocumentを変更しません。 Page.replace_text()はsearch・replacement・fallbackの合計を4,096 UTF-8 byteに制限し、展開page content、font encoding data、置換増幅、最終streamの 既定上限を64 MiBにします。page専用streamをcommit前に準備するため、複製pageの 共有contentを変更せず、no-match/errorではdocumentとcacheを保持します。 caller textはPyO3 copy前に完全なencoded copyを作らず逐次計数します。 信頼できる入力ではmax_size=Noneで明示的に解除できます。delete_pages()・select()・insert_pdf()はPythonとRustの双方で1 call 4,096 page entryが上限です。iterableは4,097 item目でgraph変更前に停止します。 空deleteはcache・generation・既存Pageviewを保持します。Page.get_images()は1ページで4,096配置、累積64,000,000 source画素、返却payload 64 MiBを超える部分結果を拒否します。Flate-wrapped JPEG passthroughも残りbyte 上限までしか展開しません。Document.embfile_add()はPyO3 copy前に64 MiB超の入力を拒否し、embfile_get()は各filter層の展開結果に同じ既定上限を適用します。既知の大容量 添付ではmax_size=を増やせます。max_size=Noneは無制限の入力または materializationを明示的に許可します。添付名treeも4,096 entry/node、深さ32、 encoded/decoded name合計1 MiBを超えると拒否します。callerのlookup/削除名と 追加時のkey/filename/description入力は、tree走査やdata copy前に合計1 MiBで 停止し、embedded_file_input_sizeを使います。編集時はinline FileSpecのclone前に、直接 object 4,096個、深さ32、直接string/name/stream data 1 MiBの上限とCatalogの 書込先を検証し、rollbackのために文書全体をcloneしません。Document.get_pdfa_claim()は各filter層のXMP展開結果を既定で1 MiBに制限します。 既知の大容量packetではmax_size=を増やせます。max_size=Noneは無制限の materializationを明示的に許可します。Page.insert_ocr_text_layer()は4,096個を超える非空wordまたはUTF-8 text合計 1 MiBを超える時点でiterableのmaterializationを停止します。core直接呼出しも 同じ上限を適用し、65,535種類目のCID割当前に停止し、入力由来bufferをPDF変異前に 準備します。- ページラベル番号treeは4,096 entry/node、深さ32、encoded/decoded
style・prefix text合計1 MiBを超える部分結果を拒否します。参照cycleは一度だけ
訪問し、書き込みはPyO3 copy前に
page_label_input_sizeで同じentry/text上限を 適用します。 - AcroForm field treeは4,096 entry/node、8,192 edge、深さ64、encoded/
decoded/returned name・value 1 MiB、choice value 4,096 itemを超える部分結果を
拒否します。参照cycleは一度だけ訪問し、継承値は返却leafごとに課金します。
fillにも同じtree上限とcaller名/値1 MiB上限を原子的に適用し、font探索・button
lookup・file読込前の拒否には
form_field_input_sizeを使います。 - AcroForm button fieldは4,096 widget、8,192 normal appearance state entry、
4,096 unique returned state name、encoded/returned state-name text 1 MiBを
超えると拒否します。fillは不足する
Off/on state keyを変更前に課金します。 - 注釈・link読み取りは4,096
/Annotsentryまたは1 call当たりaggregate encoded/returned metadata text 1 MiBを超える部分結果を拒否します。追加は同じ page件数、生成subtypeとContents/URI入力の合計1 MiB、highlight 4,096矩形を dependent object作成とcache無効化の前に検査します。caller textはPyO3 copyや rectangle iteration前にannotation_input_sizeで拒否し、highlight iterationは 4,097 item目で停止します。 - named destination lookupは参照cycleを一度だけ訪問し、4,096 entry/node、
8,192 edge、深さ32、key byte 1 MiBを超えるtreeを、通常の未解決として黙って
扱わず拒否します。
Page.get_links()はnamed linkごとにtreeを再走査せず、 1 callにつき1つのborrowed indexを構築します。 - TOC読み取りは反復outline walkで参照cycleを一度だけ訪問し、GILを解放します。
4,096 node/entry、8,192 edge、深さ64、destination間接参照32段、source/returned
text 1 MiBを超える部分結果を拒否します。書き込みもentry・深さ・title textを
PyO3 copy・変更前に
toc_input_sizeで検査します。 Document.metadataは標準Info 8項目だけをdecodeし、aggregate source/returned text 1 MiBを超えると拒否します。custom entryはPython出力にmaterializeしません。peek_metadata(max_file_size=)はpathまたはbyte inputをparse前に拒否でき、 returned standard textも制限します。入力の既定値は無制限です。書き込みは source/encoded text 1 MiBをPyO3 copy前にmetadata_input_sizeで検査して 原子的に適用します。- 埋め込みJavaScriptは設計上非対応で、実行されません。
render_pages()は最大4,096 page entryで、累積encoded PNG上限は既定512 MiB です。並列結果は1つのatomic budgetを共有し、失敗時に部分listを返しません。max_size=Noneで明示的に解除できます。worker admissionはlive raster/ conversion bufferを別に制御するため、application側で無制限の並列呼び出しを 重ねないでください。Document.to_markdown()は最大4,096 page entryで、累積UTF-8出力上限は既定 64 MiBです。heading size集計passとrender passの双方で、同時に保持する interpreted layout・table・wordは1 page分だけです。page出力を組み立てる前に 各tableへ累積残予算を渡します。Table.to_markdown()も既定で同じ上限を持ち、 merged-cell展開を含むescape後の正確なUTF-8 sizeを事前検査します。見出し、 paragraph、list、tableはentryを保持する時点で課金され、page全体はsize確定後に 線形結合します。上限超過時は部分stringを返しません。max_size=Noneで明示的に 解除できます。- CPU deadlineはWorker、process、container側で設定してください。資源上限は 文書化したallocationと出力量を抑えますが、実行中のparserやinterpreterを wall-clock時間で中断する機能ではありません。
- 可能なら、信頼できないファイルの一括処理はsandboxやcontainer内で行ってください。 nativeとPyodideのCIは同じhostile-input回帰契約を共有し、定期Atheris fuzzingは 壊れたxref、cycle、深いobject、broken stream、圧縮bombをseedにします。
依存関係の監査¶
CIはpushごとにcargo auditを実行し、Rust依存ツリーをRustSecの脆弱性データベースと
照合します。
リポジトリ上の正本は
SECURITY.mdです。