Skip to content

Commit d20e70b

Browse files
committed
Eliminated caller_file deps + writing to parents for security
1 parent 92f6bb6 commit d20e70b

10 files changed

Lines changed: 143 additions & 73 deletions

File tree

remove-json-keys/src/remove_json_keys/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .lib import data, init, log, wizard
55

66
def main():
7-
cli = init.cli(__file__)
7+
cli = init.cli()
88

99
if cli.config.init : init.config_file(cli) ; sys.exit(0)
1010
if not cli.config.no_wizard : wizard.run(cli)

remove-json-keys/src/remove_json_keys/assets/data/messages.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"prompt_INIT_CONFIG_HERE_ANYWAY": { "message": "Initialize config file here anyway" },
23
"prompt_KEYS_TO_REMOVE": { "message": "Enter key(s) to remove (comma-separated, or ENTER if done)" },
34
"prompt_NO_KEYS_EXIT": { "message": "No keys entered. Exit?" },
45
"log_OVERWRITING_CONFIG_AT": { "message": "Overwriting existing config at" },
@@ -7,14 +8,19 @@
78
"log_SEARCHING_FOR": { "message": "Searching for" },
89
"log_DIR_FOUND": { "message": "Directory found" },
910
"log_ADDED": { "message": "Added" },
11+
"log_SKIPPING": { "message": "Skipping" },
1012
"log_KEYS": { "message": "Keys" },
1113
"log_KEYS_TO_REMOVE": { "message": "Current keys to remove" },
1214
"log_NO_NEW_KEYS_ADDED": { "message": "No new keys added (all already present)" },
1315
"log_ALL_JSON_PROCESSED": { "message": "All JSON files processed" },
1416
"log_TOTAL_JSON_PROCESSED": { "message": "Total JSON files processed" },
1517
"tip_PASS_FORCE_TO_OVERWRITE": { "message": "Pass --force to overwrite" },
18+
"tip_MOVE_CONFIG_TO_ROOT": { "message": "Move config file to root of project you wish to use it in" },
1619
"warn_CONFIG_EXISTS_AT": { "message": "Config already exists at" },
1720
"warn_DIR_NOT_FOUND": { "message": "Unable to locate directory" },
21+
"warn_NO_PROJECT_ROOT_FOUND_IN": { "message": "No project root detected in" },
22+
"warn_SPECIFIED_CONFIG": { "message": "Specified config" },
23+
"warn_NOT_FOUND": { "message": "Not found" },
1824
"help_JSON_DIR": { "message": "Name of the folder containing JSON files (default: \"_locales\")" },
1925
"help_KEYS": { "message": "Keys to remove (e.g. \"app_NAME,author\")" },
2026
"help_INIT": { "message": "Create .remove-json.config.json5 file to store default options" },
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
".git",
3+
"manifest.json",
4+
"package.json",
5+
"package-lock.json",
6+
"pyproject.toml",
7+
"requirements.txt",
8+
"requirements-dev.txt",
9+
"setup.py"
10+
]

remove-json-keys/src/remove_json_keys/lib/init.py

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,62 @@
11
from pathlib import Path
2+
import sys
23

34
from . import data, language, log, settings
45

5-
def cli(caller_file):
6-
cli = data.sns.from_dict(data.json.read(Path(__file__).parent.parent / 'assets/data/package_data.json'))
6+
data_path = Path(__file__).parent.parent / 'assets/data'
7+
8+
def cli():
9+
cli = data.sns.from_dict(data.json.read(data_path / 'package_data.json'))
710
cli.msgs = language.get_msgs()
8-
settings.load(cli, caller_file)
11+
settings.load(cli)
912
return cli
1013

