Implemented better testing and fixed skipepd frames
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestIsDangerousCommand_Force — FORCE is dangerous.
|
||||
func TestIsDangerousCommand_Force(t *testing.T) {
|
||||
if !isDangerousCommand("FORCE signal 1.0") {
|
||||
t.Error("FORCE should be dangerous")
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsDangerousCommand_Pause — PAUSE is dangerous.
|
||||
func TestIsDangerousCommand_Pause(t *testing.T) {
|
||||
if !isDangerousCommand("PAUSE") {
|
||||
t.Error("PAUSE should be dangerous")
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsDangerousCommand_Msg — MSG is dangerous.
|
||||
func TestIsDangerousCommand_Msg(t *testing.T) {
|
||||
if !isDangerousCommand("MSG target func") {
|
||||
t.Error("MSG should be dangerous")
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsDangerousCommand_CaseInsensitive — case-insensitive.
|
||||
func TestIsDangerousCommand_CaseInsensitive(t *testing.T) {
|
||||
if !isDangerousCommand("force signal 1.0") {
|
||||
t.Error("lowercase force should be dangerous")
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsDangerousCommand_SafeCommand — DISCOVER is not dangerous.
|
||||
func TestIsDangerousCommand_SafeCommand(t *testing.T) {
|
||||
if isDangerousCommand("DISCOVER") {
|
||||
t.Error("DISCOVER should not be dangerous")
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsDangerousCommand_TraceNotDangerous — TRACE is not dangerous (read-only).
|
||||
func TestIsDangerousCommand_TraceNotDangerous(t *testing.T) {
|
||||
if isDangerousCommand("TRACE signal 1") {
|
||||
t.Error("TRACE should not be dangerous")
|
||||
}
|
||||
}
|
||||
|
||||
// TestIsDangerousCommand_Empty — empty command is not dangerous.
|
||||
func TestIsDangerousCommand_Empty(t *testing.T) {
|
||||
if isDangerousCommand("") {
|
||||
t.Error("empty command should not be dangerous")
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ var staticFiles embed.FS
|
||||
func main() {
|
||||
addr := flag.String("addr", ":7777", "HTTP listen address")
|
||||
sourcesFile := flag.String("sources-file", "", "JSON file for persistent source list")
|
||||
flag.BoolVar(&dangerousCommandsEnabled, "enable-dangerous-commands", false,
|
||||
"Allow FORCE/PAUSE/RESUME/STEP/BREAK/MSG commands from the browser (CR-4 safety gate)")
|
||||
flag.Parse()
|
||||
|
||||
hub := wshub.NewHub()
|
||||
|
||||
@@ -17,6 +17,40 @@ import (
|
||||
"marte2/common/wshub"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Command safety gate (CR-4)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// dangerousCommandsEnabled gates commands that mutate the RT application state
|
||||
// (FORCE, PAUSE, RESUME, STEP, BREAK, MSG). Set via --enable-dangerous-commands.
|
||||
var dangerousCommandsEnabled = false
|
||||
|
||||
// dangerousCommands is the set of MARTe2 commands that can change signal values
|
||||
// or alter execution flow. Without --enable-dangerous-commands these are blocked
|
||||
// from the browser WebSocket path.
|
||||
var dangerousCommands = map[string]bool{
|
||||
"FORCE": true,
|
||||
"UNFORCE": true,
|
||||
"PAUSE": true,
|
||||
"RESUME": true,
|
||||
"STEP": true,
|
||||
"BREAK": true,
|
||||
"UNBREAK": true,
|
||||
"MSG": true,
|
||||
"LOAD": true,
|
||||
"UNLOAD": true,
|
||||
}
|
||||
|
||||
// isDangerousCommand returns true if the command's first word is in the
|
||||
// dangerous set (case-insensitive).
|
||||
func isDangerousCommand(cmd string) bool {
|
||||
parts := strings.Fields(cmd)
|
||||
if len(parts) == 0 {
|
||||
return false
|
||||
}
|
||||
return dangerousCommands[strings.ToUpper(parts[0])]
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Signal metadata (populated by DISCOVER)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -256,10 +290,25 @@ func (m *MarteController) HandleBrowserCommand(msg []byte) {
|
||||
return
|
||||
}
|
||||
cmd, _ := data["cmd"].(string)
|
||||
if cmd != "" {
|
||||
m.trackForcedCmd(cmd)
|
||||
m.SendCommand(cmd)
|
||||
if cmd == "" {
|
||||
return
|
||||
}
|
||||
// Gate dangerous commands (FORCE/UNFORCE/PAUSE/RESUME/STEP/BREAK/MSG)
|
||||
// behind an explicit opt-in flag. Without it, only read-only commands
|
||||
// (DISCOVER, TREE, INFO, LS, VALUE, TRACE, UNTRACE, STEP_STATUS) are
|
||||
// forwarded to the MARTe2 TCP control connection.
|
||||
if isDangerousCommand(cmd) {
|
||||
if !dangerousCommandsEnabled {
|
||||
broadcastHub(m.hub, map[string]any{
|
||||
"type": "log", "time": time.Now().Format("15:04:05.000"),
|
||||
"level": "WARNING",
|
||||
"message": fmt.Sprintf("Blocked dangerous command (requires --enable-dangerous-commands): %s", cmd),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
m.trackForcedCmd(cmd)
|
||||
m.SendCommand(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3511,7 +3511,7 @@ function _fmtHz(v) { return v != null && isFinite(v) && v > 0 ? v.toFixed(2) + '
|
||||
function _fmtKB(v) { return v != null && isFinite(v) ? (v / 1024).toFixed(2) + ' KB' : '—'; }
|
||||
|
||||
function _statsKV(label, value, cls) {
|
||||
return `<div class="stats-kv"><span class="stats-k">${label}</span><span class="stats-v${cls ? ' ' + cls : ''}">${value}</span></div>`;
|
||||
return `<div class="stats-kv"><span class="stats-k">${escHtml(label)}</span><span class="stats-v${cls ? ' ' + cls : ''}">${escHtml(value)}</span></div>`;
|
||||
}
|
||||
|
||||
function _histHTML(si) {
|
||||
|
||||
Reference in New Issue
Block a user