analyzeCommandSecurity function

CommandSecurityAnalysis analyzeCommandSecurity(
  1. String input
)

Analyze a command for security risks.

Implementation

CommandSecurityAnalysis analyzeCommandSecurity(String input) {
  final tokens = tokenizeBash(input);
  final cmdList = parseCommand(input);
  final allCmds = cmdList.allCommands;

  var hasCommandSubstitution = false;
  var hasVariableExpansion = false;
  var hasGlobbing = false;
  var hasRedirection = false;
  var hasPiping = false;
  var hasBackgroundExec = false;
  var hasSubshell = false;
  var hasEval = false;
  var hasExec = false;
  var hasSudo = false;
  var hasChown = false;
  var hasRemove = false;
  var hasNetworkAccess = false;
  var hasDiskWrite = false;
  final writtenPaths = <String>[];
  final readPaths = <String>[];
  final executables = <String>[];

  // Token-level analysis.
  for (final t in tokens) {
    switch (t.type) {
      case BashTokenType.subshell:
        hasCommandSubstitution = true;
        hasSubshell = true;
        break;
      case BashTokenType.backtick:
        hasCommandSubstitution = true;
        break;
      case BashTokenType.variable:
        hasVariableExpansion = true;
        break;
      case BashTokenType.glob:
        hasGlobbing = true;
        break;
      case BashTokenType.redirect:
      case BashTokenType.heredocMarker:
        hasRedirection = true;
        break;
      case BashTokenType.pipe:
        hasPiping = true;
        break;
      case BashTokenType.background:
        hasBackgroundExec = true;
        break;
      case BashTokenType.lparen:
        hasSubshell = true;
        break;
      default:
        break;
    }
  }

  // Check for background in list operators.
  for (final entry in cmdList.entries) {
    if (entry.operator_ == ListOperator.background) {
      hasBackgroundExec = true;
    }
  }

  // Command-level analysis.
  for (final cmd in allCmds) {
    final exe = cmd.executable.split('/').last;
    if (exe.isNotEmpty) executables.add(exe);

    if (exe == 'eval') hasEval = true;
    if (exe == 'exec') hasExec = true;
    if (exe == 'sudo') hasSudo = true;
    if (exe == 'chown' || exe == 'chmod' || exe == 'chgrp') hasChown = true;
    if (exe == 'rm' || exe == 'rmdir' || exe == 'shred') hasRemove = true;
    if (_networkExecutables.contains(exe)) hasNetworkAccess = true;

    // Detect disk writes.
    if (_writeCommands.contains(exe) || exe == 'rm' || exe == 'rmdir') {
      hasDiskWrite = true;
      // Collect written paths from arguments.
      for (final arg in cmd.arguments) {
        if (!arg.startsWith('-') && !arg.startsWith('\$')) {
          writtenPaths.add(arg);
        }
      }
    }

    // Detect reads.
    if (_readCommands.contains(exe)) {
      for (final arg in cmd.arguments) {
        if (!arg.startsWith('-') && !arg.startsWith('\$')) {
          readPaths.add(arg);
        }
      }
    }

    // Redirections in the command.
    for (final redir in cmd.redirects) {
      hasRedirection = true;
      if (redir.type == RedirectType.output ||
          redir.type == RedirectType.append ||
          redir.type == RedirectType.both ||
          redir.type == RedirectType.errorOutput ||
          redir.type == RedirectType.errorAppend) {
        hasDiskWrite = true;
        if (redir.target.isNotEmpty && !redir.target.startsWith('\$')) {
          writtenPaths.add(redir.target);
        }
      }
      if (redir.type == RedirectType.input ||
          redir.type == RedirectType.inputOutput) {
        if (redir.target.isNotEmpty && !redir.target.startsWith('\$')) {
          readPaths.add(redir.target);
        }
      }
    }

    // Sudo elevates everything that follows.
    if (exe == 'sudo') {
      hasSudo = true;
      for (final arg in cmd.arguments) {
        if (!arg.startsWith('-')) {
          final sudoExe = arg.split('/').last;
          if (sudoExe == 'rm' || sudoExe == 'rmdir') hasRemove = true;
          if (sudoExe == 'chown' || sudoExe == 'chmod') hasChown = true;
          if (_networkExecutables.contains(sudoExe)) hasNetworkAccess = true;
          break;
        }
      }
    }
  }

  return CommandSecurityAnalysis(
    hasCommandSubstitution: hasCommandSubstitution,
    hasVariableExpansion: hasVariableExpansion,
    hasGlobbing: hasGlobbing,
    hasRedirection: hasRedirection,
    hasPiping: hasPiping,
    hasBackgroundExec: hasBackgroundExec,
    hasSubshell: hasSubshell,
    hasEval: hasEval,
    hasExec: hasExec,
    hasSudo: hasSudo,
    hasChown: hasChown,
    hasRemove: hasRemove,
    hasNetworkAccess: hasNetworkAccess,
    hasDiskWrite: hasDiskWrite,
    writtenPaths: writtenPaths,
    readPaths: readPaths,
    executables: executables,
  );
}