1114
def config_file(cli):
12-
config_path = Path(cli.config_filepath)
13-
if config_path.exists():
15+
target_path = Path.cwd() / f'.{cli.short_name}.config.json5'
16+
project_markers = data.json.read(data_path / 'project_markers.json')
17+
if not any((Path.cwd() / marker).exists() for marker in project_markers):
18+
log.warn(f'{cli.msgs.warn_NO_PROJECT_ROOT_FOUND_IN} {Path.cwd()}')
19+
user_resp = input(f'{cli.msgs.prompt_INIT_CONFIG_HERE_ANYWAY}? (y/N): ').strip().lower()
20+
if not user_resp.startswith('y') : return
21+
else : not_in_root = True # for move tip
22+
23+
# Handle existing file
24+
if target_path.exists():
1425
if cli.config.force:
15-
log.info(f'{cli.msgs.log_OVERWRITING_CONFIG_AT} {config_path}...')
26+
log.info(f'{cli.msgs.log_OVERWRITING_CONFIG_AT} {target_path}...')
1627
else:
17-
log.warn(f'{cli.msgs.warn_CONFIG_EXISTS_AT} {config_path}. {cli.msgs.log_SKIPPING} init.')
28+
log.warn(f'{cli.msgs.warn_CONFIG_EXISTS_AT} {target_path}. {cli.msgs.log_SKIPPING} init...')
1829
log.tip(f'{cli.msgs.tip_PASS_FORCE_TO_OVERWRITE}.')
1930
return
20-
cli.config_filename = f'.{cli.short_name}.config.json5'
21-
cli.config_filepath = str(Path(cli.project_root) / cli.config_filename)
22-
if not getattr(cli, 'default_file_config', None):
23-
cli.default_file_config = data.url.get(f'{cli.urls.jsdelivr}/{cli.name}/{cli.config_filename}')
24-
data.file.write(cli.config_filepath, cli.default_file_config)
25-
log.success(f'{cli.msgs.log_DEFAULT_CONFIG_CREATED_AT} {cli.config_filepath}')
31+
32+
# Fetch/write from jsDelivr
33+
if not getattr(cli, 'default_file_config', ''):
34+
cli.default_file_config = data.url.get(f'{cli.urls.jsdelivr}/{cli.name}/{target_path.name}')
35+
data.file.write(str(target_path), cli.default_file_config)
36+
log.success(f'{cli.msgs.log_DEFAULT_CONFIG_CREATED_AT} {target_path}')
37+
if not_in_root : log.tip(f'{cli.msgs.tip_MOVE_CONFIG_TO_ROOT}.')
38+
39+
def config_filepath(cli): # for settings.load()
40+
41+
# Check --config <path>
42+
for idx, arg in enumerate(sys.argv):
43+
if arg == '--config' and idx +1 < len(sys.argv):
44+
cli.config_filepath = Path(sys.argv[idx + 1]).resolve()
45+
if cli.config_filepath.exists(): return
46+
else : log.warn(f'{cli.msgs.warn_SPECIFIED_CONFIG} {cli.config_filepath} {cli.msgs.warn_NOT_FOUND.lower()}')
47+
48+
# Search upwards
49+
possible_config_filenames = [
50+
f'{prefix}{name}.config.json{suffix}'
51+
for prefix in ['.', ''] for name in [cli.short_name, cli.name] for suffix in ['5', '', 'c']
52+
]
53+
current_dir = Path.cwd().resolve()
54+
for parent in [current_dir, *current_dir.parents]:
55+
for filename in possible_config_filenames:
56+
possible_config_file = parent / filename
57+
if possible_config_file.exists():
58+
cli.config_filepath = possible_config_file
59+
return
2660

2761
def json_dir(cli):
2862
for path in Path.cwd().rglob(cli.config.json_dir):

remove-json-keys/src/remove_json_keys/lib/settings.py

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import argparse, sys
2-
from pathlib import Path
32
from types import SimpleNamespace as sn
43

5-
from . import data, log
4+
from . import data, init, log
65

76
controls = sn(
87
json_dir=sn(
@@ -21,7 +20,7 @@
2120
args=['--debug'], nargs='?', const=True)
2221
)
2322

