From 6cbe18ebbd73b7bafd8209ee732d1d99059b61a6 Mon Sep 17 00:00:00 2001 From: Erik Montnemery Date: Thu, 16 Jan 2025 13:26:52 +0100 Subject: [PATCH] Bump securetar to 2025.1.3 (#135762) * Bump securetar to 2025.1.3 * Remove outdated fixture --- homeassistant/components/backup/manifest.json | 2 +- homeassistant/components/backup/util.py | 20 ++++++------------ homeassistant/package_constraints.txt | 2 +- pyproject.toml | 2 +- requirements.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../{ed1608a9.tar => c0cb53bd.tar} | Bin 10240 -> 10240 bytes .../backup/snapshots/test_websocket.ambr | 6 +++--- tests/components/backup/test_http.py | 10 ++++----- tests/components/backup/test_websocket.py | 6 +++--- 11 files changed, 24 insertions(+), 30 deletions(-) rename tests/components/backup/fixtures/test_backups/{ed1608a9.tar => c0cb53bd.tar} (66%) diff --git a/homeassistant/components/backup/manifest.json b/homeassistant/components/backup/manifest.json index b1b6e6f70c6..ffaed260c88 100644 --- a/homeassistant/components/backup/manifest.json +++ b/homeassistant/components/backup/manifest.json @@ -8,5 +8,5 @@ "integration_type": "system", "iot_class": "calculated", "quality_scale": "internal", - "requirements": ["cronsim==2.6", "securetar==2025.1.2"] + "requirements": ["cronsim==2.6", "securetar==2025.1.3"] } diff --git a/homeassistant/components/backup/util.py b/homeassistant/components/backup/util.py index 55f3c3c05c7..ac1525b7d69 100644 --- a/homeassistant/components/backup/util.py +++ b/homeassistant/components/backup/util.py @@ -13,13 +13,7 @@ import tarfile from typing import IO, Self, cast import aiohttp -from securetar import ( - PLAINTEXT_SIZE_HEADER, - VERSION_HEADER, - SecureTarError, - SecureTarFile, - SecureTarReadError, -) +from securetar import SecureTarError, SecureTarFile, SecureTarReadError from homeassistant.backup_restore import password_to_key from homeassistant.core import HomeAssistant @@ -205,8 +199,6 @@ def validate_password_stream( for obj in input_tar: if not obj.name.endswith((".tar", ".tgz", ".tar.gz")): continue - if obj.pax_headers.get(VERSION_HEADER) != "2.0": - raise UnsupportedSecureTarVersion istf = SecureTarFile( None, # Not used gzip=False, @@ -215,6 +207,8 @@ def validate_password_stream( fileobj=input_tar.extractfile(obj), ) with istf.decrypt(obj) as decrypted: + if istf.securetar_header.plaintext_size is None: + raise UnsupportedSecureTarVersion try: decrypted.read(1) # Read a single byte to trigger the decryption except SecureTarReadError as err: @@ -270,10 +264,6 @@ def _decrypt_backup( if not obj.name.endswith((".tar", ".tgz", ".tar.gz")): output_tar.addfile(obj, input_tar.extractfile(obj)) continue - if obj.pax_headers.get(VERSION_HEADER) != "2.0": - raise UnsupportedSecureTarVersion - decrypted_obj = copy.deepcopy(obj) - decrypted_obj.size = int(obj.pax_headers[PLAINTEXT_SIZE_HEADER]) istf = SecureTarFile( None, # Not used gzip=False, @@ -282,6 +272,10 @@ def _decrypt_backup( fileobj=input_tar.extractfile(obj), ) with istf.decrypt(obj) as decrypted: + if (plaintext_size := istf.securetar_header.plaintext_size) is None: + raise UnsupportedSecureTarVersion + decrypted_obj = copy.deepcopy(obj) + decrypted_obj.size = plaintext_size output_tar.addfile(decrypted_obj, decrypted) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 6b994679780..6f7fe970cc9 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -60,7 +60,7 @@ PyTurboJPEG==1.7.5 pyudev==0.24.1 PyYAML==6.0.2 requests==2.32.3 -securetar==2025.1.2 +securetar==2025.1.3 SQLAlchemy==2.0.36 standard-aifc==3.13.0;python_version>='3.13' standard-telnetlib==3.13.0;python_version>='3.13' diff --git a/pyproject.toml b/pyproject.toml index 0bb5ad7ea8d..406fbe6cf25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,7 +66,7 @@ dependencies = [ "python-slugify==8.0.4", "PyYAML==6.0.2", "requests==2.32.3", - "securetar==2025.1.2", + "securetar==2025.1.3", "SQLAlchemy==2.0.36", "standard-aifc==3.13.0;python_version>='3.13'", "standard-telnetlib==3.13.0;python_version>='3.13'", diff --git a/requirements.txt b/requirements.txt index 7f12eb14274..52e1b412803 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,7 +38,7 @@ psutil-home-assistant==0.0.1 python-slugify==8.0.4 PyYAML==6.0.2 requests==2.32.3 -securetar==2025.1.2 +securetar==2025.1.3 SQLAlchemy==2.0.36 standard-aifc==3.13.0;python_version>='3.13' standard-telnetlib==3.13.0;python_version>='3.13' diff --git a/requirements_all.txt b/requirements_all.txt index 78525134731..d6d9b583d94 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2665,7 +2665,7 @@ screenlogicpy==0.10.0 scsgate==0.1.0 # homeassistant.components.backup -securetar==2025.1.2 +securetar==2025.1.3 # homeassistant.components.sendgrid sendgrid==6.8.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b2413d5ed95..4a8dfe35756 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2147,7 +2147,7 @@ sanix==1.0.6 screenlogicpy==0.10.0 # homeassistant.components.backup -securetar==2025.1.2 +securetar==2025.1.3 # homeassistant.components.emulated_kasa # homeassistant.components.sense diff --git a/tests/components/backup/fixtures/test_backups/ed1608a9.tar b/tests/components/backup/fixtures/test_backups/c0cb53bd.tar similarity index 66% rename from tests/components/backup/fixtures/test_backups/ed1608a9.tar rename to tests/components/backup/fixtures/test_backups/c0cb53bd.tar index fc928b16d1b1c3c480ea3600ef8f69d7f87dbc4c..f3b2845d5eb19b9708ae6d4fd68f7d220fb39c45 100644 GIT binary patch delta 1801 zcmbW%X;{(+90zdFN^>&hC=b+7Qz)-L{6Vg=bVxZ@GL>d|ix(nZmynu}60&01w) zYSE_4n36|cv2vMO%{)q7N**OEdvu$cN3K23uIE`VwtauEes8|7KF>FUmBG61psmXR zIAJ6(EWDbe?FBUfKq3VAD-Zw=5by*DCan+-4KIX3SIh_$3_qF6kJySQgJb|tAy7a^ z3P1%wGC{BU7J3X?pnJra2te>eGLe8MlAKBJ8u9NAfM9?>TEspPoc&1=zM)B4kiP|5 z`p45m)Wo;b*P!l;W@NM|63cxv>3e06ck*T?+p_$aLylXnC32l}9m!rZ z`g32SLBGTs43?OTa{A$hx1I|o_0QQQ@gKV!6ZLK1Z;w9qo4cv}JXY4M}@Ckos!B;M25XDJmw z$n^AxV`gCDrI{aUZI-5n++I}--%szzF_f9@Pi{P7uI@&{2~4`4u6`-8p#&Nyl4fkt_o>8q?2 zYDNjJmX1&j55#xceN#q#KA(wG>trc;a+^rn8C0*u+N(^Q*ppIl~pfPy5tLVCS{n}S`75nx({L#-0==X3L~Qs4uV6q<5Vim(g; zx$jRU1|7ep3%aj~A#p7j$gzjQSfO=w6n;HD@8CqO6up-UX&%)oHrw^p30V5298*I0 zF6&-KYO$9LFx+Kgnny!hxHshMF?~lUhCshV~`XW*Ob*JR*L!dojN0XOJ@4n9Y+Lg;G%o^0qi%^xXEDZ3_RMSDs5O>Qy+Hy3q|Hkf+g>BlvDXd)gPm z)@X6KKSdkmHsb41v@EgyRCl=E*ZzS*2$SYrxdKX6CnMX9rcU|0r|B6BoK~Zwm#{^|tazBAL3=0Op=3 z7#q_2Vzn2tXUBYa>-~mf=U>+i!~*8w|x%41>Qot-Pe7t_|}T1-nz zu_fKH-z}*E#hxyPLglCxPq^ddB`tXH;WGQ9KmBrVt#(~;QM0S8QL0)JdRqp$P5fX6 zxgy>@ePQr?-S;_erAz64V*1f%s=F(FG}mjIe5CR^1@>0XJt+? zy|M0hQerQ<>5)~nIw=+kqtj1T*H;#F8&JI~*jXrqIKiG1aPc~-u);%qVYL_+(w?kY zOxJO+(nh!Pr3=GAfbnRNO`-B-(MJ?UQ$Oj^QQH%2qh+i9kY;$mr9;>EiRu)?Un2TX zd~+WDy$@C$#;=)8gQ4Q)2q`{?qhALW-UolhIq zJ{iGbR(EkR3=Z_BIk@Pw--YX30ZuLy;HQP=y$^@>PCkr%bM=%_fFj3-4*TWc<90?9 z8i9Sx&AWH(fXfq!%hiv^Tl7!c#;oZ-;20e@j^| delta 1886 zcmc)ITR7VX9|!QHhBSi2xfG$SRTcRsBB(<&o09g`RMnw%O!7yZiFy@nX-^r1PL=T)fS1-*=UJvrFE#xJF9v!>Qr4Xx@W!E{eBnUtKai{zZai8Y96)CK@A2n z0B5XAw5x=mb_%)=z!4$XzYl^1usDDKL1&espo~%|^i41xkB(%4^b6@=MszwRH8Gl* zoB?KKM6#IKpobIS0>?SO|Bwl$vzRH#9ykmZj&ngLWiXRK4}gewcEu9`X9C6v`!DCRY;88gh!b zW&iQDo3kb)_W$bbsk)1bxO1f<8VTx)w0bKAtYh9Wt_DRu%GlO32lwm5-YjN>XhOOx z_>?L05l&>^2_^gKMfJI}ZS1CFe4JbflgHu%M=$Fw;vVaD>OW8B!^g#f!C9L+4ZrM( z*+@1k{+^W6+h{uz*ZvdmqVAdK1WxyBZ?AB9t54>&8k=uWI|Z@Sf*|#Usd9u_Y(Gn1 z*~-4l*l8L1f0}S`%tVi79@cRB!BXu&?-n9cQ5sf&bR(Z=+3H=s?3M6p&p>FW_~U2v z=CWA}ktC`>4EU6a6v+EomY&B?hRpmCbdvqt(0;Zf!$#Tff2;*%!g}nPH~7SBDp>0a zntc`aPEqjOBx1bNF$-Dz$+yYIx{4#T#x#iL@|p@kL3JusEV?;=w^U-tvnZZqS$J~q zE)bDgTZ@_svdkvlbPsyojW^5_PeFBHy45-Q)E_@R@Q@mz2yveDHx6#T#`}m&wvN!d zaYiqf6ttEX1qpOf*#(={j-ruE10+&gT@f5HY3@b6<2ead zZ@%a*V#8VPa7ZLp^9607BComkrm_K zFwy?ilI`JxCZ;W2k%zrexi0gkbG|*BBMe`4loQMaXIEz&c=tO_-JUjV=3$O@(?+Y@ zv&EGm$2B`OsN#()7Dwq%I~%zpCyrBKZg!zId}2V~ro8V^&Hj>KuFj?fx<8xHD4`UQ z9hU7eX3}+I=#J$^5F60{E#aY*%6I$&Ewg_L3tPR`N;~{}Zny9}avL6z7a+J%zhYjaUAUMCnX7bP2oZiP4RULM(dCG}eGOxLRr8!yQ_vp3^ znX>LXL}AL!f-{T3uG7%Z&{Lnn12ROX0(YO}MWn6UR&>?Q@9YkB@6T>Te5{$2s?o?v ztv9>aLmxcpAXILumd|4f9~I7DKk(mPnD0!S@LYQIdXGZ4&gc@U%Rt@WWkGuYpB`WN z`Eye?QGojHd&7)u<%Kqzi(a+rXL3Ig1kHR|Tem6ME7kIivv*xzZ?ps|I;;XTxt~K; z+jEu+hljPd9&Y>N>KbP_qydv9F*mGTCIQ305{c-vExvi9xMzmNLuy%X;OpMeO>*xSQe$WcYB&nA*a;e`e^KdDHHL9`vlz z5q;FS$@II-s#u9p0`H3kgO*UQC;5Xhw?6cs$EDSGB%^Znz-6h=Ednzz!gE6hUy{$I z^v!qeyt?GhFoH8SF9w1TO3*%Lz4B08?V9si+nPCm(@XuVqvHpo*$3lIA3lCvN-poY a>o+p#lvpSvEO}4Sn&ED None: """Test downloading a local backup file.""" - backup_path = get_fixture_path("test_backups/ed1608a9.tar", DOMAIN) + backup_path = get_fixture_path("test_backups/c0cb53bd.tar", DOMAIN) await setup_backup_integration(hass) hass.data[DATA_MANAGER].backup_agents["domain.test"] = BackupAgentTest( "test", [ AgentBackup( addons=[AddonInfo(name="Test", slug="test", version="1.0.0")], - backup_id="ed1608a9", + backup_id="c0cb53bd", database_included=True, date="1970-01-01T00:00:00Z", extra_metadata={}, @@ -141,7 +141,7 @@ async def _test_downloading_encrypted_backup( """Test downloading an encrypted backup file.""" # Try downloading without supplying a password client = await hass_client() - resp = await client.get(f"/api/backup/download/ed1608a9?agent_id={agent_id}") + resp = await client.get(f"/api/backup/download/c0cb53bd?agent_id={agent_id}") assert resp.status == 200 backup = await resp.read() # We expect a valid outer tar file, but the inner tar file is encrypted and @@ -158,7 +158,7 @@ async def _test_downloading_encrypted_backup( # Download with the wrong password resp = await client.get( - f"/api/backup/download/ed1608a9?agent_id={agent_id}&password=wrong" + f"/api/backup/download/c0cb53bd?agent_id={agent_id}&password=wrong" ) assert resp.status == 200 backup = await resp.read() @@ -171,7 +171,7 @@ async def _test_downloading_encrypted_backup( # Finally download with the correct password resp = await client.get( - f"/api/backup/download/ed1608a9?agent_id={agent_id}&password=hunter2" + f"/api/backup/download/c0cb53bd?agent_id={agent_id}&password=hunter2" ) assert resp.status == 200 backup = await resp.read() diff --git a/tests/components/backup/test_websocket.py b/tests/components/backup/test_websocket.py index 2aa6eca3b95..fe6c0c1f679 100644 --- a/tests/components/backup/test_websocket.py +++ b/tests/components/backup/test_websocket.py @@ -2560,13 +2560,13 @@ async def test_subscribe_event( ("agent_id", "backup_id", "password"), [ # Invalid agent or backup - ("no_such_agent", "ed1608a9", "hunter2"), + ("no_such_agent", "c0cb53bd", "hunter2"), ("backup.local", "no_such_backup", "hunter2"), # Legacy backup, which can't be streamed ("backup.local", "2bcb3113", "hunter2"), # New backup, which can be streamed, try with correct and wrong password - ("backup.local", "ed1608a9", "hunter2"), - ("backup.local", "ed1608a9", "wrong_password"), + ("backup.local", "c0cb53bd", "hunter2"), + ("backup.local", "c0cb53bd", "wrong_password"), ], ) @pytest.mark.usefixtures("mock_backups")