Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion keepercommander/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1530,7 +1530,7 @@ def login_and_get_mc_params_login_v3(params: KeeperParams, mc_id):
mc_params.rsa_key = params.rsa_key
mc_params.rsa_key2 = params.rsa_key2
mc_params.ecc_key = params.ecc_key
mc_params.forbid_rsa = params.forbid_rsa
mc_params.forbid_rsa = resp.forbidKeyType2

mc_params.session_token = loginv3.CommonHelperMethods.bytes_to_url_safe_str(resp.encryptedSessionToken)
mc_params.msp_tree_key = params.enterprise['unencrypted_tree_key']
Expand Down
2 changes: 1 addition & 1 deletion keepercommander/command_categories.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@
'Service Mode REST API': {
'service-create', 'service-add-config', 'service-start', 'service-stop', 'service-status',
'service-config-add', 'service-docker-setup', 'slack-app-setup', 'teams-app-setup',
'sailpoint-app-setup'
'sailpoint-app-setup', 'gchat-app-setup'
},

# Email Configuration Commands
Expand Down
3 changes: 2 additions & 1 deletion keepercommander/commands/aram.py
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,8 @@ def __init__(self):

def get_sox_data(self, params):
if not self.sox_data and is_compliance_reporting_enabled(params):
self.sox_data = get_compliance_data(params, 0, 0,False, min_updated=0, no_cache=False)
min_updated = int(datetime.datetime.now().timestamp()) if self.allow_sox_data_fetch else 0
self.sox_data = get_compliance_data(params, 0, 0, False, min_updated=min_updated, no_cache=False)
return self.sox_data

def get_value(self, params, field, event):
Expand Down
3 changes: 3 additions & 0 deletions keepercommander/commands/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ def register_enterprise_commands(commands, aliases, command_info):
from . import enterprise_reports
enterprise_reports.register_commands(commands)
enterprise_reports.register_command_info(aliases, command_info)
from . import cspm
cspm.register_commands(commands)
cspm.register_command_info(aliases, command_info)
from .risk_management import RiskManagementReportCommand
commands['risk-management'] = RiskManagementReportCommand()
command_info['risk-management'] = 'Risk Management Reports'
Expand Down
15 changes: 6 additions & 9 deletions keepercommander/commands/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,15 +194,12 @@ def get_parameter_value(params, record, parameter, temp_files, **kwargs):
@staticmethod
def get_command_string(params, record, template, temp_files, **kwargs):
# type: (KeeperParams, KeeperRecord, str, list, ...) -> str or None
command = template
while True:
m = endpoint_parameter_pattern.search(command)
if not m:
break
p = m.group(1)
pv = BaseConnectCommand.get_parameter_value(params, record, p, temp_files, **kwargs)
command = command[:m.start()] + (pv or '') + command[m.end():]
return command
# Single-pass substitution: re.sub never re-scans replacement text, so
# a field value containing ${…} cannot trigger a second expansion round.
def _replace(m):
pv = BaseConnectCommand.get_parameter_value(params, record, m.group(1), temp_files, **kwargs)
return pv or ''
return endpoint_parameter_pattern.sub(_replace, template)


class ConnectSshCommand(BaseConnectCommand):
Expand Down
36 changes: 36 additions & 0 deletions keepercommander/commands/connect_prompts.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@

_DANGEROUS_ENV_PREFIXES = ('LD_', 'DYLD_')

# SSH -o options that cause local code execution via the SSH client itself.
_DANGEROUS_SSH_OPTIONS = frozenset({
'proxycommand',
'localcommand',
'permitlocalcommand',
})

_INTERPRETER_DASH_C_FLAGS = (
'-c', '-lc', '-ic', '-Command', '-EncodedCommand',
'-e', '-E', '/c', '/C',
Expand Down Expand Up @@ -102,6 +109,29 @@ def _is_dangerous_env_name(name: Optional[str]) -> bool:
return upper in _DANGEROUS_ENV_NAMES or upper.startswith(_DANGEROUS_ENV_PREFIXES)


def _dangerous_ssh_option(argv: List[str]) -> Optional[str]:
"""Return the canonical option name if argv contains an SSH flag that causes
local code execution (ProxyCommand, LocalCommand, PermitLocalCommand).
Handles both merged (-oProxyCommand=…) and split (-o ProxyCommand=…) forms.
Returns None if no dangerous option is found.
"""
i = 0
while i < len(argv):
token = argv[i]
# Merged form: -oProxyCommand=value or -OLocalCommand=value
if len(token) > 2 and token[0] == '-' and token[1] in ('o', 'O'):
opt_name = token[2:].split('=', 1)[0]
if opt_name.lower() in _DANGEROUS_SSH_OPTIONS:
return opt_name
# Split form: -o ProxyCommand=value
elif token in ('-o', '-O') and i + 1 < len(argv):
opt_name = argv[i + 1].split('=', 1)[0]
if opt_name.lower() in _DANGEROUS_SSH_OPTIONS:
return opt_name
i += 1
return None


def _looks_multi_statement(arg: str) -> bool:
return (
len(arg) > _LONG_ARG_THRESHOLD
Expand Down Expand Up @@ -243,6 +273,12 @@ def confirm_argv(stage: str, argv: List[str], record: Any = None) -> bool:
warning_lines.append(
'argv[0] is a shell/scripting interpreter - it can run any code.'
)
dangerous_opt = _dangerous_ssh_option(argv)
if dangerous_opt is not None:
warning_lines.append(
f'{_sanitize(dangerous_opt)} is set — SSH will execute a local command'
' on this machine with your privileges.'
)
_emit_warning(warning_lines)
_emit_section('Command to execute:', _argv_block(argv))
sys.stderr.write('\n')
Expand Down
Loading