Skip to content
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

DXE-3169 Release/v7.5.0 #197

Merged
merged 3 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# EDGEGRID GOLANG RELEASE NOTES

## 7.5.0 (November 28, 2023)

#### FEATURES/ENHANCEMENTS:

* APPSEC
* Added `ASNControls` field to `UpdateIPGeoRequest` and `IPGeoFirewall` structs to support firewall blocking by ASN client lists

* BOTMAN
* Added API support for Custom Code - read and update

## 7.4.0 (October 24, 2023)

#### FEATURES/ENHANCEMENTS:
Expand All @@ -12,7 +22,7 @@

* IAM
* Phone number is no longer required for IAM user for `CreateUser` and `UpdateUserInfo` methods

## 7.3.0 (September 19, 2023)

#### FEATURES/ENHANCEMENTS:
Expand Down
9 changes: 8 additions & 1 deletion pkg/appsec/ip_geo.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@ type (
BlockedIPNetworkLists *IPGeoNetworkLists `json:"blockedIPNetworkLists,omitempty"`
}

// IPGeoIPControls is used to specify IP or GEO network lists to be blocked or allowed.
// IPGeoASNControls is used to specify ASN network lists to be blocked.
IPGeoASNControls struct {
BlockedIPNetworkLists *IPGeoNetworkLists `json:"blockedIPNetworkLists,omitempty"`
}

