diff --git a/.github/badges/branches.svg b/.github/badges/branches.svg index ef724ad..ea0fee7 100644 --- a/.github/badges/branches.svg +++ b/.github/badges/branches.svg @@ -1 +1 @@ -branches60.8% \ No newline at end of file +branches60.4% \ No newline at end of file diff --git a/.github/badges/jacoco.svg b/.github/badges/jacoco.svg index 65ea9eb..856b235 100644 --- a/.github/badges/jacoco.svg +++ b/.github/badges/jacoco.svg @@ -1 +1 @@ -coverage81.2% \ No newline at end of file +coverage80.3% \ No newline at end of file diff --git a/src/main/java/io/github/pixee/security/SystemCommand.java b/src/main/java/io/github/pixee/security/SystemCommand.java index e968624..b371762 100644 --- a/src/main/java/io/github/pixee/security/SystemCommand.java +++ b/src/main/java/io/github/pixee/security/SystemCommand.java @@ -25,6 +25,41 @@ public static Set defaultRestrictions() { SystemCommandRestrictions.PREVENT_ARGUMENTS_TARGETING_SENSITIVE_FILES); } + /** + * Does the same as {@link ProcessBuilder#start()}, but adds restrictions on what types of commands + * will be allowed. Will throw a {@link SecurityException} if any of the restrictions may be + * violated by the command found. Note that the method of detecting violations is based on + * semantic analysis of the command, and so is vulnerable to impedance mismatches between the + * analysis we perform and whatever shell is interpreting the command. Either way, it's a lot + * safer. + * + * @param processBuilder the system command about to be run + * @param restrictions the set of restrictions to run with + * @return the {@link Process} that results from the hardened {@link ProcessBuilder#start()} call + * @throws SecurityException if multiple commands are found + * @throws IllegalArgumentException if restriction is null + * @throws IOException from the wrapped system process invocation call + */ + public static Process runProcessBuilder( + final ProcessBuilder processBuilder, + final Set restrictions) + throws IOException { + runChecks(processBuilder.command(), restrictions); + return processBuilder.start(); + } + + /** + * Delegates to {@link SystemCommand#runProcessBuilder(ProcessBuilder, Set)} with default + * restrictions. + * + * @param processBuilder the system command about to be run + * @return the {@link Process} that results from the hardened {@link ProcessBuilder#start()} call + * @throws IOException from the wrapped system process invocation call + */ + public static Process runProcessBuilder(final ProcessBuilder processBuilder) throws IOException { + return runProcessBuilder(processBuilder, defaultRestrictions()); + } + /** * Does the same as {@link Runtime#exec(String)}, but adds restrictions on what types of commands * will be allowed. Will throw a {@link SecurityException} if any of the restrictions may be @@ -275,6 +310,19 @@ private static void runChecks( } } + /** + * Helper method similar to {@link #runChecks(String[], Set)} but avoids the need to convert a {@link List} to an array. + * @param command to be checked + * @param restrictions to be checked against + */ + private static void runChecks(final List command, final Set restrictions) { + final CommandLine parsedCommandLine = new CommandLine(command.get(0)); + for (int i = 1; i < command.size(); i++) { + parsedCommandLine.addArgument(command.get(i)); + } + runChecks(parsedCommandLine, restrictions); + } + private static void runChecks( final String[] command, final Set restrictions) { final CommandLine parsedCommandLine = new CommandLine(command[0]);