diff --git a/.changelog/1459.txt b/.changelog/1459.txt new file mode 100644 index 00000000000..b7d1a74e418 --- /dev/null +++ b/.changelog/1459.txt @@ -0,0 +1,3 @@ +```release-note:enhancement +page_shield: added support for page shield +``` \ No newline at end of file diff --git a/page_shield.go b/page_shield.go new file mode 100644 index 00000000000..d05c9497224 --- /dev/null +++ b/page_shield.go @@ -0,0 +1,76 @@ +package cloudflare + +import ( + "context" + "fmt" + "net/http" + + "github.com/goccy/go-json" +) + +// PageShield represents the page shield object minus any timestamps. +type PageShield struct { + Enabled *bool `json:"enabled,omitempty"` + UseCloudflareReportingEndpoint *bool `json:"use_cloudflare_reporting_endpoint,omitempty"` + UseConnectionURLPath *bool `json:"use_connection_url_path,omitempty"` +} + +type UpdatePageShieldSettingsParams struct { + Enabled *bool `json:"enabled,omitempty"` + UseCloudflareReportingEndpoint *bool `json:"use_cloudflare_reporting_endpoint,omitempty"` + UseConnectionURLPath *bool `json:"use_connection_url_path,omitempty"` +} + +// PageShieldSettings represents the page shield settings for a zone. +type PageShieldSettings struct { + PageShield + UpdatedAt string `json:"updated_at"` +} + +// PageShieldSettingsResponse represents the response from the page shield settings endpoint. +type PageShieldSettingsResponse struct { + PageShield PageShieldSettings `json:"result"` + Response +} + +type GetPageShieldSettingsParams struct{} + +// GetPageShieldSettings returns the page shield settings for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-get-page-shield-settings +func (api *API) GetPageShieldSettings(ctx context.Context, rc *ResourceContainer, params GetPageShieldSettingsParams) (*PageShieldSettingsResponse, error) { + uri := fmt.Sprintf("/zones/%s/page_shield", rc.Identifier) + + res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) + if err != nil { + return nil, err + } + + var psResponse PageShieldSettingsResponse + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return &psResponse, nil +} + +// UpdatePageShieldSettings updates the page shield settings for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-update-page-shield-settings +func (api *API) UpdatePageShieldSettings(ctx context.Context, rc *ResourceContainer, params UpdatePageShieldSettingsParams) (*PageShieldSettingsResponse, error) { + uri := fmt.Sprintf("/zones/%s/page_shield", rc.Identifier) + + res, err := api.makeRequestContext(ctx, http.MethodPut, uri, params) + if err != nil { + return nil, err + } + + var psResponse PageShieldSettingsResponse + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return &psResponse, nil +} diff --git a/page_shield_connections.go b/page_shield_connections.go new file mode 100644 index 00000000000..90465127699 --- /dev/null +++ b/page_shield_connections.go @@ -0,0 +1,88 @@ +package cloudflare + +import ( + "context" + "fmt" + "net/http" + + "github.com/goccy/go-json" +) + +// ListPageShieldConnectionsParams represents parameters for a page shield connection request. +type ListPageShieldConnectionsParams struct { + Direction string `url:"direction"` + ExcludeCdnCgi *bool `url:"exclude_cdn_cgi,omitempty"` + ExcludeUrls string `url:"exclude_urls"` + Export string `url:"export"` + Hosts string `url:"hosts"` + OrderBy string `url:"order_by"` + Page string `url:"page"` + PageURL string `url:"page_url"` + PerPage int `url:"per_page"` + PrioritizeMalicious *bool `url:"prioritize_malicious,omitempty"` + Status string `url:"status"` + URLs string `url:"urls"` +} + +// PageShieldConnection represents a page shield connection. +type PageShieldConnection struct { + AddedAt string `json:"added_at"` + DomainReportedMalicious *bool `json:"domain_reported_malicious,omitempty"` + FirstPageURL string `json:"first_page_url"` + FirstSeenAt string `json:"first_seen_at"` + Host string `json:"host"` + ID string `json:"id"` + LastSeenAt string `json:"last_seen_at"` + PageURLs []string `json:"page_urls"` + URL string `json:"url"` + URLContainsCdnCgiPath *bool `json:"url_contains_cdn_cgi_path,omitempty"` +} + +// ListPageShieldConnectionsResponse represents the response from the list page shield connections endpoint. +type ListPageShieldConnectionsResponse struct { + Result []PageShieldConnection `json:"result"` + Response + ResultInfo `json:"result_info"` +} + +// ListPageShieldConnections lists all page shield connections for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-list-page-shield-connections +func (api *API) ListPageShieldConnections(ctx context.Context, rc *ResourceContainer, params ListPageShieldConnectionsParams) ([]PageShieldConnection, ResultInfo, error) { + path := fmt.Sprintf("/zones/%s/page_shield/connections", rc.Identifier) + + uri := buildURI(path, params) + + res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) + if err != nil { + return nil, ResultInfo{}, err + } + + var psResponse ListPageShieldConnectionsResponse + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return psResponse.Result, psResponse.ResultInfo, nil +} + +// GetPageShieldConnection gets a page shield connection for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-get-a-page-shield-connection +func (api *API) GetPageShieldConnection(ctx context.Context, rc *ResourceContainer, connectionID string) (*PageShieldConnection, error) { + path := fmt.Sprintf("/zones/%s/page_shield/connections/%s", rc.Identifier, connectionID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + var psResponse PageShieldConnection + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return &psResponse, nil +} diff --git a/page_shield_connections_test.go b/page_shield_connections_test.go new file mode 100644 index 00000000000..42e78cc6310 --- /dev/null +++ b/page_shield_connections_test.go @@ -0,0 +1,90 @@ +package cloudflare + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/goccy/go-json" + "github.com/stretchr/testify/assert" +) + +// Mock data for PageShieldConnections. +var mockPageShieldConnections = []PageShieldConnection{ + { + AddedAt: "2021-08-18T10:51:10.09615Z", + DomainReportedMalicious: BoolPtr(false), + FirstPageURL: "blog.cloudflare.com/page", + FirstSeenAt: "2021-08-18T10:51:08Z", + Host: "blog.cloudflare.com", + ID: "c9ef84a6bf5e47138c75d95e2f933e8f", + LastSeenAt: "2021-09-02T09:57:54Z", + PageURLs: []string{"blog.cloudflare.com/page1", "blog.cloudflare.com/page2"}, + URL: "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js", + URLContainsCdnCgiPath: BoolPtr(false), + }, + { + AddedAt: "2021-09-18T10:51:10.09615Z", + DomainReportedMalicious: BoolPtr(false), + FirstPageURL: "blog.cloudflare.com/page02", + FirstSeenAt: "2021-08-18T10:51:08Z", + Host: "blog.cloudflare.com", + ID: "c9ef84a6bf5e47138c75d95e2f933e8f", + LastSeenAt: "2021-09-02T09:57:54Z", + PageURLs: []string{"blog.cloudflare.com/page1", "blog.cloudflare.com/page2"}, + URL: "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js", + URLContainsCdnCgiPath: BoolPtr(false), + }, + { + AddedAt: "2021-10-18T10:51:10.09615Z", + DomainReportedMalicious: BoolPtr(false), + FirstPageURL: "blog.cloudflare.com/page03", + FirstSeenAt: "2021-08-18T10:51:08Z", + Host: "blog.cloudflare.com", + ID: "c9ef84a6bf5e47138c75d95e2f933e8f", + LastSeenAt: "2021-09-02T09:57:54Z", + PageURLs: []string{"blog.cloudflare.com/page1", "blog.cloudflare.com/page2"}, + URL: "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js", + URLContainsCdnCgiPath: BoolPtr(false), + }, +} + +func TestListPageShieldConnections(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/page_shield/connections", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + response := ListPageShieldConnectionsResponse{ + Result: mockPageShieldConnections, + } + err := json.NewEncoder(w).Encode(response) + if err != nil { + t.Fatal(err) + } + }) + result, _, err := client.ListPageShieldConnections(context.Background(), ZoneIdentifier(testZoneID), ListPageShieldConnectionsParams{}) + assert.NoError(t, err) + assert.Equal(t, mockPageShieldConnections, result) +} + +func TestGetPageShieldConnection(t *testing.T) { + setup() + defer teardown() + + connectionID := "c9ef84a6bf5e47138c75d95e2f933e8f" //nolint + mux.HandleFunc(fmt.Sprintf("/zones/"+testZoneID+"/page_shield/connections/%s", connectionID), func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + response := mockPageShieldConnections[0] // Assuming it's the first mock connection + err := json.NewEncoder(w).Encode(response) + if err != nil { + t.Fatal(err) + } + }) + result, err := client.GetPageShieldConnection(context.Background(), ZoneIdentifier(testZoneID), connectionID) + assert.NoError(t, err) + assert.Equal(t, &mockPageShieldConnections[0], result) +} diff --git a/page_shield_policies.go b/page_shield_policies.go new file mode 100644 index 00000000000..29ce64ad0e2 --- /dev/null +++ b/page_shield_policies.go @@ -0,0 +1,140 @@ +package cloudflare + +import ( + "context" + "fmt" + "net/http" + + "github.com/goccy/go-json" +) + +// PageShieldPolicy represents a page shield policy. +type PageShieldPolicy struct { + Action string `json:"action"` + Description string `json:"description"` + Enabled *bool `json:"enabled,omitempty"` + Expression string `json:"expression"` + ID string `json:"id"` + Value string `json:"value"` +} + +type CreatePageShieldPolicyParams struct { + Action string `json:"action"` + Description string `json:"description"` + Enabled *bool `json:"enabled,omitempty"` + Expression string `json:"expression"` + ID string `json:"id"` + Value string `json:"value"` +} + +type UpdatePageShieldPolicyParams struct { + Action string `json:"action"` + Description string `json:"description"` + Enabled *bool `json:"enabled,omitempty"` + Expression string `json:"expression"` + ID string `json:"id"` + Value string `json:"value"` +} + +// ListPageShieldPoliciesResponse represents the response from the list page shield policies endpoint. +type ListPageShieldPoliciesResponse struct { + Result []PageShieldPolicy `json:"result"` + Response + ResultInfo `json:"result_info"` +} + +type ListPageShieldPoliciesParams struct{} + +// ListPageShieldPolicies lists all page shield policies for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-list-page-shield-policies +func (api *API) ListPageShieldPolicies(ctx context.Context, rc *ResourceContainer, params ListPageShieldPoliciesParams) ([]PageShieldPolicy, ResultInfo, error) { + path := fmt.Sprintf("/zones/%s/page_shield/policies", rc.Identifier) + + res, err := api.makeRequestContext(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, ResultInfo{}, err + } + + var psResponse ListPageShieldPoliciesResponse + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return psResponse.Result, psResponse.ResultInfo, nil +} + +// CreatePageShieldPolicy creates a page shield policy for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-create-page-shield-policy +func (api *API) CreatePageShieldPolicy(ctx context.Context, rc *ResourceContainer, params CreatePageShieldPolicyParams) (*PageShieldPolicy, error) { + path := fmt.Sprintf("/zones/%s/page_shield/policies", rc.Identifier) + + res, err := api.makeRequestContext(ctx, http.MethodPost, path, params) + if err != nil { + return nil, err + } + + var psResponse PageShieldPolicy + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return &psResponse, nil +} + +// DeletePageShieldPolicy deletes a page shield policy for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-delete-page-shield-policy +func (api *API) DeletePageShieldPolicy(ctx context.Context, rc *ResourceContainer, policyID string) error { + path := fmt.Sprintf("/zones/%s/page_shield/policies/%s", rc.Identifier, policyID) + + _, err := api.makeRequestContext(ctx, http.MethodDelete, path, nil) + if err != nil { + return err + } + + return nil +} + +// GetPageShieldPolicy gets a page shield policy for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-get-page-shield-policy +func (api *API) GetPageShieldPolicy(ctx context.Context, rc *ResourceContainer, policyID string) (*PageShieldPolicy, error) { + path := fmt.Sprintf("/zones/%s/page_shield/policies/%s", rc.Identifier, policyID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, err + } + + var psResponse PageShieldPolicy + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return &psResponse, nil +} + +// UpdatePageShieldPolicy updates a page shield policy for a zone. +// +// API documentation: https://developers.cloudflare.com/api/operations/page-shield-update-page-shield-policy +func (api *API) UpdatePageShieldPolicy(ctx context.Context, rc *ResourceContainer, params UpdatePageShieldPolicyParams) (*PageShieldPolicy, error) { + path := fmt.Sprintf("/zones/%s/page_shield/policies/%s", rc.Identifier, params.ID) + + res, err := api.makeRequestContext(ctx, http.MethodPut, path, params) + if err != nil { + return nil, err + } + + var psResponse PageShieldPolicy + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return &psResponse, nil +} diff --git a/page_shield_policies_test.go b/page_shield_policies_test.go new file mode 100644 index 00000000000..239c4639d09 --- /dev/null +++ b/page_shield_policies_test.go @@ -0,0 +1,151 @@ +package cloudflare + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/goccy/go-json" + "github.com/stretchr/testify/assert" +) + +var mockPageShieldPolicies = []PageShieldPolicy{ + { + Action: "allow", + Description: "Checkout page CSP policy", + Enabled: BoolPtr(true), + Expression: "ends_with(http.request.uri.path, \"/checkout\")", + ID: "c9ef84a6bf5e47138c75d95e2f933e8f", + Value: "script-src 'none';", + }, + { + Action: "allow", + Description: "Checkout page CSP policy", + Enabled: BoolPtr(true), + Expression: "ends_with(http.request.uri.path, \"/login\")", + ID: "c9ef84a6bf5e47138c75d95e2f933e82", + Value: "script-src 'none';", + }, + { + Action: "allow", + Description: "Checkout page CSP policy", + Enabled: BoolPtr(true), + Expression: "ends_with(http.request.uri.path, \"/logout\")", + ID: "c9ef84a6bf5e47138c75d95e2f933e83", + Value: "script-src 'none';", + }, +} + +func TestListPageShieldPolicies(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/page_shield/policies", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + response := ListPageShieldPoliciesResponse{ + Result: mockPageShieldPolicies, + } + err := json.NewEncoder(w).Encode(response) + if err != nil { + t.Fatal(err) + } + }) + result, _, err := client.ListPageShieldPolicies(context.Background(), ZoneIdentifier(testZoneID), ListPageShieldPoliciesParams{}) + assert.NoError(t, err) + assert.Equal(t, mockPageShieldPolicies, result) +} + +func TestCreatePageShieldPolicy(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/page_shield/policies", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPost, r.Method, "Expected method 'POST', got %s", r.Method) + w.Header().Set("content-type", "application/json") + var params PageShieldPolicy + err := json.NewDecoder(r.Body).Decode(¶ms) + assert.NoError(t, err) + params.ID = "newPolicyID" + err = json.NewEncoder(w).Encode(params) + if err != nil { + t.Fatal(err) + } + }) + + newPolicy := CreatePageShieldPolicyParams{ + Action: "block", + Description: "New policy", + Enabled: BoolPtr(true), + Expression: "ends_with(http.request.uri.path, \"/new\")", + Value: "script-src 'self';", + } + result, err := client.CreatePageShieldPolicy(context.Background(), ZoneIdentifier(testZoneID), newPolicy) + assert.NoError(t, err) + assert.Equal(t, "newPolicyID", result.ID) +} + +func TestDeletePageShieldPolicy(t *testing.T) { + setup() + defer teardown() + + policyID := "c9ef84a6bf5e47138c75d95e2f933e8f" + mux.HandleFunc(fmt.Sprintf("/zones/"+testZoneID+"/page_shield/policies/%s", policyID), func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodDelete, r.Method, "Expected method 'DELETE', got %s", r.Method) + w.WriteHeader(http.StatusOK) // Assuming successful deletion returns 200 OK + }) + err := client.DeletePageShieldPolicy(context.Background(), ZoneIdentifier(testZoneID), policyID) + assert.NoError(t, err) +} + +func TestGetPageShieldPolicy(t *testing.T) { + setup() + defer teardown() + + policyID := "c9ef84a6bf5e47138c75d95e2f933e8f" + mux.HandleFunc(fmt.Sprintf("/zones/"+testZoneID+"/page_shield/policies/%s", policyID), func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + err := json.NewEncoder(w).Encode(mockPageShieldPolicies[0]) // Assuming the first mock policy + if err != nil { + t.Fatal(err) + } + }) + result, err := client.GetPageShieldPolicy(context.Background(), ZoneIdentifier(testZoneID), policyID) + assert.NoError(t, err) + assert.Equal(t, &mockPageShieldPolicies[0], result) +} + +func TestUpdatePageShieldPolicy(t *testing.T) { + setup() + defer teardown() + + policyID := "c9ef84a6bf5e47138c75d95e2f933e8f" + mux.HandleFunc(fmt.Sprintf("/zones/"+testZoneID+"/page_shield/policies/%s", policyID), func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + + var params PageShieldPolicy + err := json.NewDecoder(r.Body).Decode(¶ms) + assert.NoError(t, err) + params.ID = policyID + err = json.NewEncoder(w).Encode(params) + if err != nil { + t.Fatal(err) + } + }) + + updatedPolicy := UpdatePageShieldPolicyParams{ + ID: policyID, + Action: "block", + Description: "Updated policy", + Enabled: BoolPtr(false), + Expression: "ends_with(http.request.uri.path, \"/updated\")", + Value: "script-src 'self';", + } + result, err := client.UpdatePageShieldPolicy(context.Background(), ZoneIdentifier(testZoneID), updatedPolicy) + assert.NoError(t, err) + assert.Equal(t, policyID, result.ID) + assert.Equal(t, "Updated policy", result.Description) +} diff --git a/page_shield_scripts.go b/page_shield_scripts.go new file mode 100644 index 00000000000..09559b59f91 --- /dev/null +++ b/page_shield_scripts.go @@ -0,0 +1,107 @@ +package cloudflare + +import ( + "context" + "fmt" + "net/http" + + "github.com/goccy/go-json" +) + +// PageShieldScript represents a Page Shield script. +type PageShieldScript struct { + AddedAt string `json:"added_at"` + DomainReportedMalicious *bool `json:"domain_reported_malicious,omitempty"` + FetchedAt string `json:"fetched_at"` + FirstPageURL string `json:"first_page_url"` + FirstSeenAt string `json:"first_seen_at"` + Hash string `json:"hash"` + Host string `json:"host"` + ID string `json:"id"` + JSIntegrityScore int `json:"js_integrity_score"` + LastSeenAt string `json:"last_seen_at"` + PageURLs []string `json:"page_urls"` + URL string `json:"url"` + URLContainsCdnCgiPath *bool `json:"url_contains_cdn_cgi_path,omitempty"` +} + +// ListPageShieldScriptsParams represents a PageShield Script request parameters. +// +// API reference: https://developers.cloudflare.com/api/operations/page-shield-list-page-shield-scripts#Query-Parameters +type ListPageShieldScriptsParams struct { + Direction string `url:"direction"` + ExcludeCdnCgi *bool `url:"exclude_cdn_cgi,omitempty"` + ExcludeDuplicates *bool `url:"exclude_duplicates,omitempty"` + ExcludeUrls string `url:"exclude_urls"` + Export string `url:"export"` + Hosts string `url:"hosts"` + OrderBy string `url:"order_by"` + Page string `url:"page"` + PageURL string `url:"page_url"` + PerPage int `url:"per_page"` + PrioritizeMalicious *bool `url:"prioritize_malicious,omitempty"` + Status string `url:"status"` + URLs string `url:"urls"` +} + +// PageShieldScriptsResponse represents the response from the PageShield Script API. +type PageShieldScriptsResponse struct { + Results []PageShieldScript `json:"result"` + Response + ResultInfo `json:"result_info"` +} + +// PageShieldScriptResponse represents the response from the PageShield Script API. +type PageShieldScriptResponse struct { + Result PageShieldScript `json:"result"` + Versions []PageShieldScriptVersion `json:"versions"` +} + +// PageShieldScriptVersion represents a Page Shield script version. +type PageShieldScriptVersion struct { + FetchedAt string `json:"fetched_at"` + Hash string `json:"hash"` + JSIntegrityScore int `json:"js_integrity_score"` +} + +// ListPageShieldScripts returns a list of PageShield Scripts. +// +// API reference: https://developers.cloudflare.com/api/operations/page-shield-list-page-shield-scripts +func (api *API) ListPageShieldScripts(ctx context.Context, rc *ResourceContainer, params ListPageShieldScriptsParams) ([]PageShieldScript, ResultInfo, error) { + path := fmt.Sprintf("/zones/%s/page_shield/scripts", rc.Identifier) + + uri := buildURI(path, params) + + res, err := api.makeRequestContext(ctx, http.MethodGet, uri, nil) + if err != nil { + return nil, ResultInfo{}, err + } + + var psResponse PageShieldScriptsResponse + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, ResultInfo{}, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return psResponse.Results, psResponse.ResultInfo, nil +} + +// GetPageShieldScript returns a PageShield Script. +// +// API reference: https://developers.cloudflare.com/api/operations/page-shield-get-a-page-shield-script +func (api *API) GetPageShieldScript(ctx context.Context, rc *ResourceContainer, scriptID string) (*PageShieldScript, []PageShieldScriptVersion, error) { + path := fmt.Sprintf("/zones/%s/page_shield/scripts/%s", rc.Identifier, scriptID) + + res, err := api.makeRequestContext(ctx, http.MethodGet, path, nil) + if err != nil { + return nil, nil, err + } + + var psResponse PageShieldScriptResponse + err = json.Unmarshal(res, &psResponse) + if err != nil { + return nil, nil, fmt.Errorf("%s: %w", errUnmarshalError, err) + } + + return &psResponse.Result, psResponse.Versions, nil +} diff --git a/page_shield_scripts_test.go b/page_shield_scripts_test.go new file mode 100644 index 00000000000..d6d430dcdce --- /dev/null +++ b/page_shield_scripts_test.go @@ -0,0 +1,78 @@ +package cloudflare + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/goccy/go-json" + "github.com/stretchr/testify/assert" +) + +var mockPageShieldScripts = []PageShieldScript{ + { + AddedAt: "2021-08-18T10:51:10.09615Z", + DomainReportedMalicious: BoolPtr(false), + FetchedAt: "2021-09-02T10:17:54Z", + FirstPageURL: "blog.cloudflare.com/page", + FirstSeenAt: "2021-08-18T10:51:08Z", + Hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + Host: "blog.cloudflare.com", + ID: "c9ef84a6bf5e47138c75d95e2f933e8f", + JSIntegrityScore: 10, + LastSeenAt: "2021-09-02T09:57:54Z", + PageURLs: []string{"blog.cloudflare.com/page1", "blog.cloudflare.com/page2"}, + URL: "https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.6.0/js/bootstrap.min.js", + URLContainsCdnCgiPath: BoolPtr(false), + }, +} + +var mockVersions = []PageShieldScriptVersion{ + { + FetchedAt: "2021-09-02T10:17:54Z", + Hash: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + JSIntegrityScore: 10, + }, +} + +func TestListPageShieldScripts(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/page_shield/scripts", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + response := PageShieldScriptsResponse{Results: mockPageShieldScripts} + err := json.NewEncoder(w).Encode(response) + if err != nil { + t.Fatal(err) + } + }) + result, _, err := client.ListPageShieldScripts(context.Background(), ZoneIdentifier(testZoneID), ListPageShieldScriptsParams{}) + assert.NoError(t, err) + assert.Equal(t, mockPageShieldScripts, result) +} + +func TestGetPageShieldScript(t *testing.T) { + setup() + defer teardown() + + scriptID := "c9ef84a6bf5e47138c75d95e2f933e8f" + mux.HandleFunc(fmt.Sprintf("/zones/"+testZoneID+"/page_shield/scripts/%s", scriptID), func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + response := PageShieldScriptResponse{ + Result: mockPageShieldScripts[0], + Versions: mockVersions, + } + err := json.NewEncoder(w).Encode(response) + if err != nil { + t.Fatal(err) + } + }) + result, versions, err := client.GetPageShieldScript(context.Background(), ZoneIdentifier(testZoneID), scriptID) + assert.NoError(t, err) + assert.Equal(t, &mockPageShieldScripts[0], result) + assert.Equal(t, mockVersions, versions) +} diff --git a/page_shield_test.go b/page_shield_test.go new file mode 100644 index 00000000000..a41e51e221b --- /dev/null +++ b/page_shield_test.go @@ -0,0 +1,77 @@ +package cloudflare + +import ( + "context" + "net/http" + "testing" + + "github.com/goccy/go-json" + "github.com/stretchr/testify/assert" +) + +// Mock PageShieldSettings data. +var mockPageShieldSettings = PageShieldSettings{ + PageShield: PageShield{ + Enabled: BoolPtr(true), + UseCloudflareReportingEndpoint: BoolPtr(true), + UseConnectionURLPath: BoolPtr(true), + }, + UpdatedAt: "2022-10-12T17:56:52.083582+01:00", +} + +func TestGetPageShieldSettings(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/page_shield", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodGet, r.Method, "Expected method 'GET', got %s", r.Method) + w.Header().Set("content-type", "application/json") + response := PageShieldSettingsResponse{ + PageShield: mockPageShieldSettings, + } + err := json.NewEncoder(w).Encode(response) + if err != nil { + t.Fatal(err) + } + }) + + result, err := client.GetPageShieldSettings(context.Background(), ZoneIdentifier(testZoneID), GetPageShieldSettingsParams{}) + assert.NoError(t, err) + assert.Equal(t, &PageShieldSettingsResponse{PageShield: mockPageShieldSettings}, result) +} + +func TestUpdatePageShieldSettings(t *testing.T) { + setup() + defer teardown() + + mux.HandleFunc("/zones/"+testZoneID+"/page_shield", func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, http.MethodPut, r.Method, "Expected method 'PUT', got %s", r.Method) + w.Header().Set("content-type", "application/json") + + var params PageShield + err := json.NewDecoder(r.Body).Decode(¶ms) + assert.NoError(t, err) + + response := PageShieldSettingsResponse{ + PageShield: PageShieldSettings{ + PageShield: params, + UpdatedAt: "2022-10-13T10:00:00.000Z", + }, + } + err = json.NewEncoder(w).Encode(response) + if err != nil { + t.Fatal(err) + } + }) + + newSettings := UpdatePageShieldSettingsParams{ + Enabled: BoolPtr(false), + UseCloudflareReportingEndpoint: BoolPtr(false), + UseConnectionURLPath: BoolPtr(false), + } + result, err := client.UpdatePageShieldSettings(context.Background(), ZoneIdentifier(testZoneID), newSettings) + assert.NoError(t, err) + assert.Equal(t, false, *result.PageShield.Enabled) + assert.Equal(t, false, *result.PageShield.UseCloudflareReportingEndpoint) + assert.Equal(t, false, *result.PageShield.UseConnectionURLPath) +}