Repository: nats-io/nsc (go)

Issue:
Role names should be validated
I was looking up what, if any constraints there are with role (operator, account, or user) names and so I tried this.

```
❯ nsc add account foo/bar
[ OK ] generated and stored account key "ACCPV5QZDNXZK3SJSN5RSHO3TVLKPH5GBIXRMAZZU4FJOCYAWDQ3XV3G"
[ OK ] added account "foo/bar"
Error: "foo/bar" not in accounts for operator "stage" in `/Users/ruthb/.nsc/nats`
```

Pull request description:
prevent entity names with path separators
[FIX] entity names using a path separator are parsed as a path, which then writes them to the wrong location in the store - this fix generates an error if for such names

FIXES #398

Patch:
diff --git a/cmd/addaccount.go b/cmd/addaccount.go
index 28575a4..b03e735 100644
--- a/cmd/addaccount.go
+++ b/cmd/addaccount.go
@@ -180,6 +180,11 @@ func (p *AddAccountParams) Validate(ctx ActionCtx) error {
 		return fmt.Errorf("account name is required")
 	}
 
+	if strings.ContainsAny(p.name, "/\\") {
+		ctx.CurrentCmd().SilenceUsage = false
+		return fmt.Errorf("name cannot contain '/' or '\\'")
+	}
+
 	if p.name == "*" {
 		p.name = GetRandomName(0)
 	}
diff --git a/cmd/addoperator.go b/cmd/addoperator.go
index 82b073f..04c416b 100644
--- a/cmd/addoperator.go
+++ b/cmd/addoperator.go
@@ -19,6 +19,7 @@ import (
 	"errors"
 	"fmt"
 	"os"
+	"strings"
 
 	cli "github.com/nats-io/cliprompts/v2"
 	"github.com/nats-io/jwt/v2"
@@ -251,6 +252,10 @@ func (p *AddOperatorParams) Validate(ctx ActionCtx) error {
 		ctx.CurrentCmd().SilenceUsage = false
 		return fmt.Errorf("operator name is required")
 	}
+	if strings.ContainsAny(p.name, "/\\") {
+		ctx.CurrentCmd().SilenceUsage = false
+		return fmt.Errorf("name cannot contain '/' or '\\'")
+	}
 
 	if err = p.TimeParams.Validate(); err != nil {
 		return err
diff --git a/cmd/adduser.go b/cmd/adduser.go
index 0db4865..b70a47a 100644
--- a/cmd/adduser.go
+++ b/cmd/adduser.go
@@ -213,6 +213,11 @@ func (p *AddUserParams) Validate(ctx ActionCtx) error {
 		return fmt.Errorf("user name is required")
 	}
 
+	if strings.ContainsAny(p.userName, "/\\") {
+		ctx.CurrentCmd().SilenceUsage = false
+		return fmt.Errorf("name cannot contain '/' or '\\'")
+	}
+
 	if p.userName == "*" {
 		p.userName = GetRandomName(0)
 	}


Test patch:
diff --git a/cmd/addaccount_test.go b/cmd/addaccount_test.go
index a43ad55..eb41936 100644
--- a/cmd/addaccount_test.go
+++ b/cmd/addaccount_test.go
@@ -284,3 +284,12 @@ func Test_AddAccount_Pubs(t *testing.T) {
 	require.ElementsMatch(t, cc.DefaultPermissions.Pub.Deny, []string{"foo", "bar"})
 	require.ElementsMatch(t, cc.DefaultPermissions.Sub.Deny, []string{"bar"})
 }
+
+func Test_AddAccountBadName(t *testing.T) {
+	ts := NewTestStore(t, "O")
+	defer ts.Done(t)
+
+	_, _, err := ExecuteCmd(CreateAddAccountCmd(), "A/B")
+	require.Error(t, err)
+	require.Contains(t, err.Error(), "name cannot contain '/' or '\\'")
+}
diff --git a/cmd/addoperator_test.go b/cmd/addoperator_test.go
index 28fa5ae..b45da3c 100644
--- a/cmd/addoperator_test.go
+++ b/cmd/addoperator_test.go
@@ -329,3 +329,12 @@ func TestImportReIssuedOperator(t *testing.T) {
 	require.NoError(t, err)
 	checkOp(pubNew)
 }
+
+func Test_AddOperatorBadName(t *testing.T) {
+	ts := NewEmptyStore(t)
+	defer ts.Done(t)
+
+	_, _, err := ExecuteCmd(createAddOperatorCmd(), "A/B")
+	require.Error(t, err)
+	require.Contains(t, err.Error(), "name cannot contain '/' or '\\'")
+}
diff --git a/cmd/adduser_test.go b/cmd/adduser_test.go
index ae54f6f..108d0ac 100644
--- a/cmd/adduser_test.go
+++ b/cmd/adduser_test.go
@@ -590,3 +590,14 @@ func Test_AddUsersWithSharedSigningKey(t *testing.T) {
 	_, _, err = ExecuteCmd(HoistRootFlags(CreateAddUserCmd()), "u", "--account", "B", "-K", sk)
 	require.NoError(t, err)
 }
+
+func Test_AddUserBadName(t *testing.T) {
+	ts := NewTestStore(t, "O")
+	defer ts.Done(t)
+
+	ts.AddAccount(t, "A")
+
+	_, _, err := ExecuteCmd(CreateAddUserCmd(), "A/B")
+	require.Error(t, err)
+	require.Contains(t, err.Error(), "name cannot contain '/' or '\\'")
+}