Skip to content
89 changes: 77 additions & 12 deletions cmd/kosli/assertArtifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"net/http"
"net/url"

"github.com/kosli-dev/cli/internal/output"
"github.com/kosli-dev/cli/internal/requests"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -45,6 +46,7 @@ type assertArtifactOptions struct {
fingerprint string // This is calculated or provided by the user
flowName string
envName string
output string
}

func newAssertArtifactCmd(out io.Writer) *cobra.Command {
Expand Down Expand Up @@ -75,6 +77,8 @@ func newAssertArtifactCmd(out io.Writer) *cobra.Command {
cmd.Flags().StringVarP(&o.fingerprint, "fingerprint", "F", "", fingerprintFlag)
cmd.Flags().StringVarP(&o.flowName, "flow", "f", "", flowNameFlag)
cmd.Flags().StringVar(&o.envName, "environment", "", envNameFlag)
cmd.Flags().StringVarP(&o.output, "output", "o", "table", outputFlag)

addFingerprintFlags(cmd, o.fingerprintOptions)
addDryRunFlag(cmd)

Expand Down Expand Up @@ -116,31 +120,92 @@ func (o *assertArtifactOptions) run(out io.Writer, args []string) error {
return err
}

return output.FormattedPrint(response.Body, o.output, out, 0,
map[string]output.FormatOutputFunc{
"table": printAssertAsTable,
"json": output.PrintJson,
})
}

func printAssertAsTable(raw string, out io.Writer, page int) error {
var evaluationResult map[string]interface{}
err = json.Unmarshal([]byte(response.Body), &evaluationResult)
err := json.Unmarshal([]byte(raw), &evaluationResult)
if err != nil {
return err
}

flow, _ := evaluationResult["flow"].(string)
trail, _ := evaluationResult["trail"].(string)
scope := evaluationResult["scope"].(string)
complianceStatus, _ := evaluationResult["compliance_status"].(map[string]interface{})
attestationsStatuses, _ := complianceStatus["attestations_statuses"].([]interface{})

if evaluationResult["compliant"].(bool) {
logger.Info("COMPLIANT")
if scope == "flow" {
logger.Info("See more details at %s", evaluationResult["html_url"].(string))
}
} else {
if scope == "flow" {
return fmt.Errorf("not compliant\nSee more details at %s", evaluationResult["html_url"].(string))
} else {
jsonData, err := json.MarshalIndent(evaluationResult["policy_evaluations"], "", " ")
if err != nil {
return fmt.Errorf("error marshalling evaluation result: %v", err)
logger.Info("Error: NON-COMPLIANT")
}
logger.Info("Flow: %v\nTrail %v", flow, trail)
logger.Info("%-32v %-30v %-15v %-10v", "Attestation-name", "type", "status", "compliant")

for _, item := range attestationsStatuses {
attestation := item.(map[string]interface{})
name := attestation["attestation_name"]
attType := attestation["attestation_type"]
status := attestation["status"]
isCompliant, _ := attestation["is_compliant"].(bool)
unexpected, _ := attestation["unexpected"].(bool)
unexpectedStr := ""
if unexpected {
unexpectedStr = "unexpected"
}

logger.Info(" %-32v %-30v %-15v %-10v %-10v", name, attType, status, isCompliant, unexpectedStr)
}
if scope == "environment" {
logger.Info("%-32v %-30v", "Policy-name", "status")
policyEvaluations := evaluationResult["policy_evaluations"].([]interface{})
for _, item := range policyEvaluations {
policyEvaluation := item.(map[string]interface{})
policyName := policyEvaluation["policy_name"]
policyStatus := policyEvaluation["status"]
logger.Info(" %-32v %-30v", policyName, policyStatus)
if policyStatus != "COMPLIANT" {
ruleEvaluations := policyEvaluation["rule_evaluations"].([]interface{})
var failures []string
for _, item2 := range ruleEvaluations {
ruleEvaluation := item2.(map[string]interface{})
ignored := ruleEvaluation["ignored"].(bool)
satisfied, _ := ruleEvaluation["satisfied"].(bool)
if !ignored && !satisfied {
rule := ruleEvaluation["rule"].(map[string]interface{})
resolutions := ruleEvaluation["resolutions"].([]interface{})
for _, item3 := range resolutions {
resolution := item3.(map[string]interface{})
resolutionType := resolution["type"].(string)
ruleDefinition := rule["definition"].(map[string]interface{})
attestationName := ruleDefinition["name"]
attestationType := ruleDefinition["type"]
switch resolutionType {
case "legacy_flow":
failures = append(failures, "artifact comes from a legacy flow and does not have the new attestations")
case "missing_attestation":
failures = append(failures, fmt.Sprintf("artifact is missing required '%v' (type: %v) attestation in trail", attestationName, attestationType))
case "non_compliant_attestation":
failures = append(failures, fmt.Sprintf("attestation '%v' is non-compliant in trail", attestationName))
case "non_compliant_in_trail":
failures = append(failures, "artifact is not compliant in trail")
}
}
}
}
for _, fail := range failures {
logger.Info(" %v", fail)
}
}
return fmt.Errorf("not compliant for env [%s]: \n %v", o.envName,
string(jsonData))
}
}
logger.Info("\nSee more details at %s", evaluationResult["html_url"].(string))

return nil
}
20 changes: 18 additions & 2 deletions cmd/kosli/assertArtifact_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ type AssertArtifactCommandTestSuite struct {
suite.Suite
defaultKosliArguments string
flowName string
envName string
artifactName string
artifactPath string
fingerprint string
}

func (suite *AssertArtifactCommandTestSuite) SetupTest() {
suite.flowName = "assert-artifact"
suite.envName = "assert-artifact-environment"
suite.artifactName = "arti"
suite.artifactPath = "testdata/folder1/hello.txt"
global = &GlobalOpts{
Expand All @@ -35,6 +37,7 @@ func (suite *AssertArtifactCommandTestSuite) SetupTest() {
fingerprintOptions := &fingerprintOptions{
artifactType: "file",
}
CreateEnv(global.Org, suite.envName, "server", suite.Suite.T())
var err error
suite.fingerprint, err = GetSha256Digest(suite.artifactPath, fingerprintOptions, logger)
require.NoError(suite.Suite.T(), err)
Expand All @@ -58,12 +61,25 @@ func (suite *AssertArtifactCommandTestSuite) TestAssertArtifactCmd() {
{
name: "asserting an existing compliant artifact (using --fingerprint) results in OK and zero exit",
cmd: fmt.Sprintf(`assert artifact --fingerprint %s --flow %s %s`, suite.fingerprint, suite.flowName, suite.defaultKosliArguments),
goldenRegex: "COMPLIANT\nSee more details at http://localhost(:8001)?/docs-cmd-test-user/flows/assert-artifact/artifacts/fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02(?:\\?artifact_id=[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8})?\n",
goldenRegex: "(?s)^COMPLIANT\n.*Attestation-name.*See more details at http://localhost(:8001)?/docs-cmd-test-user/flows/assert-artifact/artifacts/fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02(?:\\?artifact_id=[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8})?\n",
},
{
name: "asserting an existing compliant artifact (using --fingerprint) for an environment results in OK and zero exit",
cmd: fmt.Sprintf(`assert artifact --fingerprint %s --flow %s --environment %s %s`, suite.fingerprint, suite.flowName, suite.envName, suite.defaultKosliArguments),
goldenRegex: "(?s)^COMPLIANT\n.*Attestation-name.*Policy-name.*See more details at http://localhost(:8001)?/docs-cmd-test-user/flows/assert-artifact/artifacts/fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02(?:\\?artifact_id=[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8})?\n",
},
{
name: "asserting an existing compliant artifact (using --artifact-type) results in OK and zero exit",
cmd: fmt.Sprintf(`assert artifact %s --artifact-type file --flow %s %s`, suite.artifactPath, suite.flowName, suite.defaultKosliArguments),
goldenRegex: "COMPLIANT\nSee more details at http://localhost(:8001)?/docs-cmd-test-user/flows/assert-artifact/artifacts/fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02?(?:\\?artifact_id=[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8})?\n",
goldenRegex: "(?s)^COMPLIANT\n.*See more details at http://localhost(:8001)?/docs-cmd-test-user/flows/assert-artifact/artifacts/fcf33337634c2577a5d86fd7ecb0a25a7c1bb5d89c14fd236f546a5759252c02?(?:\\?artifact_id=[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{8})?\n",
},
{
name: "json output of asserting an existing compliant artifact (using --artifact-type) results in OK and zero exit",
cmd: fmt.Sprintf(`assert artifact %s --output json --artifact-type file --flow %s %s`, suite.artifactPath, suite.flowName, suite.defaultKosliArguments),
goldenJson: []jsonCheck{
{"compliant", true},
{"scope", "flow"},
},
},
{
wantError: true,
Expand Down
1 change: 1 addition & 0 deletions cmd/kosli/testHelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func goldenJsonContains(t *testing.T, output string, path string, want interface

require.Equal(t, want, current, "unexpected value at path %s", path)
}

func compareTwoFiles(actualFilename, expectedFilename string) error {
actual, err := os.ReadFile(actualFilename)
if err != nil {
Expand Down