コンテンツにスキップ

セキュリティ

セキュリティ修正の対象は、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操作まで遅延します。

LimitErrorPdfErrorのsubclassです。安定したcodefile_sizepage_countobject_countobject_depthdecompressed_sizepage_content_sizetotal_decompressed_sizetext_sizetext_glyph_countinterpretation_sizeembedded_file_sizeembedded_file_input_sizeform_field_input_sizeform_content_sizeannotation_input_sizemetadata_input_sizetoc_input_sizepage_label_input_sizexmp_metadata_sizerender_output_sizemarkdown_output_sizesvg_output_sizereplacement_input_sizereplacement_output_sizepdf_output_sizeimage_input_sizeimage_pixel_countfont_input_sizetext_input_sizetext_line_countsearch_input_sizesearch_hit_countpassword_input_sizepixmap_output_sizeocr_model_sizeocr_dictionary_entriesstream_filter_countdecompression_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がPython bytesを返す前に境界を越える writeを拒否し、max_size=Noneで明示解除できます。renderingは render_output_size、Pixmap直接encodeはpixmap_output_sizeを返します。
  • Document.tobytes()は通常・object/xref stream・暗号化出力すべてに512 MiBの 既定serialization上限を適用します。Rust writerがPython bytes変換前に境界を 越えるwriteを拒否し、max_size=Noneで明示的に解除できます。save()はtargetと 同じdirectoryに安全に作成したsiblingへstreamし、完全なwrite後だけ要求pathを 原子的に置換するため、serialization/置換失敗時も既存fileを保持します。この in-memory上限の対象外です。garbagedeflateobject_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=Nonemax_pixels=Noneで明示解除できます。
  • insert_textinsert_textboxset_form_fieldset_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_sizesearch_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/Q isolation 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・既存Page viewを保持します。
  • 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 /Annots entryまたは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です。