Browse Source

fix(security): 修复命令注入风险

ffmpeg/silk/直播推流改为ProcessBuilder参数数组,避免字符串拼接执行。

Co-authored-by: Cursor <cursoragent@cursor.com>
吴树波 1 day ago
parent
commit
1aaf380ea4

+ 11 - 3
fs-company/src/main/java/com/fs/company/utils/AudioUtils.java

@@ -174,9 +174,17 @@ public class AudioUtils {
              // 如果删除下班这行写process.waitFor() ,太长的语音会阻塞,BufferedReader 打印出来太长的语音也会阻塞
              process = Runtime.getRuntime().exec("taskkill -f -t -im silk_v3_encoder.exe");
              */
-            // 方法2,除了会弹出弹窗,没什么问题 cmd /c 极为重要,执行完毕后会自动关闭
-            process = Runtime.getRuntime().exec("cmd /c start " + path + "silk_v3_encoder.exe " + pcmPath + " " + target + " -tencent");
-            process .waitFor();
+            // 方法2:ProcessBuilder 数组传参,避免 Runtime.exec 字符串拼接命令注入
+            // start 的第二个参数为空窗口标题,保持与原 cmd /c start 行为一致
+            ProcessBuilder pb = new ProcessBuilder(
+                    "cmd", "/c", "start", "",
+                    path + "silk_v3_encoder.exe",
+                    pcmPath,
+                    target,
+                    "-tencent"
+            );
+            process = pb.start();
+            process.waitFor();
             Thread.sleep(1000);
             // 有更好的方法会后续慢慢更新..
         } catch (Exception e) {

+ 3 - 2
fs-qwhook-msg/src/main/java/com/fs/app/utils/AudioUtils.java

@@ -172,8 +172,9 @@ public class AudioUtils {
              process = Runtime.getRuntime().exec("taskkill -f -t -im silk_v3_encoder.exe");
              */
             // 方法2,除了会弹出弹窗,没什么问题 cmd /c 极为重要,执行完毕后会自动关闭
-            process = Runtime.getRuntime().exec("cmd /c start " + path + "silk_v3_encoder.exe " + pcmPath + " " + target + " -tencent");
-            process .waitFor();
+            ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "start", "", path + "silk_v3_encoder.exe", pcmPath, target, "-tencent");
+            process = pb.start();
+            process.waitFor();
             Thread.sleep(1000);
             // 有更好的方法会后续慢慢更新..
         } catch (Exception e) {

+ 3 - 2
fs-qwhook-sop/src/main/java/com/fs/app/utils/AudioUtils.java

@@ -173,8 +173,9 @@ public class AudioUtils {
              process = Runtime.getRuntime().exec("taskkill -f -t -im silk_v3_encoder.exe");
              */
             // 方法2,除了会弹出弹窗,没什么问题 cmd /c 极为重要,执行完毕后会自动关闭
-            process = Runtime.getRuntime().exec("cmd /c start " + path + "silk_v3_encoder.exe " + pcmPath + " " + target + " -tencent");
-            process .waitFor();
+            ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "start", "", path + "silk_v3_encoder.exe", pcmPath, target, "-tencent");
+            process = pb.start();
+            process.waitFor();
             Thread.sleep(1000);
             // 有更好的方法会后续慢慢更新..
         } catch (Exception e) {

+ 3 - 2
fs-qwhook/src/main/java/com/fs/app/utils/AudioUtils.java

@@ -172,8 +172,9 @@ public class AudioUtils {
              process = Runtime.getRuntime().exec("taskkill -f -t -im silk_v3_encoder.exe");
              */
             // 方法2,除了会弹出弹窗,没什么问题 cmd /c 极为重要,执行完毕后会自动关闭
-            process = Runtime.getRuntime().exec("cmd /c start " + path + "silk_v3_encoder.exe " + pcmPath + " " + target + " -tencent");
-            process .waitFor();
+            ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "start", "", path + "silk_v3_encoder.exe", pcmPath, target, "-tencent");
+            process = pb.start();
+            process.waitFor();
             Thread.sleep(1000);
             // 有更好的方法会后续慢慢更新..
         } catch (Exception e) {

+ 12 - 7
fs-service/src/main/java/com/fs/course/service/impl/HuaweiObsServiceImpl.java

@@ -68,13 +68,18 @@ public class HuaweiObsServiceImpl implements IHuaweiObsService {
 
     private void mergeVideos(String fixedVideoPath, String uploadedVideoPath, String outputPath) throws IOException, InterruptedException {
 
-        String command = String.format("ffmpeg -i %s -i %s -filter_complex \"[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa]\" -map \"[outv]\" -map \"[outa]\" %s", fixedVideoPath,uploadedVideoPath, outputPath);
-
-        // 打印调试信息
-        System.out.println("Executing command: " + command);
-
-        // 执行命令
-        Process process = Runtime.getRuntime().exec(command);
+        // 使用 ProcessBuilder 数组传参,避免 Runtime.exec 字符串拼接命令注入
+        ProcessBuilder processBuilder = new ProcessBuilder(
+                "ffmpeg",
+                "-i", fixedVideoPath,
+                "-i", uploadedVideoPath,
+                "-filter_complex", "[0:v][0:a][1:v][1:a]concat=n=2:v=1:a=1[outv][outa]",
+                "-map", "[outv]",
+                "-map", "[outa]",
+                outputPath
+        );
+        System.out.println("Executing command: " + processBuilder.command());
+        Process process = processBuilder.start();
 
         // 等待命令执行完成
         int exitCode = process.waitFor();

+ 9 - 7
fs-service/src/main/java/com/fs/course/service/impl/HuaweiVodServiceImpl.java

@@ -63,13 +63,15 @@ public class HuaweiVodServiceImpl implements IHuaweiVodService {
             writer.println("file '" + fixedVideoPath.replace("\\", "/") + "'");
             writer.println("file '" + uploadedVideoPath.replace("\\", "/") + "'");
         }
-        String command = String.format("ffmpeg -f concat -safe 0 -i %s -c copy %s", listFile.getPath(), outputPath);
-
-        // 打印调试信息
-        System.out.println("Executing command: " + command);
-
-        // 执行命令
-        Process process = Runtime.getRuntime().exec(command);
+        // 使用 ProcessBuilder 数组传参,避免 Runtime.exec 字符串拼接命令注入
+        ProcessBuilder processBuilder = new ProcessBuilder(
+                "ffmpeg", "-f", "concat", "-safe", "0",
+                "-i", listFile.getPath(),
+                "-c", "copy",
+                outputPath
+        );
+        System.out.println("Executing command: " + processBuilder.command());
+        Process process = processBuilder.start();
 
         // 等待命令执行完成
         int exitCode = process.waitFor();

+ 25 - 9
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -854,8 +854,8 @@ public class LiveServiceImpl implements ILiveService
             // 生成唯一的流密钥
             String streamKey = "stream_" + live.getLiveId() + "_" + System.currentTimeMillis();
 
-            // 构建FFmpeg推流命令
-            String ffmpegCmd = buildFFmpegCommand(curLiveVideo.getVideoUrl(), streamKey);
+            // 构建FFmpeg推流命令(参数列表,避免命令注入)
+            List<String> ffmpegCmd = buildFFmpegCommand(curLiveVideo.getVideoUrl(), streamKey);
 
             // 启动推流进程
             Process process = processManager.startProcess(ffmpegCmd);
@@ -1468,14 +1468,30 @@ public class LiveServiceImpl implements ILiveService
 
 
     /**
-     * 构建FFmpeg推流命令
+     * 构建FFmpeg推流命令(参数列表形式,路径作为独立参数)
      */
-    private String buildFFmpegCommand(String videoPath, String streamKey) {
-        return String.format(
-                "ffmpeg -re -stream_loop -1 -i \"%s\" -c:v libx264 -preset ultrafast -b:v 1000k " +
-                        "-c:a aac -b:a 128k -f flv rtmp://your-srs-server/live/%s",
-                videoPath, streamKey
-        );
+    private List<String> buildFFmpegCommand(String videoPath, String streamKey) {
+        List<String> cmd = new ArrayList<>();
+        cmd.add("ffmpeg");
+        cmd.add("-re");
+        cmd.add("-stream_loop");
+        cmd.add("-1");
+        cmd.add("-i");
+        cmd.add(videoPath);
+        cmd.add("-c:v");
+        cmd.add("libx264");
+        cmd.add("-preset");
+        cmd.add("ultrafast");
+        cmd.add("-b:v");
+        cmd.add("1000k");
+        cmd.add("-c:a");
+        cmd.add("aac");
+        cmd.add("-b:a");
+        cmd.add("128k");
+        cmd.add("-f");
+        cmd.add("flv");
+        cmd.add("rtmp://your-srs-server/live/" + streamKey);
+        return cmd;
     }
 
     /**

+ 4 - 3
fs-service/src/main/java/com/fs/live/utils/ProcessManager.java

@@ -4,6 +4,7 @@ import org.springframework.stereotype.Component;
 
 import java.io.IOException;
 import java.lang.reflect.Field;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
@@ -18,10 +19,10 @@ public class ProcessManager {
     private final Map<String, Process> processMap = new ConcurrentHashMap<>();
 
     /**
-     * 启动进程
+     * 启动进程(命令参数列表,避免字符串 split 导致路径含空格时异常或注入)
      */
-    public Process startProcess(String command) throws IOException {
-        ProcessBuilder processBuilder = new ProcessBuilder(command.split(" "));
+    public Process startProcess(List<String> command) throws IOException {
+        ProcessBuilder processBuilder = new ProcessBuilder(command);
         Process process = processBuilder.start();
 
         String processId = getProcessId(process);