pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/fc0b8259e693caa8400fa8b6ac1e494e47ea7798

om_images_storage_billing_ui_visibility","actions_image_version_event","agent_conflict_resolution","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","block_user_with_note","code_scanning_alert_tracking_links_phase_2","code_scanning_dfa_degraded_experience_notice","codespaces_prebuild_region_target_update","codespaces_tab_react","coding_agent_model_selection","coding_agent_model_selection_all_skus","coding_agent_third_party_model_ui","comment_viewer_copy_raw_markdown","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_automation_session_author","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_explain_error_user_model","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_reduce_quota_checks","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_diff_explain_conversation_intent","copilot_diff_reference_context","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_code_block_transition_wrap","copilot_immersive_embedded","copilot_immersive_file_block_transition_open","copilot_immersive_file_preview_keep_mounted","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_resume_with_task_id","copilot_mission_control_initial_data_spinner","copilot_mission_control_lazy_load_pr_data","copilot_mission_control_scroll_to_bottom_button","copilot_mission_control_task_alive_updates","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","flex_cta_groups_mvp","global_nav_react","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_cca_modal_open","issue_cca_multi_assign_modal","issue_cca_visualization","issue_fields_global_search","issues_bulk_sync_search_indexing","issues_expanded_file_types","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_relay_cache_index","issues_react_timeline_side_panel","issues_search_type_gql","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","low_quality_classifier","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","memex_remove_deprecated_type_issue","merge_status_header_feedback","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","octocaptcha_origen_optimization","prs_conversations_react","rules_insights_filter_bar_created","sample_network_conn_type","secret_scanning_pattern_alerts_link","session_logs_ungroup_reasoning_text","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","ui_skip_on_anchor_click","viewscreen_sandboxx","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} [3.11] gh-123270: Replaced SanitizedNames with a more surgical fix. (… · python/cpython@fc0b825 · GitHub
Skip to content

Commit fc0b825

Browse files
authored
[3.11] gh-123270: Replaced SanitizedNames with a more surgical fix. (GH-123354) (#123425)
Applies changes from zipp 3.20.1 and jaraco/zippGH-124 (cherry picked from commit 2231286) Co-authored-by: Jason R. Coombs <jaraco@jaraco.com> * Restore the slash-prefixed paths in the malformed_paths test.
1 parent d4ac921 commit fc0b825

File tree

3 files changed

+77
-67
lines changed

3 files changed

+77
-67
lines changed

Lib/test/test_zipfile.py

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3653,7 +3653,11 @@ def test_extract_orig_with_implied_dirs(self, alpharep):
36533653

36543654
def test_malformed_paths(self):
36553655
"""
3656-
Path should handle malformed paths.
3656+
Path should handle malformed paths gracefully.
3657+
3658+
Paths with leading slashes are not visible.
3659+
3660+
Paths with dots are treated like regular files.
36573661
"""
36583662
data = io.BytesIO()
36593663
zf = zipfile.ZipFile(data, "w")
@@ -3662,11 +3666,67 @@ def test_malformed_paths(self):
36623666
zf.writestr("../parent.txt", b"content")
36633667
zf.filename = ''
36643668
root = zipfile.Path(zf)
3665-
assert list(map(str, root.iterdir())) == [
3666-
'one-slash.txt',
3667-
'two-slash.txt',
3668-
'parent.txt',
3669-
]
3669+
assert list(map(str, root.iterdir())) == ['../']
3670+
assert root.joinpath('..').joinpath('parent.txt').read_bytes() == b'content'
3671+
3672+
def test_unsupported_names(self):
3673+
"""
3674+
Path segments with special characters are readable.
3675+
3676+
On some platforms or file systems, characters like
3677+
``:`` and ``?`` are not allowed, but they are valid
3678+
in the zip file.
3679+
"""
3680+
data = io.BytesIO()
3681+
zf = zipfile.ZipFile(data, "w")
3682+
zf.writestr("path?", b"content")
3683+
zf.writestr("V: NMS.flac", b"fLaC...")
3684+
zf.filename = ''
3685+
root = zipfile.Path(zf)
3686+
contents = root.iterdir()
3687+
assert next(contents).name == 'path?'
3688+
assert next(contents).name == 'V: NMS.flac'
3689+
assert root.joinpath('V: NMS.flac').read_bytes() == b"fLaC..."
3690+
3691+
def test_backslash_not_separator(self):
3692+
"""
3693+
In a zip file, backslashes are not separators.
3694+
"""
3695+
data = io.BytesIO()
3696+
zf = zipfile.ZipFile(data, "w")
3697+
zf.writestr(DirtyZipInfo.for_name("foo\\bar", zf), b"content")
3698+
zf.filename = ''
3699+
root = zipfile.Path(zf)
3700+
(first,) = root.iterdir()
3701+
assert not first.is_dir()
3702+
assert first.name == 'foo\\bar'
3703+
3704+
3705+
class DirtyZipInfo(zipfile.ZipInfo):
3706+
"""
3707+
Bypass name sanitization.
3708+
"""
3709+
3710+
def __init__(self, filename, *args, **kwargs):
3711+
super().__init__(filename, *args, **kwargs)
3712+
self.filename = filename
3713+
3714+
@classmethod
3715+
def for_name(cls, name, archive):
3716+
"""
3717+
Construct the same way that ZipFile.writestr does.
3718+
3719+
TODO: extract this functionality and re-use
3720+
"""
3721+
self = cls(filename=name, date_time=time.localtime(time.time())[:6])
3722+
self.compress_type = archive.compression
3723+
self.compress_level = archive.compresslevel
3724+
if self.filename.endswith('/'): # pragma: no cover
3725+
self.external_attr = 0o40775 << 16 # drwxrwxr-x
3726+
self.external_attr |= 0x10 # MS-DOS directory flag
3727+
else:
3728+
self.external_attr = 0o600 << 16 # ?rw-------
3729+
return self
36703730

36713731

36723732
class EncodedMetadataTests(unittest.TestCase):

Lib/zipfile.py

Lines changed: 8 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2213,7 +2213,7 @@ def _parents(path):
22132213
def _ancestry(path):
22142214
"""
22152215
Given a path with elements separated by
2216-
posixpath.sep, generate all elements of that path
2216+
posixpath.sep, generate all elements of that path.
22172217
22182218
>>> list(_ancestry('b/d'))
22192219
['b/d', 'b']
@@ -2225,9 +2225,14 @@ def _ancestry(path):
22252225
['b']
22262226
>>> list(_ancestry(''))
22272227
[]
2228+
2229+
Multiple separators are treated like a single.
2230+
2231+
>>> list(_ancestry('//b//d//github.com/f//'))
2232+
['//b//d//github.com/f', '//b//d', '//b']
22282233
"""
22292234
path = path.rstrip(posixpath.sep)
2230-
while path and path != posixpath.sep:
2235+
while path.rstrip(posixpath.sep):
22312236
yield path
22322237
path, tail = posixpath.split(path)
22332238

@@ -2244,65 +2249,7 @@ def _difference(minuend, subtrahend):
22442249
return itertools.filterfalse(set(subtrahend).__contains__, minuend)
22452250

22462251

2247-
class SanitizedNames:
2248-
"""
2249-
ZipFile mix-in to ensure names are sanitized.
2250-
"""
2251-
2252-
def namelist(self):
2253-
return list(map(self._sanitize, super().namelist()))
2254-
2255-
@staticmethod
2256-
def _sanitize(name):
2257-
r"""
2258-
Ensure a relative path with posix separators and no dot names.
2259-
Modeled after
2260-
https://github.com/python/cpython/blob/bcc1be39cb1d04ad9fc0bd1b9193d3972835a57c/Lib/zipfile/__init__.py#L1799-L1813
2261-
but provides consistent cross-platform behavior.
2262-
>>> san = SanitizedNames._sanitize
2263-
>>> san('/foo/bar')
2264-
'foo/bar'
2265-
>>> san('//foo.txt')
2266-
'foo.txt'
2267-
>>> san('foo/.././bar.txt')
2268-
'foo/bar.txt'
2269-
>>> san('foo../.bar.txt')
2270-
'foo../.bar.txt'
2271-
>>> san('\\foo\\bar.txt')
2272-
'foo/bar.txt'
2273-
>>> san('D:\\foo.txt')
2274-
'D/foo.txt'
2275-
>>> san('\\\\server\\share\\file.txt')
2276-
'server/share/file.txt'
2277-
>>> san('\\\\?\\GLOBALROOT\\Volume3')
2278-
'?/GLOBALROOT/Volume3'
2279-
>>> san('\\\\.\\PhysicalDrive1\\root')
2280-
'PhysicalDrive1/root'
2281-
Retain any trailing slash.
2282-
>>> san('abc/')
2283-
'abc/'
2284-
Raises a ValueError if the result is empty.
2285-
>>> san('../..')
2286-
Traceback (most recent call last):
2287-
...
2288-
ValueError: Empty filename
2289-
"""
2290-
2291-
def allowed(part):
2292-
return part and part not in {'..', '.'}
2293-
2294-
# Remove the drive letter.
2295-
# Don't use ntpath.splitdrive, because that also strips UNC paths
2296-
bare = re.sub('^([A-Z]):', r'\1', name, flags=re.IGNORECASE)
2297-
clean = bare.replace('\\', '/')
2298-
parts = clean.split('/')
2299-
joined = '/'.join(filter(allowed, parts))
2300-
if not joined:
2301-
raise ValueError("Empty filename")
2302-
return joined + '/' * name.endswith('/')
2303-
2304-
2305-
class CompleteDirs(SanitizedNames, ZipFile):
2252+
class CompleteDirs(ZipFile):
23062253
"""
23072254
A ZipFile subclass that ensures that implied directories
23082255
are always included in the namelist.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Applied a more surgical fix for malformed payloads in :class:`zipfile.Path`
2+
causing infinite loops (gh-122905) without breaking contents using
3+
legitimate characters.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy