Skip to content

Commit d71415a

Browse files
committed
enforce restricting secret file permissions to user read/write
1 parent 9783607 commit d71415a

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

planet/auth.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@
1919
import logging
2020
import os
2121
import pathlib
22+
import stat
2223
import typing
24+
from typing import Optional
2325

2426
import httpx
2527
import jwt
2628

2729
from . import http
2830
from .constants import ENV_API_KEY, PLANET_BASE_URL, SECRET_FILE_PATH
2931
from .exceptions import AuthException
30-
from typing import Optional
3132

3233
LOGGER = logging.getLogger(__name__)
3334

@@ -226,8 +227,15 @@ def value(self):
226227

227228
class _SecretFile:
228229

229-
def __init__(self, path):
230-
self.path = path
230+
def __init__(self, path: typing.Union[str, pathlib.Path]):
231+
self.path = pathlib.Path(path)
232+
233+
self.permissions = stat.S_IRUSR | stat.S_IWUSR # user rw
234+
235+
# in sdk versions <=2.0.0, secret file was created with the wrong
236+
# permissions, fix this automatically as well as catching the unlikely
237+
# cases where the permissions get changed externally
238+
self._enforce_permissions()
231239

232240
def write(self, contents: dict):
233241
try:
@@ -240,11 +248,29 @@ def write(self, contents: dict):
240248

241249
def _write(self, contents: dict):
242250
LOGGER.debug(f'Writing to {self.path}')
243-
with open(self.path, 'w') as fp:
251+
252+
def opener(path, flags):
253+
return os.open(path, flags, self.permissions)
254+
255+
with open(self.path, 'w', opener=opener) as fp:
244256
fp.write(json.dumps(contents))
245257

246258
def read(self) -> dict:
247259
LOGGER.debug(f'Reading from {self.path}')
248260
with open(self.path, 'r') as fp:
249261
contents = json.loads(fp.read())
250262
return contents
263+
264+
def _enforce_permissions(self):
265+
'''if the file's permissions are not what they should be, fix them'''
266+
try:
267+
# in octal, permissions is the last three bits of the mode
268+
file_permissions = self.path.stat().st_mode & 0o777
269+
if file_permissions != self.permissions:
270+
LOGGER.debug(
271+
f'{self.path} permissions are {oct(file_permissions)}, '
272+
f'should be {oct(self.permissions)}. Fixing.')
273+
self.path.chmod(self.permissions)
274+
except FileNotFoundError:
275+
# just skip it if the secret file doesn't exist
276+
pass

tests/unit/test_auth.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def test_secretfile_read():
3131

3232
@pytest.fixture
3333
def secret_path(monkeypatch, tmp_path):
34-
secret_path = str(tmp_path / '.test')
34+
secret_path = tmp_path / '.test'
3535
monkeypatch.setattr(auth, 'SECRET_FILE_PATH', secret_path)
3636
yield secret_path
3737

@@ -138,3 +138,19 @@ def test_Auth_store_exists(tmp_path):
138138

139139
with open(secret_path, 'r') as fp:
140140
assert json.loads(fp.read()) == {"key": "test", "existing": "exists"}
141+
142+
143+
def test__SecretFile_permissions_doesnotexist(secret_path):
144+
'''No exception is raised if the file doesn't exist'''
145+
auth._SecretFile(secret_path)
146+
147+
148+
def test__SecretFile_permissions_incorrect(secret_path):
149+
'''Incorrect permissions are fixed'''
150+
with open(secret_path, 'w') as fp:
151+
fp.write('{"existing": "exists"}')
152+
153+
secret_path.chmod(0o666)
154+
155+
auth._SecretFile(secret_path)
156+
assert secret_path.stat().st_mode & 0o777 == 0o600

0 commit comments

Comments
 (0)