@@ -36,58 +36,75 @@ function sanitizeInput(input: string): string {
3636 return input . replace ( / [ ; & | ` $ ( ) < > \\ ] / g, '' ) ;
3737}
3838
39+ /**
40+ * Checks if command contains dangerous characters
41+ */
42+ function containsDangerousCharacters ( command : string ) : boolean {
43+ const dangerousPatterns = [ '&&' , '||' , ';' , '|' , '`' , '$(' , '>' , '<' ] ;
44+ return dangerousPatterns . some ( ( pattern ) => command . includes ( pattern ) ) ;
45+ }
46+
47+ /**
48+ * Processes a character during command parsing
49+ */
50+ function processCharacter (
51+ char : string ,
52+ index : number ,
53+ command : string ,
54+ state : { inQuotes : boolean ; quoteChar : string ; current : string }
55+ ) : void {
56+ const isQuote = char === '"' || char === "'" ;
57+ const isEscaped = index > 0 && command [ index - 1 ] === '\\' ;
58+
59+ if ( isQuote && ! isEscaped ) {
60+ if ( ! state . inQuotes ) {
61+ state . inQuotes = true ;
62+ state . quoteChar = char ;
63+ } else if ( char === state . quoteChar ) {
64+ state . inQuotes = false ;
65+ state . quoteChar = '' ;
66+ } else {
67+ state . current += char ;
68+ }
69+ } else if ( char === ' ' && ! state . inQuotes ) {
70+ // Space handling is done in the main function
71+ return ;
72+ } else {
73+ state . current += char ;
74+ }
75+ }
76+
3977/**
4078 * Validates and parses command arguments safely
4179 */
4280function parseCommand ( command : string ) : string [ ] {
4381 // Basic validation to prevent obvious injection attempts
44- if (
45- command . includes ( '&&' ) ||
46- command . includes ( '||' ) ||
47- command . includes ( ';' ) ||
48- command . includes ( '|' ) ||
49- command . includes ( '`' ) ||
50- command . includes ( '$(' ) ||
51- command . includes ( '>' ) ||
52- command . includes ( '<' )
53- ) {
82+ if ( containsDangerousCharacters ( command ) ) {
5483 throw new Error ( 'Command contains potentially dangerous characters. Please use simple commands only.' ) ;
5584 }
5685
5786 // Split by spaces but respect quoted strings
5887 const args : string [ ] = [ ] ;
59- let current = '' ;
60- let inQuotes = false ;
61- let quoteChar = '' ;
88+ const state = { current : '' , inQuotes : false , quoteChar : '' } ;
6289
6390 for ( let i = 0 ; i < command . length ; i ++ ) {
6491 const char = command [ i ] ;
6592
66- if ( ( char === '"' || char === "'" ) && ( i === 0 || command [ i - 1 ] !== '\\' ) ) {
67- if ( ! inQuotes ) {
68- inQuotes = true ;
69- quoteChar = char ;
70- } else if ( char === quoteChar ) {
71- inQuotes = false ;
72- quoteChar = '' ;
73- } else {
74- current += char ;
75- }
76- } else if ( char === ' ' && ! inQuotes ) {
77- if ( current ) {
78- args . push ( current ) ;
79- current = '' ;
93+ if ( char === ' ' && ! state . inQuotes ) {
94+ if ( state . current ) {
95+ args . push ( state . current ) ;
96+ state . current = '' ;
8097 }
8198 } else {
82- current += char ;
99+ processCharacter ( char , i , command , state ) ;
83100 }
84101 }
85102
86- if ( current ) {
87- args . push ( current ) ;
103+ if ( state . current ) {
104+ args . push ( state . current ) ;
88105 }
89106
90- if ( inQuotes ) {
107+ if ( state . inQuotes ) {
91108 throw new Error ( 'Unclosed quote in command' ) ;
92109 }
93110
@@ -259,8 +276,9 @@ export async function run(): Promise<void> {
259276
260277 // Validate inputs for non-standalone mode
261278 const isStandalone = instance === 'standalone' ;
262- if ( ! isStandalone && ! accessToken ) {
263- throw new Error ( 'access-token is required when not in standalone mode' ) ;
279+ const isLocal = instance === 'local' ;
280+ if ( ! isStandalone && ! accessToken && ! isLocal ) {
281+ throw new Error ( 'access-token is required when not in standalone or local mode' ) ;
264282 }
265283
266284 // Setup output masking for sensitive values
0 commit comments