Skip to content

KnoxIQ CLI Changes - #69

Open
Phonx38 wants to merge 10 commits into
developfrom
PD-2353
Open

KnoxIQ CLI Changes#69
Phonx38 wants to merge 10 commits into
developfrom
PD-2353

Conversation

@Phonx38

@Phonx38 Phonx38 commented Aug 17, 2026

Copy link
Copy Markdown
Title Value
Type Feature / Bugfix
Ticket/Issue PD-2353
Migrations No
Migration Scripts No
ENV vars change Yes — APPKNOX_KNOXIQ_TIMEOUT (KnoxIQ triage timeout), APPKNOX_INCLUDE_NEEDS_REVIEW (needs-review inclusion)
Frontend No
Local testing Done
Staging testing Done — verified end-to-end against sherlock-knoxiq-uat
On premise notes Falls back to plain SAST behavior on 403/404 from knoxiq_scan/status, so older/non-KnoxIQ backends are unaffected
Documentation None
Release notes None
Version upgrade Minor

Changelog

  1. Add --knoxiq flag to upload to request KnoxIQ triage for a specific CI/CD build
    appknox upload app.apk --knoxiq
    
  2. Add KnoxIQ awareness to cicheck: intermediary → triage status → final triaged results, with AEIS score and exploit likelihood columns
  3. Add --exploit-likelihood-threshold low|medium|high to cicheck to gate builds on KnoxIQ exploit likelihood
  4. Add --include-needs-review flag and config get/set include-needs-review to control whether KnoxIQ needs-review vulnerabilities count toward the build decision (default: excluded)
  5. Add shared --knoxiq-timeout (default 30 min), combined with the static-scan timeout into one budget so unused SAST time carries over to KnoxIQ; shared consistently across cicheck, sarif, and reports knoxiq
  6. Add reports knoxiq <file_id> to generate and download the KnoxIQ PDF report in one step; fails explicitly if the file has no KnoxIQ results instead of falling back to a standard report
  7. Bring sarif to parity with cicheck: shares the same timeout budget and availability check, excludes needs-review by default, adds aeisScore/exploitLikelihood result properties
  8. Fix: likelihood-only gating on a file with no KnoxIQ triage used to silently pass with zero active gates; now falls back to the default risk gate and prints an explicit "Active gates:" line
  9. Fix: reports knoxiq printed its fail-fast error but exited 0 instead of 1
  10. Fix: config file path resolution on Windows — viper's $HOME expansion silently broke on Windows path separators, causing config set/init to fail with "Config File Not Found"
  11. Remove File.IsKnoxIQAutomated/KnoxIQStatus fields and GetByIDV3 (superseded by the new knoxiq_scan/status and cicd/analyses endpoints)

Dependent PRs

  • mycroft PD-2352 — backend endpoints/fields this CLI work depends on (cicd/analyses, per-build knoxiq_requested, is_knoxiq on reports)