// IPGeoIPControls is used to specify IP, GEO or ASN network lists to be blocked or allowed.
IPGeoIPControls struct {
AllowedIPNetworkLists *IPGeoNetworkLists `json:"allowedIPNetworkLists,omitempty"`
BlockedIPNetworkLists *IPGeoNetworkLists `json:"blockedIPNetworkLists,omitempty"`
Expand All @@ -59,6 +64,7 @@ type (
Block string `json:"block"`
GeoControls *IPGeoGeoControls `json:"geoControls,omitempty"`
IPControls *IPGeoIPControls `json:"ipControls,omitempty"`
ASNControls *IPGeoASNControls `json:"asnControls,omitempty"`
UkraineGeoControls *UkraineGeoControl `json:"ukraineGeoControl,omitempty"`
}

Expand All @@ -67,6 +73,7 @@ type (
Block string `json:"block"`
GeoControls *IPGeoGeoControls `json:"geoControls,omitempty"`
IPControls *IPGeoIPControls `json:"ipControls,omitempty"`
ASNControls *IPGeoASNControls `json:"asnControls,omitempty"`
UkraineGeoControls *UkraineGeoControl `json:"ukraineGeoControl,omitempty"`
}

Expand Down
144 changes: 116 additions & 28 deletions pkg/appsec/ip_geo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package appsec

import (
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
Expand All @@ -15,13 +14,6 @@ import (

// Test IPGeo
func TestAppSec_GetIPGeo(t *testing.T) {

result := GetIPGeoResponse{}

respData := compactJSON(loadFixtureBytes("testdata/TestIPGeo/IPGeo.json"))
err := json.Unmarshal([]byte(respData), &result)
require.NoError(t, err)

tests := map[string]struct {
params GetIPGeoRequest
responseStatus int
Expand All @@ -36,10 +28,64 @@ func TestAppSec_GetIPGeo(t *testing.T) {
Version: 15,
PolicyID: "AAAA_81230",
},
responseStatus: http.StatusOK,
responseBody: respData,
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
expectedResponse: &result,
responseStatus: http.StatusOK,
responseBody: `{
"block": "blockSpecificIPGeo",
"asnControls": {
"blockedIPNetworkLists": {
"networkList": [
"12345_ASNTEST"
]
}
},
"geoControls": {
"blockedIPNetworkLists": {
"networkList": [
"72138_TEST1"
]
}
},
"ipControls": {
"allowedIPNetworkLists": {
"networkList": [
"56921_TEST"
]
},
"blockedIPNetworkLists": {
"networkList": [
"53712_TESTLIST123"
]
}
},
"ukraineGeoControl": {
"action": "alert"
}
}`,
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
expectedResponse: &GetIPGeoResponse{
Block: "blockSpecificIPGeo",
GeoControls: &IPGeoGeoControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"72138_TEST1"},
},
},
IPControls: &IPGeoIPControls{
AllowedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"56921_TEST"},
},
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"53712_TESTLIST123"},
},
},
ASNControls: &IPGeoASNControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"12345_ASNTEST"},
},
},
UkraineGeoControls: &UkraineGeoControl{
Action: "alert",
},
},
},
"500 internal server error": {
params: GetIPGeoRequest{
Expand Down Expand Up @@ -87,18 +133,6 @@ func TestAppSec_GetIPGeo(t *testing.T) {

// Test Update IPGeo.
func TestAppSec_UpdateIPGeo(t *testing.T) {
result := UpdateIPGeoResponse{}

respData := compactJSON(loadFixtureBytes("testdata/TestIPGeo/IPGeo.json"))
err := json.Unmarshal([]byte(respData), &result)
require.NoError(t, err)

req := UpdateIPGeoRequest{}

reqData := compactJSON(loadFixtureBytes("testdata/TestIPGeo/IPGeo.json"))
err = json.Unmarshal([]byte(reqData), &req)
require.NoError(t, err)

tests := map[string]struct {
params UpdateIPGeoRequest
responseStatus int
Expand All @@ -117,10 +151,64 @@ func TestAppSec_UpdateIPGeo(t *testing.T) {
headers: http.Header{
"Content-Type": []string{"application/json;charset=UTF-8"},
},
responseStatus: http.StatusCreated,
responseBody: respData,
expectedResponse: &result,
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
responseStatus: http.StatusCreated,
responseBody: `{
"block": "blockSpecificIPGeo",
"asnControls": {
"blockedIPNetworkLists": {
"networkList": [
"12345_ASNTEST"
]
}
},
"geoControls": {
"blockedIPNetworkLists": {
"networkList": [
"72138_TEST1"
]
}
},
"ipControls": {
"allowedIPNetworkLists": {
"networkList": [
"56921_TEST"
]
},
"blockedIPNetworkLists": {
"networkList": [
"53712_TESTLIST123"
]
}
},
"ukraineGeoControl": {
"action": "alert"
}
}`,
expectedResponse: &UpdateIPGeoResponse{
Block: "blockSpecificIPGeo",
GeoControls: &IPGeoGeoControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"72138_TEST1"},
},
},
IPControls: &IPGeoIPControls{
AllowedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"56921_TEST"},
},
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"53712_TESTLIST123"},
},
},
ASNControls: &IPGeoASNControls{
BlockedIPNetworkLists: &IPGeoNetworkLists{
NetworkList: []string{"12345_ASNTEST"},
},
},
UkraineGeoControls: &UkraineGeoControl{
Action: "alert",
},
},
expectedPath: "/appsec/v1/configs/43253/versions/15/security-policies/AAAA_81230/ip-geo-firewall",
},
"500 internal server error": {
params: UpdateIPGeoRequest{
Expand Down
25 changes: 0 additions & 25 deletions pkg/appsec/testdata/TestIPGeo/IPGeo.json

This file was deleted.

1 change: 1 addition & 0 deletions pkg/botman/botman.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type (
CustomClientSequence
CustomDefinedBot
CustomDenyAction
CustomCode
JavascriptInjection
RecategorizedAkamaiDefinedBot
ResponseAction
Expand Down
112 changes: 112 additions & 0 deletions pkg/botman/custom_code.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package botman

import (
"context"
"encoding/json"
"fmt"
"net/http"

validation "github.com/go-ozzo/ozzo-validation/v4"
)

type (
// The CustomCode interface supports retrieving and updating custom code
CustomCode interface {
GetCustomCode(ctx context.Context, params GetCustomCodeRequest) (map[string]interface{}, error)

UpdateCustomCode(ctx context.Context, params UpdateCustomCodeRequest) (map[string]interface{}, error)
}

// GetCustomCodeRequest is used to retrieve custom code
GetCustomCodeRequest struct {
ConfigID int64
Version int64
}

// UpdateCustomCodeRequest is used to modify custom code
UpdateCustomCodeRequest struct {
ConfigID int64
Version int64
JsonPayload json.RawMessage
}
)

// Validate validates a GetCustomCodeRequest.
func (v GetCustomCodeRequest) Validate() error {
return validation.Errors{
"ConfigID": validation.Validate(v.ConfigID, validation.Required),
"Version": validation.Validate(v.Version, validation.Required),
}.Filter()
}

// Validate validates an UpdateCustomCodeRequest.
func (v UpdateCustomCodeRequest) Validate() error {
return validation.Errors{
"ConfigID": validation.Validate(v.ConfigID, validation.Required),
"Version": validation.Validate(v.Version, validation.Required),
"JsonPayload": validation.Validate(v.JsonPayload, validation.Required),
}.Filter()
}

func (b *botman) GetCustomCode(ctx context.Context, params GetCustomCodeRequest) (map[string]interface{}, error) {
logger := b.Log(ctx)
logger.Debug("GetCustomCode")

if err := params.Validate(); err != nil {
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
}

uri := fmt.Sprintf(
"/appsec/v1/configs/%d/versions/%d/advanced-settings/transactional-endpoint-protection/custom-code",
params.ConfigID,
params.Version)

req, err := http.NewRequestWithContext(ctx, http.MethodGet, uri, nil)
if err != nil {
return nil, fmt.Errorf("failed to create GetCustomCode request: %w", err)
}

var result map[string]interface{}
resp, err := b.Exec(req, &result)
if err != nil {
return nil, fmt.Errorf("GetCustomCode request failed: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, b.Error(resp)
}

return result, nil
}

func (b *botman) UpdateCustomCode(ctx context.Context, params UpdateCustomCodeRequest) (map[string]interface{}, error) {
logger := b.Log(ctx)
logger.Debug("UpdateCustomCode")

if err := params.Validate(); err != nil {
return nil, fmt.Errorf("%w: %s", ErrStructValidation, err.Error())
}

putURL := fmt.Sprintf(
"/appsec/v1/configs/%d/versions/%d/advanced-settings/transactional-endpoint-protection/custom-code",
params.ConfigID,
params.Version,
)

req, err := http.NewRequestWithContext(ctx, http.MethodPut, putURL, nil)
if err != nil {
return nil, fmt.Errorf("failed to create UpdateCustomCode request: %w", err)
}

var result map[string]interface{}
resp, err := b.Exec(req, &result, params.JsonPayload)
if err != nil {
return nil, fmt.Errorf("UpdateCustomCode request failed: %w", err)
}

if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent {
return nil, b.Error(resp)
}

return result, nil
}
Loading
Loading