24-
def load(cli, caller_file):
23+
def load(cli):
2524

2625
# Init debug target
2726
if '--debug' in sys.argv:
@@ -35,32 +34,19 @@ def load(cli, caller_file):
3534

3635
# Load from config file
3736
cli.config = sn()
38-
caller_path = Path(caller_file)
39-
cli.project_root = str(caller_path.parent.parent.parent if 'src' in str(caller_path)
40-
else caller_path.parent.parent)
41-
possible_config_filenames = [
42-
f'{prefix}{name}.config.json{suffix}'
43-
for prefix in ['.', ''] for name in [cli.short_name, cli.name]
44-
for suffix in ['5', '', 'c']
45-
]
46-
for filename in possible_config_filenames:
47-
config_path = Path(cli.project_root) / filename
48-
if config_path.exists():
49-
cli.config_filepath = str(config_path)
50-
cli.config = data.sns.from_dict(data.json.read(cli.config_filepath))
51-
cli.config_filename = filename
52-
break
53-
if hasattr(cli, 'config_filename'):
37+
init.config_filepath(cli)
38+
if getattr(cli, 'config_filepath', None):
39+
cli.config = data.sns.from_dict(data.json.read(cli.config_filepath))
5440
log.debug('Config file loaded!\n{}', cli)
5541
else:
5642
log.debug('No config file found.')
5743

5844
# Parse CLI args
5945
argp = argparse.ArgumentParser(description=cli.description, add_help=False)
60-
for attr_name in vars(controls):
46+
for attr_name in vars(controls): # add args to argp
6147
kwargs = getattr(controls, attr_name).__dict__.copy()
62-
args = kwargs.pop('args') # separate positional flags
63-
for custom_attr in ('default_val', 'parser', 'subcmd'): # remove custom attrs from kwargs
48+
args = kwargs.pop('args')
49+
for custom_attr in ('default_val', 'parser', 'subcmd'):
6450
kwargs.pop(custom_attr, None)
6551
argp.add_argument(*args, **kwargs)
6652
parsed_args, unknown = argp.parse_known_args()

translate-messages/src/translate_messages/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from .lib import init, language, log, wizard
55

66
def main():
7-
cli = init.cli(__file__)
7+
cli = init.cli()
88

99
if cli.config.init : init.config_file(cli) ; sys.exit(0)
1010
if not cli.config.no_wizard : wizard.run(cli)