Comment thread appknox/knoxiq.go
if err != nil {
if strings.Contains(err.Error(), "404") {
fileId := strconv.Itoa(fileID)
return nil, nil, errors.New("KnoxIQ CI/CD analyses for fileID " + fileId + " not found (404)")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use fmt.Errorf

Comment thread cmd/config.go
Supported keys:
include-needs-review Include KnoxIQ "needs review" vulnerabilities in the
CI check results and build decision (default: false).`,
}

@utkarshpandey12 utkarshpandey12 Aug 23, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update README.md for all changes done

@sonarqubecloud

Copy link
Copy Markdown

Comment thread appknox/knoxiq.go
var drfResponse DRFResponseKnoxIQCICDAnalysis
_, err = s.client.Do(ctx, req, &drfResponse)
if err != nil {
if strings.Contains(err.Error(), "404") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read the response code (StatusCodeOf

Comment thread helper/knoxiq_check.go
) (enums.KnoxIQScanStatusType, bool) {
scanStatus, _, err := client.KnoxIQ.GetScanStatus(ctx, fileID)
if err != nil {
switch appknox.StatusCodeOf(err) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-switch statusCode {
-case 403, 404:
-default:
-    PrintError(err)
-}
-return disabled, false
+if statusCode == 403 || statusCode == 404 {
+    return disabled, false
+}
+PrintError(err)

Comment thread cmd/sarif.go

func init() {
RootCmd.AddCommand(sarifCmd)
sarifCmd.Flags().StringP(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add something like --no-knoxiq to skips the KnoxIQ wait entirely and produces the plain report immediately

Comment thread cmd/root.go
os.Create(file)
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider adding below to provide more context about read failures , if it makes sense

+if _, statErr := os.Stat(configFile); statErr == nil {
+    fmt.Println("Warning: config file exists but could not be read; recreating it.")
+}

Comment thread cmd/config.go
os.Exit(1)
}
viper.Set(key, value)
if err := viper.WriteConfig(); err != nil {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider below

-viper.Set(key, value)
-if err := viper.WriteConfig(); err != nil {
+if err := setOnlyThisKey(key, value); err != nil {   // patches just `key` in the file on disk

Comment thread cmd/config.go
Short: "Print the current value of a configuration key.",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(viper.GetString(args[0]))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+             key := args[0]
+             if !isKnownConfigKey(key) {
+                     helper.PrintError(fmt.Errorf(
+                             "unknown config key %q. Supported keys: %s",
+                             key, strings.Join(knownConfigKeys, ", "),
+                     ))
+                     os.Exit(1)
+             }
-             fmt.Println(viper.GetString(args[0]))
+             fmt.Println(viper.GetString(key))
      },
 }

consider adding restrictions for get cmd as well

Comment thread cmd/config.go
))
os.Exit(1)
}
viper.Set(key, value)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config set saves a value without validating it so a include-needs-review Yes is accepted but checks strictly compares like if settings["include_needs_review"] == "true":

Comment thread helper/knoxiq_check.go
PrintError("exploit-likelihood gating requires KnoxIQ triage — skipping (no KnoxIQ results for this file)")
return 0
}
if !waitForKnoxIQ(ctx, client, fileID, policy.Budget.KnoxIQDeadline()) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the above check can be used as a short circuit for next wait call , less probability but still can be helpful if ci is run for some file where it already exists or has been evaluated at the first call itself

-func countLikelihoodOffenders(ctx context.Context, client *appknox.Client, fileID int, policy CiPolicy) int {
-     if _, available := knoxIQAvailable(ctx, client, fileID); !available {
+func countLikelihoodOffenders(ctx context.Context, client *appknox.Client, fileID int, policy CiPolicy) int {
+     status, available := knoxIQAvailable(ctx, client, fileID)
+     if !available {
              ...
-     if !waitForKnoxIQ(ctx, client, fileID, policy.Budget.KnoxIQDeadline()) {
+     if status != enums.KnoxIQStatusCompleted && !waitForKnoxIQ(ctx, client, fileID, policy.Budget.KnoxIQDeadline()) {

Comment thread helper/knoxiq_check.go
t.Print()
}

func reportKnoxIQGate(fileID int, policy CiPolicy, triaged []*appknox.KnoxIQCICDAnalysis) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write a test that calls the existing reportKnoxIQGate with one made-up finding that has: highest risk level (Critical), highest likelihood level (High), and NeedsReview = true — set the risk and likelihood thresholds so that a normal finding with this severity would fail the build — and checks that the build does not fail (no offending count, no exit) precisely because that one finding is marked "needs review."

Comment thread helper/knoxiq_check.go
// 403 (org without the KnoxIQ feature) and 404 (backend without the KnoxIQ
// endpoints) both mean "not available", so the CLI silently falls back to the
// plain SAST flow. Anything else is surfaced before falling back.
func knoxIQAvailable(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider adding tests
func TestKnoxIQAvailable_404IsQuiet(t testing.T) { / 404 -> false, nothing printed */ }
func TestKnoxIQAvailable_ServerErrorIsLoud(t testing.T) { / 500 -> false, error printed */ }
func TestKnoxIQAvailable_TrueWhenInProgressOrDone(t testing.T) { / pending/running/completed -> true */ }

Comment thread helper/knoxiq_check.go
enums.KnoxIQStatusLegacy:
return false
}
if time.Now().After(deadline) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider adding test case
TestWaitForKnoxIQ_DeadlinePassed

@utkarshpandey12 utkarshpandey12 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requested changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants