Skip to content

Commit a1f7b21

Browse files
authored
block sys commands on -X (#142)
* block sys commands on -X * check return
1 parent 69f9273 commit a1f7b21

File tree

4 files changed

+46
-1
lines changed

4 files changed

+46
-1
lines changed

cmd/sqlcmd/main.go

+4
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ func run(vars *sqlcmd.Variables, args *SQLCmdArguments) (int, error) {
230230
s := sqlcmd.New(line, wd, vars)
231231
s.UnicodeOutputFile = args.UnicodeOutputFile
232232

233+
if args.DisableCmdAndWarn {
234+
s.Cmd.DisableSysCommands(false)
235+
}
236+
233237
if args.BatchTerminator != "GO" {
234238
err = s.Cmd.SetBatchTerminator(args.BatchTerminator)
235239
if err != nil {

pkg/sqlcmd/commands.go

+20-1
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,17 @@ func newCommands() Commands {
9494
name: "EXEC",
9595
},
9696
}
97-
9897
}
9998

99+
// DisableSysCommands disables the ED and :!! commands.
100+
// When exitOnCall is true, running those commands will exit the process.
101+
func (c Commands) DisableSysCommands(exitOnCall bool) {
102+
f := warnDisabled
103+
if exitOnCall {
104+
f = errorDisabled
105+
}
106+
c["EXEC"].action = f
107+
}
100108
func (c Commands) matchCommand(line string) (*Command, []string) {
101109
for _, cmd := range c {
102110
matchedCommand := cmd.regex.FindStringSubmatch(line)
@@ -107,6 +115,17 @@ func (c Commands) matchCommand(line string) (*Command, []string) {
107115
return nil, nil
108116
}
109117

118+
func warnDisabled(s *Sqlcmd, args []string, line uint) error {
119+
_, _ = s.GetError().Write([]byte(ErrCommandsDisabled.Error() + SqlcmdEol))
120+
return nil
121+
}
122+
123+
func errorDisabled(s *Sqlcmd, args []string, line uint) error {
124+
_, _ = s.GetError().Write([]byte(ErrCommandsDisabled.Error() + SqlcmdEol))
125+
s.Exitcode = 1
126+
return ErrExitRequested
127+
}
128+
110129
func batchTerminatorRegex(terminator string) string {
111130
return fmt.Sprintf(`(?im)^[\t ]*?%s(?:[ ]+(.*$)|$)`, regexp.QuoteMeta(terminator))
112131
}

pkg/sqlcmd/commands_test.go

+20
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,23 @@ func TestExecCommand(t *testing.T) {
267267
assert.Equal(t, buf.buf.String(), "hello"+SqlcmdEol, "echo output should be in sqlcmd output")
268268
}
269269
}
270+
271+
func TestDisableSysCommandBlocksExec(t *testing.T) {
272+
s, buf := setupSqlCmdWithMemoryOutput(t)
273+
defer buf.Close()
274+
s.Cmd.DisableSysCommands(false)
275+
c := []string{"set nocount on", ":!! echo hello", "select 100", "go"}
276+
err := runSqlCmd(t, s, c)
277+
if assert.NoError(t, err, ":!! with warning should not raise error") {
278+
assert.Contains(t, buf.buf.String(), ErrCommandsDisabled.Error()+SqlcmdEol+"100"+SqlcmdEol)
279+
assert.Equal(t, 0, s.Exitcode, "ExitCode after warning")
280+
}
281+
buf.buf.Reset()
282+
s.Cmd.DisableSysCommands(true)
283+
err = runSqlCmd(t, s, c)
284+
if assert.NoError(t, err, ":!! with error should not return error") {
285+
assert.Contains(t, buf.buf.String(), ErrCommandsDisabled.Error()+SqlcmdEol)
286+
assert.NotContains(t, buf.buf.String(), "100", "query should not run when syscommand disabled")
287+
assert.Equal(t, 1, s.Exitcode, "ExitCode after error")
288+
}
289+
}

pkg/sqlcmd/sqlcmd.go

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ var (
3232
ErrNeedPassword = errors.New("need password")
3333
// ErrCtrlC indicates execution was ended by ctrl-c or ctrl-break
3434
ErrCtrlC = errors.New(WarningPrefix + "The last operation was terminated because the user pressed CTRL+C")
35+
// ErrCommandsDisabled indicates system commands and startup script are disabled
36+
ErrCommandsDisabled = errors.New(ErrorPrefix + "ED and !!<command> commands, startup script, and environment variables are disabled.")
3537
)
3638

3739
const maxLineBuffer = 2 * 1024 * 1024 // 2Mb

0 commit comments

Comments
 (0)