translate-messages/src/translate_messages/assets/data/messages.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
{
2+
"prompt_INIT_CONFIG_HERE_ANYWAY": { "message": "Initialize config file here anyway" },
23
"prompt_KEYS_TO_IGNORE": { "message": "Enter key(s) to ignore (comma-separated, or ENTER if done)" },
34
"log_OVERWRITING_CONFIG_AT": { "message": "Overwriting existing config at" },
45
"log_DEFAULT_CONFIG_CREATED_AT": { "message": "Default config created at" },
@@ -15,11 +16,15 @@
1516
"log_NO_NEW_KEYS_ADDED": { "message": "No new keys added (all already present)" },
1617
"log_ALL_JSON_UPDATED": { "message": "All JSON files updated successfully" },
1718
"tip_PASS_FORCE_TO_OVERWRITE": { "message": "Pass --force to overwrite" },
19+
"tip_MOVE_CONFIG_TO_ROOT": { "message": "Move config file to root of project you wish to use it in" },
1820
"tip_MAKE_SURE": { "message": "Make sure" },
1921
"tip_EXISTS": { "message": "exists" },
2022
"tip_IT_HAS_VALID_JSON": { "message": "it contains valid JSON" },
2123
"warn_CONFIG_EXISTS_AT": { "message": "Config already exists at" },
2224
"warn_DIR_NOT_FOUND": { "message": "Unable to locate directory" },
25+
"warn_NO_PROJECT_ROOT_FOUND_IN": { "message": "No project root detected in" },
26+
"warn_SPECIFIED_CONFIG": { "message": "Specified config" },
27+
"warn_NOT_FOUND": { "message": "Not found" },
2328
"err_PARSE_FAILED": { "message": "Failed to parse" },
2429
"err_TRANSLATE_FAILED_FOR_KEY": { "message": "Translation failed for key" },
2530
"err_EN_LOC_NOT_FOUND_AT": { "message": "English locale not found at" },
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[
2+
".git",
3+
"manifest.json",
4+
"package.json",
5+
"package-lock.json",
6+
"pyproject.toml",
7+
"requirements.txt",
8+
"requirements-dev.txt",
9+
"setup.py"
10+
]

translate-messages/src/translate_messages/lib/init.py

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,67 @@
33

44
from . import data, language, log, settings
55

6-
def cli(caller_file):
7-
cli = data.sns.from_dict(data.json.read(Path(__file__).parent.parent / 'assets/data/package_data.json'))
6+
data_path = Path(__file__).parent.parent / 'assets/data'
7+
8+
def cli():
9+
cli = data.sns.from_dict(data.json.read(data_path / 'package_data.json'))
810
cli.msgs = language.get_msgs()
9-
settings.load(cli, caller_file)
11+
settings.load(cli)
1012
return cli
1113

1214
def config_file(cli):
13-
config_path = Path(cli.config_filepath)
14-
if config_path.exists():
15+
target_path = Path.cwd() / f'.{cli.short_name}.config.json5'
16+
project_markers = data.json.read(data_path / 'project_markers.json')
17+
if not any((Path.cwd() / marker).exists() for marker in project_markers):
18+
log.warn(f'{cli.msgs.warn_NO_PROJECT_ROOT_FOUND_IN} {Path.cwd()}')
19+
user_resp = input(f'{cli.msgs.prompt_INIT_CONFIG_HERE_ANYWAY}? (y/N): ').strip().lower()
20+
if not user_resp.startswith('y') : return
21+
else : not_in_root = True # for move tip
22+
23+
# Handle existing file
24+
if target_path.exists():
1525
if cli.config.force:
16-
log.info(f'{cli.msgs.log_OVERWRITING_CONFIG_AT} {config_path}...')
26+
log.info(f'{cli.msgs.log_OVERWRITING_CONFIG_AT} {target_path}...')
1727
else:
18-
log.warn(f'{cli.msgs.warn_CONFIG_EXISTS_AT} {config_path}. {cli.msgs.log_SKIPPING} init.')
28+
log.warn(f'{cli.msgs.warn_CONFIG_EXISTS_AT} {target_path}. {cli.msgs.log_SKIPPING} init...')
1929
log.tip(f'{cli.msgs.tip_PASS_FORCE_TO_OVERWRITE}.')
2030
return
21-
cli.config_filename = f'.{cli.short_name}.config.json5'
22-
cli.config_filepath = str(Path(cli.project_root) / cli.config_filename)
23-
if not getattr(cli, 'default_file_config', None):
24-
cli.default_file_config = data.url.get(f'{cli.urls.jsdelivr}/{cli.name}/{cli.config_filename}')
25-
data.file.write(cli.config_filepath, cli.default_file_config)
26-
log.success(f'{cli.msgs.log_DEFAULT_CONFIG_CREATED_AT} {cli.config_filepath}')
31+
32+
# Fetch/write from jsDelivr
33+
if not getattr(cli, 'default_file_config', ''):
34+
cli.default_file_config = data.url.get(f'{cli.urls.jsdelivr}/{cli.name}/{target_path.name}')
35+
data.file.write(str(target_path), cli.default_file_config)
36+
log.success(f'{cli.msgs.log_DEFAULT_CONFIG_CREATED_AT} {target_path}')
37+
if not_in_root : log.tip(f'{cli.msgs.tip_MOVE_CONFIG_TO_ROOT}.')
38+
39+
def config_filepath(cli): # for settings.load()
40+
41+
# Check --config <path>
42+
for idx, arg in enumerate(sys.argv):
43+
if arg == '--config' and idx +1 < len(sys.argv):
44+
cli.config_filepath = Path(sys.argv[idx + 1]).resolve()
45+
if cli.config_filepath.exists(): return
46+
else : log.warn(f'{cli.msgs.warn_SPECIFIED_CONFIG} {cli.config_filepath} {cli.msgs.warn_NOT_FOUND.lower()}')
47+
48+
# Search upwards
49+
possible_config_filenames = [
50+
f'{prefix}{name}.config.json{suffix}'
51+
for prefix in ['.', ''] for name in [cli.short_name, cli.name] for suffix in ['5', '', 'c']
52+
]
53+
current_dir = Path.cwd().resolve()
54+
for parent in [current_dir, *current_dir.parents]:
55+
for filename in possible_config_filenames:
56+
possible_config_file = parent / filename
57+
if possible_config_file.exists():
58+
cli.config_filepath = possible_config_file
59+
return
2760

2861
def locales_dir(cli):
2962
for path in Path.cwd().rglob(cli.config.locales_dir):
3063
if path.is_dir():
3164
cli.config.locales_dir = str(path)
3265
return
33-
cli.config.locales_dir = None
66+
cli.config.locales_dir = ''
3467

3568
def src_msgs(cli):
3669
cli.msgs_filename = 'messages.json'

translate-messages/src/translate_messages/lib/settings.py

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import argparse, sys
2-
from pathlib import Path
32
from types import SimpleNamespace as sn
43

5-
from . import data, log
4+
from . import data, init, log
65

76
controls = sn(
87
locales_dir=sn(
@@ -31,7 +30,7 @@
3130
args=['--debug'], nargs='?', const=True)
3231
)
3332

34-
def load(cli, caller_file):
33+
def load(cli):
3534

3635
# Init debug target
3736
if '--debug' in sys.argv:
@@ -45,32 +44,19 @@ def load(cli, caller_file):
4544

4645
# Load from config file
4746
cli.config = sn()
48-
caller_path = Path(caller_file)
49-
cli.project_root = str(caller_path.parent.parent.parent if 'src' in str(caller_path)
50-
else caller_path.parent.parent)
51-
possible_config_filenames = [
52-
f'{prefix}{name}.config.json{suffix}'
53-
for prefix in ['.', ''] for name in [cli.short_name, cli.name]
54-
for suffix in ['5', '', 'c']
55-
]
56-
for filename in possible_config_filenames:
57-
config_path = Path(cli.project_root) / filename
58-
if config_path.exists():
59-
cli.config_filepath = str(config_path)
60-
cli.config = data.sns.from_dict(data.json.read(cli.config_filepath))
61-
cli.config_filename = filename
62-
break
63-
if hasattr(cli, 'config_filename'):
47+
init.config_filepath(cli)
48+
if getattr(cli, 'config_filepath', None):
49+
cli.config = data.sns.from_dict(data.json.read(cli.config_filepath))
6450
log.debug('Config file loaded!\n{}', cli)
6551
else:
6652
log.debug('No config file found.')
6753

6854
# Parse CLI args
6955
argp = argparse.ArgumentParser(description=cli.description, add_help=False)
70-
for attr_name in vars(controls):
56+
for attr_name in vars(controls): # add args to argp
7157
kwargs = getattr(controls, attr_name).__dict__.copy()
72-
args = kwargs.pop('args') # separate positional flags
73-
for custom_attr in ('default_val', 'parser', 'subcmd'): # remove custom attrs from kwargs
58+
args = kwargs.pop('args')
59+
for custom_attr in ('default_val', 'parser', 'subcmd'):
7460
kwargs.pop(custom_attr, None)
7561
argp.add_argument(*args, **kwargs)
7662
parsed_args, unknown = argp.parse_known_args()

0 commit comments

Comments
 (0)