From 47fd78915da239fbd6dc86298f94ff599851cac3 Mon Sep 17 00:00:00 2001 From: Phil Hunt Date: Mon, 17 Jun 2024 13:53:10 -0700 Subject: [PATCH] Issue #47, Updated versioning and codeql Signed-off-by: Phil Hunt --- .github/workflows/codeql-analysis.yml | 10 +- models/formats/awsCedar/amazon_cedar.go | 2 +- models/formats/gcpBind/google_bind_policy.go | 394 +++++++++---------- models/rar/policy_transformer.go | 178 ++++----- 4 files changed, 293 insertions(+), 291 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b72aa07..8e80d35 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,13 +14,15 @@ jobs: language: [ 'go' ] steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 + - name: Setup Go 1.22 + uses: actions/setup-go@v5 - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} config-file: ./.github/config/mod-config.yml - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/models/formats/awsCedar/amazon_cedar.go b/models/formats/awsCedar/amazon_cedar.go index 69d8c90..07cb711 100644 --- a/models/formats/awsCedar/amazon_cedar.go +++ b/models/formats/awsCedar/amazon_cedar.go @@ -413,7 +413,7 @@ func (c *CedarPolicyMapper) MapCedarPolicyToIdql(policy *CedarPolicy) (*hexapoli obj = mapResourceToObject(policy.Head.Resource) } ret := hexapolicy.PolicyInfo{ - Meta: hexapolicy.MetaInfo{Version: "0.6"}, + Meta: hexapolicy.MetaInfo{Version: hexapolicy.IdqlVersion}, Actions: actions, Subject: subj, Object: obj, diff --git a/models/formats/gcpBind/google_bind_policy.go b/models/formats/gcpBind/google_bind_policy.go index a055919..1ef47a1 100644 --- a/models/formats/gcpBind/google_bind_policy.go +++ b/models/formats/gcpBind/google_bind_policy.go @@ -1,251 +1,251 @@ package gcpBind import ( - "encoding/json" - "fmt" - "os" - "strings" - - "github.com/hexa-org/policy-mapper/models/conditionLangs/gcpcel" - "github.com/hexa-org/policy-mapper/pkg/hexapolicy" - "github.com/hexa-org/policy-mapper/pkg/hexapolicy/conditions" - "google.golang.org/api/iam/v1" + "encoding/json" + "fmt" + "os" + "strings" + + "github.com/hexa-org/policy-mapper/models/conditionLangs/gcpcel" + "github.com/hexa-org/policy-mapper/pkg/hexapolicy" + "github.com/hexa-org/policy-mapper/pkg/hexapolicy/conditions" + "google.golang.org/api/iam/v1" ) type GooglePolicyMapper struct { - conditionMapper gcpcel.GoogleConditionMapper + conditionMapper gcpcel.GoogleConditionMapper } /* BindAssignment is an array of GCP Bindings combined with a resource identifier. */ type BindAssignment struct { - ResourceId string `json:"resource_id"` - Bindings []iam.Binding `json:"bindings"` + ResourceId string `json:"resource_id"` + Bindings []iam.Binding `json:"bindings"` } func New(nameMap map[string]string) *GooglePolicyMapper { - return &GooglePolicyMapper{conditionMapper: gcpcel.GoogleConditionMapper{NameMapper: conditions.NewNameMapper(nameMap)}} + return &GooglePolicyMapper{conditionMapper: gcpcel.GoogleConditionMapper{NameMapper: conditions.NewNameMapper(nameMap)}} } func (m *GooglePolicyMapper) Name() string { - return "bind" + return "bind" } func (m *GooglePolicyMapper) MapBindingAssignmentsToPolicy(bindAssignments []*BindAssignment) ([]hexapolicy.PolicyInfo, error) { - var policies []hexapolicy.PolicyInfo - for _, v := range bindAssignments { - pols, err := m.MapBindingAssignmentToPolicy(*v) - if err != nil { - return nil, err - } - for _, pol := range pols { - policies = append(policies, pol) - } - } - return policies, nil + var policies []hexapolicy.PolicyInfo + for _, v := range bindAssignments { + pols, err := m.MapBindingAssignmentToPolicy(*v) + if err != nil { + return nil, err + } + for _, pol := range pols { + policies = append(policies, pol) + } + } + return policies, nil } func (m *GooglePolicyMapper) MapBindingAssignmentToPolicy(bindAssignment BindAssignment) ([]hexapolicy.PolicyInfo, error) { - var policies []hexapolicy.PolicyInfo - objectId := bindAssignment.ResourceId - for _, v := range bindAssignment.Bindings { - policy, err := m.MapBindingToPolicy(objectId, v) - if err != nil { - return nil, err - } - policies = append(policies, policy) - } - - return policies, nil + var policies []hexapolicy.PolicyInfo + objectId := bindAssignment.ResourceId + for _, v := range bindAssignment.Bindings { + policy, err := m.MapBindingToPolicy(objectId, v) + if err != nil { + return nil, err + } + policies = append(policies, policy) + } + + return policies, nil } func (m *GooglePolicyMapper) MapBindingToPolicy(objectId string, binding iam.Binding) (hexapolicy.PolicyInfo, error) { - bindingCondition := binding.Condition - if bindingCondition != nil { - condition, err := m.convertCelToCondition(binding.Condition) - if err != nil { - return hexapolicy.PolicyInfo{}, err - } - - policy := hexapolicy.PolicyInfo{ - Meta: hexapolicy.MetaInfo{Version: "0.6"}, - Actions: convertRoleToAction(binding.Role), - Subject: hexapolicy.SubjectInfo{Members: binding.Members}, - Object: hexapolicy.ObjectInfo{ResourceID: objectId}, - Condition: &condition, - } - return policy, nil - } - policy := hexapolicy.PolicyInfo{ - Meta: hexapolicy.MetaInfo{Version: "0.6"}, - Actions: convertRoleToAction(binding.Role), - Subject: hexapolicy.SubjectInfo{Members: binding.Members}, - Object: hexapolicy.ObjectInfo{ResourceID: objectId}, - } - return policy, nil + bindingCondition := binding.Condition + if bindingCondition != nil { + condition, err := m.convertCelToCondition(binding.Condition) + if err != nil { + return hexapolicy.PolicyInfo{}, err + } + + policy := hexapolicy.PolicyInfo{ + Meta: hexapolicy.MetaInfo{Version: hexapolicy.IdqlVersion}, + Actions: convertRoleToAction(binding.Role), + Subject: hexapolicy.SubjectInfo{Members: binding.Members}, + Object: hexapolicy.ObjectInfo{ResourceID: objectId}, + Condition: &condition, + } + return policy, nil + } + policy := hexapolicy.PolicyInfo{ + Meta: hexapolicy.MetaInfo{Version: hexapolicy.IdqlVersion}, + Actions: convertRoleToAction(binding.Role), + Subject: hexapolicy.SubjectInfo{Members: binding.Members}, + Object: hexapolicy.ObjectInfo{ResourceID: objectId}, + } + return policy, nil } func (m *GooglePolicyMapper) MapPolicyToBinding(policy hexapolicy.PolicyInfo) (*iam.Binding, error) { - cond := policy.Condition - var condExpr *iam.Expr - var err error - if cond != nil { - condExpr, err = m.convertPolicyCondition(policy) - } else { - condExpr = nil - } - - if err != nil { - return nil, err - } - return &iam.Binding{ - Condition: condExpr, - Members: policy.Subject.Members, - Role: convertActionToRole(policy), - }, nil + cond := policy.Condition + var condExpr *iam.Expr + var err error + if cond != nil { + condExpr, err = m.convertPolicyCondition(policy) + } else { + condExpr = nil + } + + if err != nil { + return nil, err + } + return &iam.Binding{ + Condition: condExpr, + Members: policy.Subject.Members, + Role: convertActionToRole(policy), + }, nil } func (m *GooglePolicyMapper) MapPoliciesToBindings(policies []hexapolicy.PolicyInfo) []*BindAssignment { - bindingMap := make(map[string][]iam.Binding) - - for i, policy := range policies { - binding, err := m.MapPolicyToBinding(policy) - if err != nil { - fmt.Println(err.Error()) - continue - } - key := policies[i].Object.ResourceID - - existing := bindingMap[key] - existing = append(existing, *binding) - bindingMap[key] = existing - - } - bindings := make([]*BindAssignment, len(bindingMap)) - i := 0 - for k, v := range bindingMap { - bindings[i] = &BindAssignment{ - ResourceId: k, - Bindings: v, - } - i++ - } - return bindings + bindingMap := make(map[string][]iam.Binding) + + for i, policy := range policies { + binding, err := m.MapPolicyToBinding(policy) + if err != nil { + fmt.Println(err.Error()) + continue + } + key := policies[i].Object.ResourceID + + existing := bindingMap[key] + existing = append(existing, *binding) + bindingMap[key] = existing + + } + bindings := make([]*BindAssignment, len(bindingMap)) + i := 0 + for k, v := range bindingMap { + bindings[i] = &BindAssignment{ + ResourceId: k, + Bindings: v, + } + i++ + } + return bindings } func convertActionToRole(policy hexapolicy.PolicyInfo) string { - for _, v := range policy.Actions { - action := v.ActionUri - if strings.HasPrefix(action, "gcp:") { - return action[4:] - } - return action // allow non gcp specific roles to be passed unmapped - } - return "" + for _, v := range policy.Actions { + action := v.ActionUri + if strings.HasPrefix(action, "gcp:") { + return action[4:] + } + return action // allow non gcp specific roles to be passed unmapped + } + return "" } func convertRoleToAction(role string) []hexapolicy.ActionInfo { - if role == "" { - return nil - } - return []hexapolicy.ActionInfo{{"gcp:" + role}} + if role == "" { + return nil + } + return []hexapolicy.ActionInfo{{"gcp:" + role}} } func (m *GooglePolicyMapper) convertCelToCondition(expr *iam.Expr) (conditions.ConditionInfo, error) { - return m.conditionMapper.MapProviderToCondition(expr.Expression) + return m.conditionMapper.MapProviderToCondition(expr.Expression) } func (m *GooglePolicyMapper) convertPolicyCondition(policy hexapolicy.PolicyInfo) (*iam.Expr, error) { - if policy.Condition == nil { - return nil, nil // do nothing as policy has no condition - } + if policy.Condition == nil { + return nil, nil // do nothing as policy has no condition + } - celString, err := m.conditionMapper.MapConditionToProvider(*policy.Condition) - if err != nil { - return nil, err - } + celString, err := m.conditionMapper.MapConditionToProvider(*policy.Condition) + if err != nil { + return nil, err + } - iamExpr := iam.Expr{ - Expression: celString, - } - return &iamExpr, nil + iamExpr := iam.Expr{ + Expression: celString, + } + return &iamExpr, nil } type Assignments struct { - BindAssignments []*BindAssignment + BindAssignments []*BindAssignment } // UnmarshalJSON implements json.Unmarshaler func (d *Assignments) UnmarshalJSON(b []byte) error { - if len(b) == 0 { - return fmt.Errorf("no bytes to unmarshal") - } - - switch b[0] { - case '{': - return d.unMarshallSingle(b) - case '[': - return d.unMarshallMulti(b) - } - return nil + if len(b) == 0 { + return fmt.Errorf("no bytes to unmarshal") + } + + switch b[0] { + case '{': + return d.unMarshallSingle(b) + case '[': + return d.unMarshallMulti(b) + } + return nil } func (d *Assignments) unMarshallSingle(b []byte) error { - type DetectSingle struct { - iam.Binding - BindAssignment - } - var single DetectSingle - err := json.Unmarshal(b, &single) - if err != nil { - return err - } - assignments := make([]*BindAssignment, 1) - if len(single.Bindings) != 0 { - assignments[0] = &single.BindAssignment - } else { - iamBindings := make([]iam.Binding, 1) - iamBindings[0] = single.Binding - assignments[0] = &BindAssignment{ - Bindings: iamBindings, - ResourceId: "", - } - } - d.BindAssignments = assignments - return nil + type DetectSingle struct { + iam.Binding + BindAssignment + } + var single DetectSingle + err := json.Unmarshal(b, &single) + if err != nil { + return err + } + assignments := make([]*BindAssignment, 1) + if len(single.Bindings) != 0 { + assignments[0] = &single.BindAssignment + } else { + iamBindings := make([]iam.Binding, 1) + iamBindings[0] = single.Binding + assignments[0] = &BindAssignment{ + Bindings: iamBindings, + ResourceId: "", + } + } + d.BindAssignments = assignments + return nil } func (d *Assignments) unMarshallMulti(b []byte) error { - var iamBinds []iam.Binding - var assigns []BindAssignment - - err := json.Unmarshal(b, &assigns) - if err != nil { - return err - } - if len(assigns) > 0 { - pAssigns := make([]*BindAssignment, len(assigns)) - for k := range assigns { - pAssigns[k] = &assigns[k] - } - d.BindAssignments = pAssigns - return nil - } - - err = json.Unmarshal(b, &iamBinds) - if err != nil { - return err - } - - assignments := make([]*BindAssignment, 1) - assignments[0] = &BindAssignment{ - Bindings: iamBinds, - ResourceId: "", - } - return nil + var iamBinds []iam.Binding + var assigns []BindAssignment + + err := json.Unmarshal(b, &assigns) + if err != nil { + return err + } + if len(assigns) > 0 { + pAssigns := make([]*BindAssignment, len(assigns)) + for k := range assigns { + pAssigns[k] = &assigns[k] + } + d.BindAssignments = pAssigns + return nil + } + + err = json.Unmarshal(b, &iamBinds) + if err != nil { + return err + } + + assignments := make([]*BindAssignment, 1) + assignments[0] = &BindAssignment{ + Bindings: iamBinds, + ResourceId: "", + } + return nil } /* @@ -253,22 +253,22 @@ ParseBindings will read either an iam.Binding or GcpBindAssignment structure and Note that if a single binding is provided, the GcpBindAssignment.ResourceId value will be nil */ func ParseBindings(bindingBytes []byte) ([]*BindAssignment, error) { - var data Assignments - err := json.Unmarshal(bindingBytes, &data) - if err != nil { - return nil, err - } + var data Assignments + err := json.Unmarshal(bindingBytes, &data) + if err != nil { + return nil, err + } - return data.BindAssignments, nil + return data.BindAssignments, nil } /* ParseFile will load a file from the specified path and will auto-detect format and convert to GcpBindAssignment. See ParseBindings */ func ParseFile(path string) ([]*BindAssignment, error) { - policyBytes, err := os.ReadFile(path) - if err != nil { - return nil, err - } - return ParseBindings(policyBytes) + policyBytes, err := os.ReadFile(path) + if err != nil { + return nil, err + } + return ParseBindings(policyBytes) } diff --git a/models/rar/policy_transformer.go b/models/rar/policy_transformer.go index ff784c8..b88f1d9 100644 --- a/models/rar/policy_transformer.go +++ b/models/rar/policy_transformer.go @@ -1,115 +1,115 @@ package rar import ( - "sort" - "strings" + "sort" + "strings" - "github.com/hexa-org/policy-mapper/models/rar/functionalsupport" - "github.com/hexa-org/policy-mapper/pkg/hexapolicy" + "github.com/hexa-org/policy-mapper/models/rar/functionalsupport" + "github.com/hexa-org/policy-mapper/pkg/hexapolicy" - "golang.org/x/exp/slices" - log "golang.org/x/exp/slog" + "golang.org/x/exp/slices" + log "golang.org/x/exp/slog" ) const ActionUriPrefix = "http:" func BuildPolicies(resourceActionRolesList []ResourceActionRoles) []hexapolicy.PolicyInfo { - policies := make([]hexapolicy.PolicyInfo, 0) - for _, one := range resourceActionRolesList { - httpMethod := one.Action - roles := one.Roles - slices.Sort(roles) - policies = append(policies, hexapolicy.PolicyInfo{ - Meta: hexapolicy.MetaInfo{Version: hexapolicy.IdqlVersion, ProviderType: "RARmodel"}, - Actions: []hexapolicy.ActionInfo{{ActionUriPrefix + httpMethod}}, - Subject: hexapolicy.SubjectInfo{Members: roles}, - Object: hexapolicy.ObjectInfo{ResourceID: one.Resource}, - }) - } - - sortPolicies(policies) - return policies + policies := make([]hexapolicy.PolicyInfo, 0) + for _, one := range resourceActionRolesList { + httpMethod := one.Action + roles := one.Roles + slices.Sort(roles) + policies = append(policies, hexapolicy.PolicyInfo{ + Meta: hexapolicy.MetaInfo{Version: hexapolicy.IdqlVersion, ProviderType: "RARmodel"}, + Actions: []hexapolicy.ActionInfo{{ActionUriPrefix + httpMethod}}, + Subject: hexapolicy.SubjectInfo{Members: roles}, + Object: hexapolicy.ObjectInfo{ResourceID: one.Resource}, + }) + } + + sortPolicies(policies) + return policies } func FlattenPolicy(origPolicies []hexapolicy.PolicyInfo) []hexapolicy.PolicyInfo { - resActionPolicyMap := make(map[string]hexapolicy.PolicyInfo) - for _, pol := range origPolicies { - resource := pol.Object.ResourceID - if resource == "" { - log.Warn("FlattenPolicy Skipping policy without resource") - continue - } - for _, act := range pol.Actions { - if strings.TrimSpace(act.ActionUri) == "" { - log.Warn("FlattenPolicy Skipping policy without actionUri", "resource", resource) - continue - } - lookupKey := act.ActionUri + resource - matchingPolicy, found := resActionPolicyMap[lookupKey] - var existingMembers []string - if found { - existingMembers = matchingPolicy.Subject.Members - } - newMembers := CompactMembers(existingMembers, pol.Subject.Members) - newPol := hexapolicy.PolicyInfo{ - Meta: hexapolicy.MetaInfo{Version: "0.5"}, - Actions: []hexapolicy.ActionInfo{{ActionUri: act.ActionUri}}, - Subject: hexapolicy.SubjectInfo{Members: newMembers}, - Object: hexapolicy.ObjectInfo{ResourceID: resource}, - } - - resActionPolicyMap[lookupKey] = newPol - } - } - - flat := make([]hexapolicy.PolicyInfo, 0) - for _, pol := range resActionPolicyMap { - flat = append(flat, pol) - } - - sortPolicies(flat) - return flat + resActionPolicyMap := make(map[string]hexapolicy.PolicyInfo) + for _, pol := range origPolicies { + resource := pol.Object.ResourceID + if resource == "" { + log.Warn("FlattenPolicy Skipping policy without resource") + continue + } + for _, act := range pol.Actions { + if strings.TrimSpace(act.ActionUri) == "" { + log.Warn("FlattenPolicy Skipping policy without actionUri", "resource", resource) + continue + } + lookupKey := act.ActionUri + resource + matchingPolicy, found := resActionPolicyMap[lookupKey] + var existingMembers []string + if found { + existingMembers = matchingPolicy.Subject.Members + } + newMembers := CompactMembers(existingMembers, pol.Subject.Members) + newPol := hexapolicy.PolicyInfo{ + Meta: hexapolicy.MetaInfo{Version: hexapolicy.IdqlVersion}, + Actions: []hexapolicy.ActionInfo{{ActionUri: act.ActionUri}}, + Subject: hexapolicy.SubjectInfo{Members: newMembers}, + Object: hexapolicy.ObjectInfo{ResourceID: resource}, + } + + resActionPolicyMap[lookupKey] = newPol + } + } + + flat := make([]hexapolicy.PolicyInfo, 0) + for _, pol := range resActionPolicyMap { + flat = append(flat, pol) + } + + sortPolicies(flat) + return flat } func CompactActions(existing, new []hexapolicy.ActionInfo) []hexapolicy.ActionInfo { - actionUris := make([]string, 0) - for _, act := range existing { - actionUris = append(actionUris, act.ActionUri) - } - for _, act := range new { - actionUris = append(actionUris, act.ActionUri) - } - actionUris = functionalsupport.SortCompact(actionUris) - - actionInfos := make([]hexapolicy.ActionInfo, 0) - for _, uri := range actionUris { - actionInfos = append(actionInfos, hexapolicy.ActionInfo{ - ActionUri: uri, - }) - } - return actionInfos + actionUris := make([]string, 0) + for _, act := range existing { + actionUris = append(actionUris, act.ActionUri) + } + for _, act := range new { + actionUris = append(actionUris, act.ActionUri) + } + actionUris = functionalsupport.SortCompact(actionUris) + + actionInfos := make([]hexapolicy.ActionInfo, 0) + for _, uri := range actionUris { + actionInfos = append(actionInfos, hexapolicy.ActionInfo{ + ActionUri: uri, + }) + } + return actionInfos } func CompactMembers(existing, new []string) []string { - compacted := make([]string, 0) - compacted = append(compacted, existing...) - compacted = append(compacted, new...) - return functionalsupport.SortCompact(compacted) + compacted := make([]string, 0) + compacted = append(compacted, existing...) + compacted = append(compacted, new...) + return functionalsupport.SortCompact(compacted) } func sortPolicies(policies []hexapolicy.PolicyInfo) { - sort.SliceStable(policies, func(i, j int) bool { - resComp := strings.Compare(policies[i].Object.ResourceID, policies[j].Object.ResourceID) - actComp := strings.Compare(policies[i].Actions[0].ActionUri, policies[j].Actions[0].ActionUri) - switch resComp { - case 0: - return actComp <= 0 - default: - return resComp < 0 - - } - }) + sort.SliceStable(policies, func(i, j int) bool { + resComp := strings.Compare(policies[i].Object.ResourceID, policies[j].Object.ResourceID) + actComp := strings.Compare(policies[i].Actions[0].ActionUri, policies[j].Actions[0].ActionUri) + switch resComp { + case 0: + return actComp <= 0 + default: + return resComp < 0 + + } + }) } /*