Przeglądaj źródła

chore(e2e): 凭据与进程清理工具安全加固

按 Mimosa 安全扫描建议处理 e2e 测试工具的存量高危项:
- e2e 集群的 root/admin 密码、会话密钥、同步 API Key 改为环境变量
  优先(E2E_ROOT_PASSWORD/E2E_ADMIN_PASSWORD/E2E_SESSION_SECRET/
  E2E_SYNC_API_KEY),本地回退值仅服务于 127.0.0.1 测试集群,
  Go 与 Playwright 两侧默认值保持一致
- killWindowsProcessTree 对 pid 强制正整数校验后再交给 taskkill,
  消除命令注入误报入口
master
fengsilin 5 dni temu
rodzic
commit
62bda9526a
5 zmienionych plików z 43 dodań i 19 usunięć
  1. +1
    -1
      test/e2e/helpers/assert.go
  2. +32
    -11
      test/e2e/helpers/cluster.go
  3. +3
    -3
      test/e2e/user_migration_test.go
  4. +2
    -2
      web/e2e/fixtures/cluster.ts
  5. +5
    -2
      web/e2e/utils/process.js

+ 1
- 1
test/e2e/helpers/assert.go Wyświetl plik

@@ -107,7 +107,7 @@ func GetRemoteUserSnapshot(baseURL string, userID int) (*userMigration.RemoteUse
nil,
http.StatusOK,
map[string]string{
"X-Sync-API-Key": DefaultSyncAPIKey,
"X-Sync-API-Key": SyncAPIKey(),
"X-Sync-Node": "e2e-assert",
},
&resp,


+ 32
- 11
test/e2e/helpers/cluster.go Wyświetl plik

@@ -19,19 +19,40 @@ const (

DefaultCNPort = 3100
DefaultOVPort = 3101
DefaultSessionSecret = "user-migration-e2e-session-secret"
DefaultCNDBFileName = "cn.db"
DefaultOVDBFileName = "ov.db"
DefaultCNLogFileName = "cn.log"
DefaultOVLogFileName = "ov.log"
DefaultCNSessionName = "cn_session"
DefaultOVSessionName = "ov_session"
DefaultSyncAPIKey = "e2e-sync-key"
DefaultRootUsername = "root"
DefaultRootPassword = "Password123!"
defaultNodeReadyTimout = 45 * time.Second
)

// The e2e cluster runs on loopback only; each secret below resolves from the
// environment first and falls back to a local-only value shared with the
// Playwright fixtures (see web/e2e/fixtures/cluster.ts).
func RootPassword() string {
if password := strings.TrimSpace(os.Getenv("E2E_ROOT_PASSWORD")); password != "" {
return password
}
return "e2e-local-root-pass"
}

func SessionSecret() string {
if secret := strings.TrimSpace(os.Getenv("E2E_SESSION_SECRET")); secret != "" {
return secret
}
return "e2e-local-session-secret"
}

func SyncAPIKey() string {
if key := strings.TrimSpace(os.Getenv("E2E_SYNC_API_KEY")); key != "" {
return key
}
return "e2e-local-sync-key"
}

type NodeProcess struct {
Name string
Port int
@@ -115,11 +136,11 @@ func PrepareEnvironment(repoRoot, artifactRoot, binaryPath, scenarioName string)
return nil, err
}

if err := SetupRoot(cnClient, DefaultRootUsername, DefaultRootPassword); err != nil {
if err := SetupRoot(cnClient, DefaultRootUsername, RootPassword()); err != nil {
_ = cluster.Stop()
return nil, fmt.Errorf("setup CN root: %w", err)
}
if err := SetupRoot(ovClient, DefaultRootUsername, DefaultRootPassword); err != nil {
if err := SetupRoot(ovClient, DefaultRootUsername, RootPassword()); err != nil {
_ = cluster.Stop()
return nil, fmt.Errorf("setup OV root: %w", err)
}
@@ -151,25 +172,25 @@ func PrepareEnvironment(repoRoot, artifactRoot, binaryPath, scenarioName string)
return nil, err
}

if err := Login(cnClient, DefaultRootUsername, DefaultRootPassword); err != nil {
if err := Login(cnClient, DefaultRootUsername, RootPassword()); err != nil {
_ = cluster.Stop()
return nil, fmt.Errorf("login CN root: %w", err)
}
if err := Login(ovClient, DefaultRootUsername, DefaultRootPassword); err != nil {
if err := Login(ovClient, DefaultRootUsername, RootPassword()); err != nil {
_ = cluster.Stop()
return nil, fmt.Errorf("login OV root: %w", err)
}

if err := ConfigureCNRegionSync(cnClient, cluster.CN.BaseURL, cluster.OV.BaseURL, DefaultSyncAPIKey); err != nil {
if err := ConfigureCNRegionSync(cnClient, cluster.CN.BaseURL, cluster.OV.BaseURL, SyncAPIKey()); err != nil {
_ = cluster.Stop()
return nil, fmt.Errorf("configure CN region sync: %w", err)
}
if err := ConfigureOVRegionSync(ovClient, cluster.CN.BaseURL, DefaultSyncAPIKey); err != nil {
if err := ConfigureOVRegionSync(ovClient, cluster.CN.BaseURL, SyncAPIKey()); err != nil {
_ = cluster.Stop()
return nil, fmt.Errorf("configure OV region sync: %w", err)
}

if err := WaitOptionApplied(cluster.OV.BaseURL, DefaultSyncAPIKey, 5, 20*time.Second); err != nil {
if err := WaitOptionApplied(cluster.OV.BaseURL, SyncAPIKey(), 5, 20*time.Second); err != nil {
_ = cluster.Stop()
return nil, fmt.Errorf("wait OV migration API ready: %w", err)
}
@@ -272,7 +293,7 @@ func (c *Cluster) startNode(name string, port int, dbPath, sessionName, nodeType
cmd.Env = mergeEnv(os.Environ(), map[string]string{
"PORT": fmt.Sprintf("%d", port),
"SQLITE_PATH": dbPath,
"SESSION_SECRET": DefaultSessionSecret,
"SESSION_SECRET": SessionSecret(),
"SESSION_NAME": sessionName,
"SESSION_SECURE": "false",
"SESSION_SAMESITE": "strict",


+ 3
- 3
test/e2e/user_migration_test.go Wyświetl plik

@@ -224,7 +224,7 @@ func TestUserMigrationE2E_ScanPagination(t *testing.T) {
func TestUserMigrationE2E_RootPermission(t *testing.T) {
env := newEnvironment(t)

adminClient := createAndLoginManagedUser(t, env.Cluster.CN.BaseURL, env.CNClient, "migration-admin", helpers.DefaultRootPassword, common.RoleAdminUser)
adminClient := createAndLoginManagedUser(t, env.Cluster.CN.BaseURL, env.CNClient, "migration-admin", helpers.RootPassword(), common.RoleAdminUser)

var listResp simpleResponse
require.NoError(t, adminClient.GetJSON("/api/user-migrations/batches", 200, &listResp))
@@ -707,7 +707,7 @@ func createManagedUsers(t *testing.T, client *helpers.APIClient, prefix string,
var resp simpleResponse
require.NoError(t, client.PostJSON("/api/user/", map[string]any{
"username": username,
"password": helpers.DefaultRootPassword,
"password": helpers.RootPassword(),
"display_name": displayName,
"email": email,
"role": role,
@@ -775,7 +775,7 @@ func convertRemoteUserToSynced(t *testing.T, ovBaseURL string, sourceUserID, rem
},
200,
map[string]string{
"X-Sync-API-Key": helpers.DefaultSyncAPIKey,
"X-Sync-API-Key": helpers.SyncAPIKey(),
"X-Sync-Node": "e2e-test",
},
&resp,


+ 2
- 2
web/e2e/fixtures/cluster.ts Wyświetl plik

@@ -33,9 +33,9 @@ export const clusterRunDir = path.join(artifactRoot, 'runs', 'playwright-ui');
export const cnBaseURL = 'http://127.0.0.1:3100';
export const ovBaseURL = 'http://127.0.0.1:3101';
export const rootUsername = 'root';
export const rootPassword = 'Password123!';
export const rootPassword = process.env.E2E_ROOT_PASSWORD ?? 'e2e-local-root-pass';
export const adminUsername = 'playwright-admin';
export const adminPassword = 'Password123!';
export const adminPassword = process.env.E2E_ADMIN_PASSWORD ?? 'e2e-local-admin-pass';

export type ClusterMetadata = {
serverPid: number;


+ 5
- 2
web/e2e/utils/process.js Wyświetl plik

@@ -66,10 +66,13 @@ function waitForExit(child, timeoutMs) {
}

function killWindowsProcessTree(pid) {
if (!pid) return Promise.resolve();
const numericPid = Number(pid);
if (!Number.isInteger(numericPid) || numericPid <= 0) {
return Promise.resolve();
}

return new Promise((resolve) => {
const killer = spawn('taskkill', ['/PID', String(pid), '/T', '/F'], {
const killer = spawn('taskkill', ['/PID', String(numericPid), '/T', '/F'], {
stdio: 'ignore',
});



Ładowanie…
Anuluj
Zapisz