(script: string)
| 71 | } |
| 72 | |
| 73 | function detectCatFileRead(script: string): boolean { |
| 74 | // Fast-path: avoid doing any work if "cat" doesn't appear at all. |
| 75 | if (!script.includes("cat")) { |
| 76 | return false; |
| 77 | } |
| 78 | |
| 79 | // Split on common statement separators and pipelines. |
| 80 | // Note: this is intentionally not a full shell parser; we aim to catch the common |
| 81 | // "cat <path>" pattern without false positives like "echo foo | cat". |
| 82 | const segments = script.split(/\n|&&|\|\||;|\|/); |
| 83 | |
| 84 | for (const segment of segments) { |
| 85 | const tokens = segment.trim().split(/\s+/).filter(Boolean); |
| 86 | const catIndex = getCatCommandTokenIndex(tokens); |
| 87 | if (catIndex === null) continue; |
| 88 | |
| 89 | const args = tokens.slice(catIndex + 1); |
| 90 | if (args.length === 0) continue; // "cat" (stdin passthrough) |
| 91 | |
| 92 | // Ignore heredocs / here-strings (not a file read). |
| 93 | if (args.some((t) => t.startsWith("<<") || t.startsWith("<<<"))) { |
| 94 | continue; |
| 95 | } |
| 96 | |
| 97 | let expectInputFile = false; |
| 98 | let skipNextOutputTarget = false; |
| 99 | |
| 100 | for (const token of args) { |
| 101 | if (expectInputFile) { |
| 102 | expectInputFile = false; |
| 103 | if (token !== "-" && token.length > 0) { |
| 104 | return true; |
| 105 | } |
| 106 | continue; |
| 107 | } |
| 108 | |
| 109 | if (skipNextOutputTarget) { |
| 110 | skipNextOutputTarget = false; |
| 111 | continue; |
| 112 | } |
| 113 | |
| 114 | // Input redirection: cat < file OR cat <file |
| 115 | if (token === "<" || token === "0<") { |
| 116 | expectInputFile = true; |
| 117 | continue; |
| 118 | } |
| 119 | const inputMatch = /^(?:0)?<(.+)$/.exec(token); |
| 120 | if (inputMatch && !token.startsWith("<<") && !token.startsWith("<<<")) { |
| 121 | const inputFile = inputMatch[1]; |
| 122 | if (inputFile !== "-" && inputFile.length > 0) { |
| 123 | return true; |
| 124 | } |
| 125 | continue; |
| 126 | } |
| 127 | |
| 128 | // Output redirection: ignore output targets (doesn't indicate reading a file). |
| 129 | if ( |
| 130 | token === ">" || |
no test coverage detected