initial commit

This commit is contained in:
2026-04-02 01:53:56 -04:00
commit e71ff601d7
3 changed files with 220 additions and 0 deletions

3
go.mod Normal file
View File

@@ -0,0 +1,3 @@
module libpolicy
go 1.25.0

192
policy-allow.go Normal file
View File

@@ -0,0 +1,192 @@
package libpolicy
import (
"log"
"net"
"net/netip"
"strings"
)
func contains(slice []string, val string) bool {
for _, v := range slice {
if v == val {
return true
}
}
return false
}
type Effect uint8
const (
Allow Effect = iota
Deny
NoMatch
)
func IsIPInCIDR(ipStr, cidrStr string) bool {
// Parse the IP address
cidrStr = normalizeCIDR(cidrStr)
log.Println("Parsing IP:", ipStr, " against CIDR:", cidrStr)
ip := net.ParseIP(ipStr)
if ip == nil {
return false // Invalid IP
}
// Parse the CIDR
_, ipnet, err := net.ParseCIDR(cidrStr)
if err != nil {
return false // Invalid CIDR
}
// net.IPNet.Contains() handles IPv4/IPv6 correctly, including IPv4-mapped IPv6
return ipnet.Contains(ip)
}
func normalizeCIDR(cidrStr string) string {
cidrStr = strings.TrimSpace(cidrStr)
if cidrStr == "" {
return ""
}
// Already has a mask suffix → use as-is
if strings.Contains(cidrStr, "/") {
return cidrStr
}
// Try to parse as IP address to determine version
addr, err := netip.ParseAddr(cidrStr)
if err != nil {
return "" // Invalid
}
if addr.Is4() || addr.Is4In6() {
return cidrStr + "/32"
}
return cidrStr + "/128"
}
func verifyRequest(request *Request, policy *Policy) (Effect, string) {
//conditionReview = []Condition{}
var effect Effect
effect = NoMatch
statementid := "NoMatch"
for _, condition := range policy.Conditions {
log.Println("Checking Statement: ", condition.StatementID)
// Check the principal
if !contains(condition.Principal, request.Principal) {
log.Println("No match on principal")
log.Println("Checked condition principal:", condition.Principal)
log.Println("Against: request principal:", request.Principal)
continue
}
if !contains(condition.Actions, request.Action) {
log.Println("No match on actions")
log.Println("Checked condition actions:", condition.Actions)
log.Println("Against: request action:", request.Action)
continue
}
// Source Check
sourceMatch := false
for _, source := range condition.Source {
if IsIPInCIDR(request.SourceIPAddress, source) {
sourceMatch = true
}
}
if sourceMatch == false {
log.Println("No match on source")
log.Println("Checked condition source:", condition.Source)
log.Println("Against: request source:", request.SourceIPAddress)
continue
}
if condition.Effect == "allow" {
effect = Allow
log.Println("Allowed per ")
log.Printf("Request is allowed by condition: %s\n", condition.StatementID)
effect = Allow
statementid = condition.StatementID
} else if condition.Effect == "deny" {
// Immediately deny upon explicit deny
log.Printf("Request is denied by condition: %s\n", condition.StatementID)
return Deny, condition.StatementID
}
}
return effect, statementid
}
/*
func loadRequestFile(filename string) (*Request, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open '%s' %w", filename, err)
}
defer file.Close()
var request Request
if err := json.NewDecoder(file).Decode(&request); err != nil {
return nil, fmt.Errorf("failed to parse json from '%s': %w", filename, err)
}
fmt.Printf("Successfully loaded request from %s\n", filename)
return &request, nil
}
func loadPolicyFile(filename string) (*Policy, error) {
file, err := os.Open(filename)
if err != nil {
return nil, fmt.Errorf("failed to open file '%s': %w", filename, err)
}
defer file.Close()
var policy Policy
if err := json.NewDecoder(file).Decode(&policy); err != nil {
return nil, fmt.Errorf("failed to parse JSON from '%s': %w", filename, err)
}
fmt.Printf("Successfully loaded policy from %s\n", filename)
return &policy, nil
}
*/
/*
func main() {
policy, err := loadPolicyFile("policy.json")
if err != nil {
fmt.Println("Error: %v", err)
os.Exit(1)
}
fmt.Println("Policy structure loaded successfully:")
pretty, _ := json.MarshalIndent(policy, "", "\t")
fmt.Println(string(pretty))
request, err := loadRequestFile("request.json")
if err != nil {
fmt.Println("Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Loaded Request successfully\n")
pretty, _ = json.MarshalIndent(request, "", "\t")
fmt.Println(string(pretty))
effect, statementid := verifyRequest(request, policy)
fmt.Println("")
if effect == NoMatch {
fmt.Println("No matching conditions found. Defaulting to Deny.")
} else if effect == Allow {
fmt.Println("Request is allowed based on matching conditions.")
} else {
fmt.Println("Request is denied based on matching conditions.")
}
println("statementid:", statementid)
}
*/

25
structures.go Normal file
View File

@@ -0,0 +1,25 @@
package libpolicy
// Policy represents the top-level structure of your custom policy.json
type Policy struct {
Name string `json:"name"`
Description string `json:"description"`
Conditions []Condition `json:"conditions"`
}
// Condition represents each item in the "conditions" array
type Condition struct {
StatementID string `json:"statementid"`
Principal []string `json:"principals"`
Actions []string `json:"actions"`
Source []string `json:"source"`
Effect string `json:"effect"`
Operator string `json:"operator"`
}
type Request struct {
Principal string `json:"principal"`
SourceIPAddress string `json:"source_ipaddress"`
Action string `json:"action"`
Target string `json:"target"`
}