Fix path injection, sensitive attribute exposure, and error body truncation
Security fixes from audit: - Escape user-supplied strings (ext_relation_id, interface_name) with url.PathEscape before interpolating into API URL paths, preventing path traversal via crafted values like "../admin" or "foo/bar" - Mark auth token URL attributes as Sensitive in both virtfusion_user_auth_token and virtfusion_user_server_auth_token resources, since the URL embeds the signed token - Truncate raw API error response bodies to 500 bytes in error messages to prevent leaking sensitive data from verbose Laravel error responses Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
||||
"terraform-provider-virtfusion/internal/client"
|
||||
|
||||
@@ -138,7 +139,7 @@ func (r *ServerFirewallResource) Create(ctx context.Context, req resource.Create
|
||||
iface := data.InterfaceName.ValueString()
|
||||
|
||||
// Enable the firewall
|
||||
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/enable", serverID, iface), nil)
|
||||
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/enable", serverID, url.PathEscape(iface)), nil)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error enabling server firewall", err.Error())
|
||||
return
|
||||
@@ -153,7 +154,7 @@ func (r *ServerFirewallResource) Create(ctx context.Context, req resource.Create
|
||||
|
||||
if len(rules) > 0 {
|
||||
rulesReq := client.FirewallSetRulesRequest{Rules: rules}
|
||||
_, err = r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/rules", serverID, iface), rulesReq)
|
||||
_, err = r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/rules", serverID, url.PathEscape(iface)), rulesReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error setting firewall rules", err.Error())
|
||||
return
|
||||
@@ -175,7 +176,7 @@ func (r *ServerFirewallResource) Read(ctx context.Context, req resource.ReadRequ
|
||||
serverID := data.ServerID.ValueInt64()
|
||||
iface := data.InterfaceName.ValueString()
|
||||
|
||||
result, err := r.client.Get(ctx, fmt.Sprintf("/servers/%d/firewall/%s", serverID, iface))
|
||||
result, err := r.client.Get(ctx, fmt.Sprintf("/servers/%d/firewall/%s", serverID, url.PathEscape(iface)))
|
||||
if err != nil {
|
||||
var apiErr *client.APIError
|
||||
if errors.As(err, &apiErr) && apiErr.IsNotFound() {
|
||||
@@ -247,7 +248,7 @@ func (r *ServerFirewallResource) Update(ctx context.Context, req resource.Update
|
||||
}
|
||||
|
||||
rulesReq := client.FirewallSetRulesRequest{Rules: rules}
|
||||
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/rules", serverID, iface), rulesReq)
|
||||
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/rules", serverID, url.PathEscape(iface)), rulesReq)
|
||||
if err != nil {
|
||||
resp.Diagnostics.AddError("Error updating firewall rules", err.Error())
|
||||
return
|
||||
@@ -268,7 +269,7 @@ func (r *ServerFirewallResource) Delete(ctx context.Context, req resource.Delete
|
||||
serverID := data.ServerID.ValueInt64()
|
||||
iface := data.InterfaceName.ValueString()
|
||||
|
||||
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/disable", serverID, iface), nil)
|
||||
_, err := r.client.Post(ctx, fmt.Sprintf("/servers/%d/firewall/%s/disable", serverID, url.PathEscape(iface)), nil)
|
||||
if err != nil {
|
||||
var apiErr *client.APIError
|
||||
if errors.As(err, &apiErr) && apiErr.IsNotFound() {
|
||||
|
||||
Reference in New Issue
Block a user