-
Notifications
You must be signed in to change notification settings - Fork 176
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
Adding Analyze command to guacone #1809
base: main
Are you sure you want to change the base?
Conversation
@pxp928 ready for review. |
Cool, @arorasoham9 can you add some screenshots to what the output will look like for the CLI? |
pkg/analyzer/analyze.go
Outdated
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package guacanalyze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
one major task would be to add unit tests to this package. Some things like the graph, getting data from graphQL can be abstracted away but do you have some tests you can add here to test the functionality?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't have any at the moment. I can write a few. Could they come in a future PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added unit tests as requested.
Added some screenshots. The Prettify function errored out at many instances so I improvised to print the same details that function does but taken from the graph DS itself, not graphQL. |
pkg/analyzer/analyze.go
Outdated
node *Node | ||
) | ||
if node, err = g.Vertex(ID); err != nil { | ||
fmt.Println("Error setting node attribute", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to return this as an error.
pkg/analyzer/analyze.go
Outdated
) | ||
if node, err = g.Vertex(ID); err != nil { | ||
fmt.Println("Error setting node attribute", err) | ||
os.Exit(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
don't use os.Exit(1)
should return error
pkg/analyzer/analyze.go
Outdated
|
||
func GetNodeAttribute(g graph.Graph[string, *Node],ID, key string) interface{} { | ||
var ( | ||
err error |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to instantiate this here, can just it on line 72?
pkg/analyzer/analyze.go
Outdated
node.Attributes[key] = value | ||
} | ||
|
||
func GetNodeAttribute(g graph.Graph[string, *Node],ID, key string) interface{} { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is this returning an interface{}
pkg/analyzer/analyze.go
Outdated
foundHasSBOMPkg, err = model.HasSBOMs(ctx, gqlclient, model.HasSBOMSpec{Subject: &model.PackageOrArtifactSpec{Package: &model.PkgSpec{Id: &pkgResponse.Packages[0].Namespaces[0].Names[0].Versions[0].Id}}, | ||
}) | ||
if err != nil { | ||
fmt.Printf("(purl)failed getting hasSBOM with error :%v", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dont use fmt.Printf
, you should return an error via fmt.Errorf
pkg/analyzer/analyze.go
Outdated
func verifyAnalyzeFlags(slsas, sboms []string, errSlsa, errSbom error, uri, purl, id bool) { | ||
|
||
if (errSlsa != nil && errSbom != nil) || (len(slsas) ==0 && len(sboms) == 0 ){ | ||
fmt.Println("Must specify slsa or sboms ") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return a proper error and dont use os.Exit
pkg/analyzer/analyze.go
Outdated
namespaceTwo, okTwo := GetNodeAttribute(analysisGraph,diffList.MissingAddedRemovedLinks[i][1], "Namespace[0]").(string) | ||
|
||
if !okOne || !okTwo { | ||
fmt.Println("Error getting node namespace attribute") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
clean up and pass out proper error
pkg/analyzer/analyze.go
Outdated
table.SetAlignment(tablewriter.ALIGN_LEFT) | ||
table.Render() | ||
if (!all && max > maxprint){ | ||
fmt.Println("Run with --all to see full list") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should user logger here: https://github.com/guacsec/guac/blob/main/pkg/handler/processor/process/process.go#L117
It should be able to be pulled from the context:
logger := logging.FromContext(ctx)
pkg/analyzer/analyze.go
Outdated
} | ||
|
||
func HasSBOMToGraph(cmd *cobra.Command, ctx context.Context, gqlclient graphql.Client) ( []graph.Graph[string, *Node]){ | ||
slsas, errSlsa := cmd.Flags().GetStringSlice("slsa") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pass in options or something similar and not the actual cmd
. See: https://github.com/guacsec/guac/blob/main/cmd/guacone/cmd/s3.go#L80-L93
pkg/analyzer/analyze.go
Outdated
if uri { | ||
hasSBOMResponseOne, err = FindHasSBOMBy(model.HasSBOMSpec{} ,sboms[0],"", "", ctx, gqlclient) | ||
if err != nil { | ||
fmt.Println("(uri)failed to lookup sbom:", sboms[0], err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fix error
pkg/analyzer/analyze_test.go
Outdated
@@ -0,0 +1,66 @@ | |||
package guacanalyze_test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing header
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly code organization and naming comments.
cmd/guacone/cmd/analyze.go
Outdated
"github.com/guacsec/guac/pkg/logging" | ||
"github.com/spf13/cobra" | ||
"github.com/spf13/viper" | ||
analysis "github.com/guacsec/guac/pkg/analyzer" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for the import rename here?
cmd/guacone/cmd/analyze.go
Outdated
if diff && intersect && union || diff && intersect || diff && union || intersect && union { | ||
fmt.Println("Must specify only one of --diff, --intersect, --union") | ||
return | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be a positional argument if required, not a flag/option.
cmd/guacone/cmd/analyze.go
Outdated
} | ||
|
||
//create graphs | ||
graphs := analysis.HasSBOMToGraph(cmd, ctx, gqlclient) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't pass cmd to a package under pkg/
. Those shouldn't have a dependency on cli commands or cobra. Explicitly pass the parameters this needs.
cmd/guacone/cmd/analyze.go
Outdated
|
||
func init() { | ||
|
||
rootCmd.PersistentFlags().Bool("intersect", false, "compute intesection of given sboms") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add flags to rootCmd, this will show up on all guacone
cmds if so. Use this analyzeCmd
you are creating.
Also, cli options are normally under pkg/cli/store.go
but this command has so many it might make sense to just leave them here.
cmd/guacone/cmd/analyze.go
Outdated
rootCmd.PersistentFlags().Bool("purl", false, "input is a pURL") | ||
rootCmd.PersistentFlags().Bool("id", false, "input is an Id") | ||
rootCmd.PersistentFlags().Bool("metadata", false, "Compare SBOM metadata") | ||
rootCmd.PersistentFlags().Bool("inclSoft", false, "Compare Included Softwares") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change to incl-soft
and similar for others. All cli options use hyphen-case.
cmd/guacone/cmd/analyze.go
Outdated
rootCmd.PersistentFlags().Int("maxprint", PRINT_MAX, "max number of similar sboms to print") | ||
rootCmd.AddCommand(analyzeCmd) | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a newline to the end of the file
pkg/analyzer/analyze.go
Outdated
) | ||
if node, err = g.Vertex(ID); err != nil { | ||
fmt.Println("Error setting node attribute", err) | ||
os.Exit(1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't have any os.Exit in a package under pkg/
, assume this is a library that can be called from anywhere. Propagate errors back to the caller.
pkg/analyzer/analyze.go
Outdated
return foundHasSBOMPkg, nil | ||
} | ||
|
||
func verifyAnalyzeFlags(slsas, sboms []string, errSlsa, errSbom error, uri, purl, id bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All this should be under the file in cmd/
, shouldn't have any flag validation here in pkg/
.
pkg/analyzer/analyze.go
Outdated
//print to stdout | ||
printHighlightedAnalysis(dot, diffList, all, maxprint, action, analysisGraph ) | ||
} | ||
func max(nums []int) int { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add an empty line between functions.
pkg/analyzer/analyze.go
Outdated
//Create dot file | ||
createGraphDotFile(dot, analysisGraph) | ||
//print to stdout | ||
printHighlightedAnalysis(dot, diffList, all, maxprint, action, analysisGraph ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anything under pkg/
should not print anything to stdout. Any logic should result in a datastructure that returns back to cmd/
and is then printed. This helps for testability. Looks like much of this may need to be moved back to cmd/
@arorasoham9 did the |
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
@arorasoham9 any luck in improving the output? It is very hard to understand. Like if you can show less information and just show only what is relevant, that would be better. |
@pxp928 I am still thinking of ways to show only the relevant information. This is a better explanation for why you see so much even with just a small difference between two sboms. The diff works at the granularity of a path, not a node, so when it finds even one node different it classifies all paths with that participating node as different (and prints them). I believe the original aim was to find missing/different paths bw two sboms was to also offer a graph "structural" difference as well. I am trying to think how we can still offer the same differences in an easier to understand manner. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @arorasoham9 for working on this big feature!! definitely going to be very useful, would you be able to help break this up into a few separate PRs, that would be very helpful in getting this merged in faster!
maybe start with the analyzer package, and then add a table formatter pkg, and then the CLI options and the CLI itself?
cmd/guacone/cmd/analyze.go
Outdated
uri, _ := cmd.Flags().GetBool("uri") | ||
purl, _ := cmd.Flags().GetBool("purl") | ||
|
||
metadata, _ := cmd.Flags().GetBool("metadata") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these flags should be initialized in https://github.com/guacsec/guac/blob/main/pkg/cli/store.go, and preferably prefixed in a way that shows its for guacanalyze
cmd/guacone/cmd/analyze.go
Outdated
|
||
if test == "" { | ||
if err = verifyAnalyzeFlags(slsas, sboms, errSlsa, errSbom, uri, purl, id); err != nil { | ||
fmt.Fprintf(os.Stderr, "Error: %s", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: golang errors style guide is lowercase first characters.
cmd/guacone/cmd/analyze.go
Outdated
|
||
//create graphs | ||
graphs, err = hasSBOMToGraph(ctx, gqlclient, sboms, AnalyzeOpts{ | ||
Metadata: metadata, InclSoft: inclSoft, InclDeps: inclDeps, InclOccur: inclOccur, Namespaces: namespaces, URI: uri, PURL: purl, ID: id}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make it into different lines for readability.
cmd/guacone/cmd/analyze.go
Outdated
os.Exit(1) | ||
} | ||
|
||
//create graphs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the comment doesn't add much context to the code, either remove or expand.
} | ||
} | ||
|
||
if args[0] == "diff" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it make sense for these to be subcommands? otherwise this should be a flag maybe?
pkg/analyzer/analyzer.go
Outdated
return n.ID | ||
} | ||
|
||
func SetNodeAttribute(g graph.Graph[string, *Node], ID, key string, value interface{}) bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you provide additional context and documentation around the functions? it is not clear what should be in value, unless there's a good reason, i would highly recommend not having interface{}
as the type of value. If anything it can be a hidden type that can be minted by the analyzer but must provide sufficient constructors to make sense of it.
pkg/analyzer/analyzer.go
Outdated
|
||
switch action { | ||
//0 is diff | ||
case 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use an enum :)
pkg/analyzer/analyzer.go
Outdated
return hex.EncodeToString(hash[:]) | ||
} | ||
|
||
func AddGraphNode(g graph.Graph[string, *Node], _ID, color string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is color used for? Is it the identity of an edge of just for analysis purpose? can it be passed it as an option on the node for extensibility?
_ID
should be id to follow golang style?
cmd/guacone/cmd/analyze.go
Outdated
}, | ||
} | ||
|
||
func printDiffedPathTable(diffs []analyzer.DiffedPath) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would be helpful to move all printing stuff to either a different package or a different file
cmd/guacone/cmd/analyze.go
Outdated
return nil | ||
} | ||
|
||
func readTwoSBOM(filename string) ([]graph.Graph[string, *analyzer.Node], error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why read two sboms from a file? i don't think that's a common pattern?
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
…th the comparing path, not sure which to use Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Signed-off-by: Soham Arora <[email protected]>
Hey @arorasoham9 , would you be able to chat at bit more amount helping to turn the corner to complete the PR. |
Description of the PR
Analyze command for guacone
Comparing SBOMs with only patch version differences
guacone analyze diff --uri --sboms=https://anchore.com/syft/image/k8s.gcr.io/kube-apiserver-v1.24.1-583a02ce-8f7e-4794-91af-35f27ffeb73d,https://anchore.com/syft/image/k8s.gcr.io/kube-apiserver-v1.24.2-ee7e0a81-87de-4761-9689-4f7162d81e44
The diff is a 4 step process:
Each step has tests to ensure that it is stable. Meaning, if the steps are repeated for the same Input SBOM, the results are the same. The JSON you see are the test SBOMS I use to carry out the tests for each step of the diff. These JSON files are marshalled HasSBOM nodes for SBOMs I ingested into GUAC, pulled using the Node function.
PR Checklist
-s
flag togit commit
.make generate
has been runmake generate
has been runcollectsub
protobuf has been changed,make proto
has been run