-
Notifications
You must be signed in to change notification settings - Fork 15
DRAFT: feature: enable manual rollback #381
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
/AzurePipelines run [GITHUB]-trident-pr-e2e |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/AzurePipelines run [GITHUB]-trident-pr-e2e |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/AzurePipelines run [GITHUB]-trident-pr-e2e |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
/AzurePipelines run [GITHUB]-trident-pr-e2e |
|
Azure Pipelines successfully started running 1 pipeline(s). |
5fc0ede to
08fe5bd
Compare
| return fmt.Errorf("failed to write host config file locally: %w", err) | ||
| } | ||
| logrus.Tracef("Putting updated Host Configuration on VM") | ||
| logrus.Tracef("Putting updated Host Configuration on VM:\n%s", string(hostConfigBytes)) |
Check failure
Code scanning / CodeQL
Clear-text logging of sensitive information High test
Sensitive data returned by an access to DebugPassword
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
The recommended fix is to prevent sensitive information—specifically, the password in testConfig.DebugPassword—from being included in logs. In this code, logging the full YAML host configuration (hostConfigBytes) will expose the password if DebugPassword is set.
To fix the issue:
- Redact or remove the password-containing field(s) from the copy being logged, or avoid logging the full config altogether.
- It's common and safe to create a copy of the
hostConfigmap and redact any sensitive fields (in this case, the password set in thescripts->postProvision->contentpath) before marshalling and logging. - This copy can then be marshaled and logged, while the original (with the sensitive data) continues to be used for all functional purposes.
To do this safely:
- Clone the
hostConfigstructure, recursively if needed. - Redact or mask (
"***REDACTED***") the password in the appropriate field. - Marshal the redacted copy and log only that.
Required changes:
- Update the logging logic on line 335 to perform the redacting before logging.
- Add a helper function to create a redacted copy of the hostConfig for logging purposes.
- No new dependencies are needed.
-
Copy modified lines R308-R402 -
Copy modified lines R430-R436
| @@ -305,6 +305,101 @@ | ||
| return validateRollbacksAvailable(testConfig, vmConfig, vmIP, expectedAvailableRollbacks, expectedFirstRollbackNeedsReboot) | ||
| } | ||
|
|
||
| // redactHostConfigForLogging returns a copy of the hostConfig with sensitive fields (like DebugPassword) redacted. | ||
| func redactHostConfigForLogging(hostConfig map[string]interface{}) map[string]interface{} { | ||
| // Deep copy the map | ||
| redacted := make(map[string]interface{}) | ||
| for k, v := range hostConfig { | ||
| redacted[k] = deepCopyWithRedaction(k, v) | ||
| } | ||
| return redacted | ||
| } | ||
|
|
||
| // Helper function to deep copy and redact as needed | ||
| func deepCopyWithRedaction(key string, value interface{}) interface{} { | ||
| if key == "scripts" { | ||
| // Redact passwords in postProvision scripts | ||
| if scriptsMap, ok := value.(map[string]interface{}); ok { | ||
| newScriptsMap := make(map[string]interface{}) | ||
| for sk, sv := range scriptsMap { | ||
| if sk == "postProvision" { | ||
| if postProvisionArr, okArr := sv.([]map[string]interface{}); okArr { | ||
| newArr := []map[string]interface{}{} | ||
| for _, elem := range postProvisionArr { | ||
| newElem := make(map[string]interface{}) | ||
| for ek, ev := range elem { | ||
| // Redact password in the "content" field of "set-password-script" | ||
| if ek == "name" && ev == "set-password-script" { | ||
| newElem[ek] = ev | ||
| // Copy all fields, but redact "content" | ||
| for nk, nv := range elem { | ||
| if nk == "content" { | ||
| newElem[nk] = "***REDACTED***" | ||
| } else { | ||
| newElem[nk] = nv | ||
| } | ||
| } | ||
| break | ||
| } | ||
| newElem[ek] = ev | ||
| } | ||
| newArr = append(newArr, newElem) | ||
| } | ||
| newScriptsMap[sk] = newArr | ||
| } else if postProvisionArr, okArr2 := sv.([]interface{}); okArr2 { | ||
| newArr := []interface{}{} | ||
| for _, elemIf := range postProvisionArr { | ||
| if elem, okElem := elemIf.(map[string]interface{}); okElem { | ||
| newElem := make(map[string]interface{}) | ||
| for ek, ev := range elem { | ||
| if ek == "name" && ev == "set-password-script" { | ||
| newElem[ek] = ev | ||
| for nk, nv := range elem { | ||
| if nk == "content" { | ||
| newElem[nk] = "***REDACTED***" | ||
| } else { | ||
| newElem[nk] = nv | ||
| } | ||
| } | ||
| break | ||
| } | ||
| newElem[ek] = ev | ||
| } | ||
| newArr = append(newArr, newElem) | ||
| } else { | ||
| newArr = append(newArr, elemIf) | ||
| } | ||
| } | ||
| newScriptsMap[sk] = newArr | ||
| } else { | ||
| newScriptsMap[sk] = sv | ||
| } | ||
| } else { | ||
| newScriptsMap[sk] = sv | ||
| } | ||
| } | ||
| return newScriptsMap | ||
| } | ||
| } | ||
| // Deep copy for nested maps | ||
| if m, ok := value.(map[string]interface{}); ok { | ||
| newMap := make(map[string]interface{}) | ||
| for nk, nv := range m { | ||
| newMap[nk] = deepCopyWithRedaction(nk, nv) | ||
| } | ||
| return newMap | ||
| } | ||
| // Deep copy for nested slices | ||
| if arr, ok := value.([]interface{}); ok { | ||
| newArr := make([]interface{}, len(arr)) | ||
| for i, v := range arr { | ||
| newArr[i] = deepCopyWithRedaction("", v) | ||
| } | ||
| return newArr | ||
| } | ||
| return value | ||
| } | ||
|
|
||
| func doUpdateTest( | ||
| testConfig stormrollbackconfig.TestConfig, | ||
| vmConfig stormvmconfig.AllVMConfig, | ||
| @@ -332,7 +427,13 @@ | ||
| if err != nil { | ||
| return fmt.Errorf("failed to write host config file locally: %w", err) | ||
| } | ||
| logrus.Tracef("Putting updated Host Configuration on VM:\n%s", string(hostConfigBytes)) | ||
| // Redact sensitive fields from the hostConfig before logging | ||
| redactedHostConfig := redactHostConfigForLogging(hostConfig) | ||
| redactedHostConfigBytes, err := yaml.Marshal(redactedHostConfig) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to marshal redacted Host Configuration: %w", err) | ||
| } | ||
| logrus.Tracef("Putting updated Host Configuration on VM:\n%s", string(redactedHostConfigBytes)) | ||
| err = stormssh.ScpUploadFile(vmConfig.VMConfig, vmIP, localTmpFile.Name(), vmHostConfigPath) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to put updated Host Configuration on VM (%w)", err) |
🔍 Description
Enable
trident rollbackto undo the last update, with options:--ab: user expects rollback will undo an a/b update, trident return error otherwise--runtime: user expects rollback will undo a runtime update, trident return error otherwise--show validation|target|chain: list rollback details