Update CLI to reflect change done in audit remediation
This commit is contained in:
parent
31216b0885
commit
d740d5fd91
13 changed files with 141 additions and 180 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,4 @@
|
|||
cpu.out
|
||||
vendor
|
||||
cmd/cmd
|
||||
cmd/jess
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
/*
|
||||
CLI:
|
||||
### Jess CLI
|
||||
|
||||
This is currently still more of a planning and working document.
|
||||
Here is the CLI interface that is planned:
|
||||
|
||||
```
|
||||
jess create <envelope file>
|
||||
|
||||
jess close <file> with <envelope file>
|
||||
jess close <file> with <envelope name>
|
||||
encrypt a file, write to file with the same name, but with a .letter suffix
|
||||
-o <file> ... write output to <file>
|
||||
|
||||
|
@ -23,11 +26,12 @@ jess show <file>
|
|||
- letter
|
||||
- seal (signature-only letter)
|
||||
|
||||
global arguments
|
||||
--tsdir /path/to/truststore
|
||||
--seclevel <uint>
|
||||
--symkeysize <uint>
|
||||
--quiet only output errors and warnings
|
||||
jess generate
|
||||
generate a new signet and store both signet and recipient in the truststore
|
||||
|
||||
*/
|
||||
package main
|
||||
global arguments
|
||||
--tsdir /path/to/truststore
|
||||
--seclevel <uint>
|
||||
--symkeysize <uint>
|
||||
--quiet only output errors and warnings
|
||||
```
|
|
@ -26,10 +26,10 @@ func newEnvelope(name string) (*jess.Envelope, error) {
|
|||
Message: "Select preset:",
|
||||
Options: []string{
|
||||
"Encrypt with password",
|
||||
"Encrypt with keyfile",
|
||||
"Encrypt for someone",
|
||||
"Encrypt with key",
|
||||
"Encrypt for someone and sign",
|
||||
"Encrypt for someone but don't sign",
|
||||
"Sign a file",
|
||||
"Custom from scratch",
|
||||
},
|
||||
}
|
||||
err := survey.AskOne(prompt, &preset, nil)
|
||||
|
@ -39,26 +39,23 @@ func newEnvelope(name string) (*jess.Envelope, error) {
|
|||
|
||||
switch preset {
|
||||
case "Encrypt with password":
|
||||
envelope.Tools = jess.RecommendedStoragePassword
|
||||
envelope.SuiteID = jess.SuitePassword
|
||||
err = selectSignets(envelope, "pw")
|
||||
|
||||
case "Encrypt with keyfile":
|
||||
envelope.Tools = jess.RecommendedStorageKey
|
||||
case "Encrypt with key":
|
||||
envelope.SuiteID = jess.SuiteKey
|
||||
err = selectSignets(envelope, "key")
|
||||
|
||||
case "Encrypt for someone":
|
||||
envelope.Tools = jess.RecommendedStorageKey
|
||||
case "Encrypt for someone and sign":
|
||||
envelope.SuiteID = jess.SuiteComplete
|
||||
err = selectSignets(envelope, "recipient")
|
||||
if err == nil {
|
||||
err = selectSignets(envelope, "sender")
|
||||
}
|
||||
|
||||
case "Encrypt for someone but don't sign":
|
||||
envelope.SuiteID = jess.SuiteRcptOnly
|
||||
err = selectSignets(envelope, "recipient")
|
||||
case "Sign a file":
|
||||
envelope.NoConfidentiality().NoIntegrity().NoRecipientAuth()
|
||||
envelope.SuiteID = jess.SuiteSign
|
||||
err = selectSignets(envelope, "sender")
|
||||
|
||||
case "Custom from scratch":
|
||||
// do nothing
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -72,12 +69,13 @@ func editEnvelope(envelope *jess.Envelope) error {
|
|||
// main menu
|
||||
|
||||
// print envelope status
|
||||
envelope.SecurityLevel = 0 // reset
|
||||
session, err := envelope.Correspondence(trustStore)
|
||||
if err != nil {
|
||||
fmt.Printf("Envelope status: %s\n", err)
|
||||
} else {
|
||||
fmt.Println("Envelope status: valid.")
|
||||
envelope.MinimumSecurityLevel = session.SecurityLevel
|
||||
envelope.SecurityLevel = session.SecurityLevel
|
||||
}
|
||||
|
||||
// sub menu
|
||||
|
@ -86,17 +84,18 @@ func editEnvelope(envelope *jess.Envelope) error {
|
|||
Message: "Select to edit",
|
||||
Options: formatColumns([][]string{
|
||||
{"Done", "save and return"},
|
||||
{" "},
|
||||
{"Requirements", formatRequirements(envelope)},
|
||||
{"Tools", strings.Join(envelope.Tools, ", ")},
|
||||
{""},
|
||||
{"Suite", envelope.SuiteID},
|
||||
{"", "provides " + formatRequirements(envelope.Suite().Provides)},
|
||||
{"", "and " + formatSecurityLevel(envelope.Suite().SecurityLevel)},
|
||||
{"Secrets", formatSignetNames(envelope.Secrets)},
|
||||
{"Recipients", formatSignetNames(envelope.Recipients)},
|
||||
{"Senders", formatSignetNames(envelope.Senders)},
|
||||
{" "},
|
||||
{""},
|
||||
{"Abort", "discard changes and return"},
|
||||
{"Delete", "delete and return"},
|
||||
}),
|
||||
PageSize: 10,
|
||||
PageSize: 15,
|
||||
}
|
||||
err = survey.AskOne(prompt, &submenu, nil)
|
||||
if err != nil {
|
||||
|
@ -111,10 +110,8 @@ func editEnvelope(envelope *jess.Envelope) error {
|
|||
return nil
|
||||
case strings.HasPrefix(submenu, "Delete"):
|
||||
return trustStore.DeleteEnvelope(envelope.Name)
|
||||
case strings.HasPrefix(submenu, "Requirements"):
|
||||
err = editEnvelopeRequirements(envelope)
|
||||
case strings.HasPrefix(submenu, "Tools"):
|
||||
err = editEnvelopeTools(envelope)
|
||||
case strings.HasPrefix(submenu, "Suite"):
|
||||
err = editEnvelopeSuite(envelope)
|
||||
case strings.HasPrefix(submenu, "Secrets"):
|
||||
err = selectSignets(envelope, "pw/key")
|
||||
case strings.HasPrefix(submenu, "Recipients"):
|
||||
|
@ -128,69 +125,30 @@ func editEnvelope(envelope *jess.Envelope) error {
|
|||
}
|
||||
}
|
||||
|
||||
func editEnvelopeRequirements(envelope *jess.Envelope) error {
|
||||
// TODO: improve
|
||||
|
||||
// get reqs
|
||||
requirements := envelope.Requirements()
|
||||
if requirements == nil {
|
||||
return errors.New("envelope requirements uninitialized")
|
||||
func editEnvelopeSuite(envelope *jess.Envelope) error {
|
||||
all := jess.Suites()
|
||||
suiteOptions := make([][]string, 0, len(all))
|
||||
for _, suite := range all {
|
||||
suiteOptions = append(suiteOptions, []string{
|
||||
suite.ID,
|
||||
"provides " + suite.Provides.ShortString(),
|
||||
formatSecurityLevel(suite.SecurityLevel),
|
||||
"uses " + strings.Join(suite.Tools, ", "),
|
||||
formatSuiteStatus(suite),
|
||||
})
|
||||
}
|
||||
|
||||
// build defaults
|
||||
var defaults []string
|
||||
if requirements.Has(jess.Confidentiality) {
|
||||
defaults = append(defaults, "Confidentiality")
|
||||
var selectedSuite string
|
||||
prompt := &survey.Select{
|
||||
Message: "Select suite",
|
||||
Options: formatColumns(suiteOptions),
|
||||
PageSize: 10,
|
||||
}
|
||||
if requirements.Has(jess.Integrity) {
|
||||
defaults = append(defaults, "Integrity")
|
||||
}
|
||||
if requirements.Has(jess.RecipientAuthentication) {
|
||||
defaults = append(defaults, "Recipient Authentication")
|
||||
}
|
||||
if requirements.Has(jess.SenderAuthentication) {
|
||||
defaults = append(defaults, "Sender Authentication")
|
||||
}
|
||||
|
||||
// prompt
|
||||
var selected []string
|
||||
prompt := &survey.MultiSelect{
|
||||
Message: "Select requirements:",
|
||||
Options: []string{
|
||||
"Confidentiality",
|
||||
"Integrity",
|
||||
"Recipient Authentication",
|
||||
"Sender Authentication",
|
||||
},
|
||||
Default: defaults,
|
||||
}
|
||||
err := survey.AskOne(prompt, &selected, nil)
|
||||
err := survey.AskOne(prompt, &selectedSuite, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse
|
||||
requirements.Remove(jess.Confidentiality)
|
||||
requirements.Remove(jess.Integrity)
|
||||
requirements.Remove(jess.RecipientAuthentication)
|
||||
requirements.Remove(jess.SenderAuthentication)
|
||||
for _, req := range selected {
|
||||
switch req {
|
||||
case "Confidentiality":
|
||||
requirements.Add(jess.Confidentiality)
|
||||
case "Integrity":
|
||||
requirements.Add(jess.Integrity)
|
||||
case "Recipient Authentication":
|
||||
requirements.Add(jess.RecipientAuthentication)
|
||||
case "Sender Authentication":
|
||||
requirements.Add(jess.SenderAuthentication)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func editEnvelopeTools(envelope *jess.Envelope) (err error) {
|
||||
envelope.Tools, err = pickTools(envelope.Tools, "Select tools:")
|
||||
return err
|
||||
envelope.SuiteID = strings.Fields(selectedSuite)[0]
|
||||
return envelope.ReloadSuite()
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package main
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -32,19 +31,8 @@ func newSignet(name, scheme string) (*jess.Signet, error) {
|
|||
schemeSelection := [][]string{
|
||||
{jess.SignetSchemePassword, "Password"},
|
||||
{jess.SignetSchemeKey, "Key", "dynamic b/s (set manually via --symkeysize)"},
|
||||
}
|
||||
for _, tool := range tools.AsList() {
|
||||
// check if tool support Signets
|
||||
switch tool.Info.Purpose {
|
||||
case tools.PurposeKeyExchange,
|
||||
tools.PurposeKeyEncapsulation,
|
||||
tools.PurposeSigning:
|
||||
schemeSelection = append(schemeSelection, []string{
|
||||
tool.Info.Name,
|
||||
tool.Info.FormatPurpose(),
|
||||
formatToolSecurityLevel(tool),
|
||||
})
|
||||
}
|
||||
{"ECDH-X25519", "Receiving (KeyExchange)"},
|
||||
{"Ed25519", "Signing"},
|
||||
}
|
||||
|
||||
// select scheme
|
||||
|
@ -204,42 +192,6 @@ func selectSignets(envelope *jess.Envelope, scope string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// add schemes to envelope
|
||||
checkSchemaLoop:
|
||||
for _, signet := range selectedSignets {
|
||||
switch signet.Scheme {
|
||||
case jess.SignetSchemePassword, jess.SignetSchemeKey:
|
||||
default:
|
||||
for _, toolID := range envelope.Tools {
|
||||
scheme := toolID
|
||||
if strings.Contains(scheme, "(") {
|
||||
scheme = strings.Split(scheme, "(")[0]
|
||||
}
|
||||
if scheme == signet.Scheme {
|
||||
continue checkSchemaLoop
|
||||
}
|
||||
}
|
||||
|
||||
// schema not found in envelope toolset
|
||||
tool, err := signet.Tool()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tool.Info.HasOption(tools.OptionNeedsManagedHasher) ||
|
||||
tool.Info.HasOption(tools.OptionNeedsDedicatedHasher) {
|
||||
// add hash tool
|
||||
hashToolName, err := pickHashTool(fmt.Sprintf("Select hash tool for %s:", tool.Info.Name), tool.Info.SecurityLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envelope.Tools = append(envelope.Tools, fmt.Sprintf("%s(%s)", tool.Info.Name, hashToolName))
|
||||
} else {
|
||||
envelope.Tools = append(envelope.Tools, signet.Scheme)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// make stubs
|
||||
selectedSignetStubs := make([]*jess.Signet, 0, len(selectedSignets))
|
||||
for _, signet := range selectedSignets {
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/AlecAivazis/survey"
|
||||
)
|
||||
|
||||
func pickTools(toolNames []string, promptMsg string) ([]string, error) {
|
||||
func pickTools(toolNames []string, promptMsg string) ([]string, error) { //nolint:unused,deadcode // TODO
|
||||
var toolSelection [][]string //nolint:prealloc
|
||||
preSelectedTools := make([]string, 0, len(toolNames))
|
||||
var preSelected int
|
||||
|
@ -101,7 +101,7 @@ func pickTools(toolNames []string, promptMsg string) ([]string, error) {
|
|||
return newTools, nil
|
||||
}
|
||||
|
||||
func pickHashTool(prompt string, minSecurityLevel int) (string, error) {
|
||||
func pickHashTool(prompt string, minSecurityLevel int) (string, error) { //nolint:unused // TODO
|
||||
var hashToolSelection [][]string
|
||||
for _, hashTool := range hashtools.AsList() {
|
||||
if hashTool.SecurityLevel >= minSecurityLevel {
|
||||
|
@ -126,7 +126,7 @@ func pickHashTool(prompt string, minSecurityLevel int) (string, error) {
|
|||
return strings.Fields(selectedEnty)[0], nil
|
||||
}
|
||||
|
||||
func stringInSlice(s string, a []string) bool {
|
||||
func stringInSlice(s string, a []string) bool { //nolint:unused // TODO
|
||||
for _, entry := range a {
|
||||
if entry == s {
|
||||
return true
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
|
||||
func init() {
|
||||
rootCmd.AddCommand(closeCmd)
|
||||
closeCmd.Flags().StringVarP(&closeFlagOutput, "output", "o", "", "specify output file")
|
||||
closeCmd.Flags().StringVarP(&closeFlagOutput, "output", "o", "", "specify output file (`-` for stdout")
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -20,9 +20,11 @@ var (
|
|||
closeCmdHelp = "usage: jess close <file> with <envelope name>"
|
||||
|
||||
closeCmd = &cobra.Command{
|
||||
Use: "close",
|
||||
Short: "encrypt file",
|
||||
PreRunE: requireTrustStore,
|
||||
Use: "close <file> with <envelope name>",
|
||||
Short: "encrypt file",
|
||||
Long: "encrypt file with the given envelope. Use `-` to use stdin",
|
||||
DisableFlagsInUseLine: true,
|
||||
PreRunE: requireTrustStore,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
registerPasswordCallbacks()
|
||||
|
||||
|
|
|
@ -16,10 +16,11 @@ func init() {
|
|||
|
||||
var (
|
||||
configureCmd = &cobra.Command{
|
||||
Use: "configure",
|
||||
Short: "configure (and create) envelope",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
PreRunE: requireTrustStore,
|
||||
Use: "configure <envelope name>",
|
||||
Short: "configure (and create) envelope",
|
||||
DisableFlagsInUseLine: true,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
PreRunE: requireTrustStore,
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
// check envelope name existence
|
||||
if len(args) == 0 {
|
||||
|
|
|
@ -15,10 +15,11 @@ var (
|
|||
generateFlagScheme string
|
||||
|
||||
generateCmd = &cobra.Command{
|
||||
Use: "generate",
|
||||
Short: "generate a new signet",
|
||||
Args: cobra.NoArgs,
|
||||
PreRunE: requireTrustStore,
|
||||
Use: "generate",
|
||||
Short: "generate a new signet",
|
||||
DisableFlagsInUseLine: true,
|
||||
Args: cobra.NoArgs,
|
||||
PreRunE: requireTrustStore,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
_, err := newSignet(generateFlagName, generateFlagScheme)
|
||||
return err
|
||||
|
|
|
@ -2,22 +2,42 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/safing/jess"
|
||||
"github.com/safing/jess/hashtools"
|
||||
"github.com/safing/jess/tools"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(toolsCmd)
|
||||
rootCmd.AddCommand(listCmd)
|
||||
}
|
||||
|
||||
var toolsCmd = &cobra.Command{
|
||||
Use: "tools",
|
||||
Short: "list all available tools",
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list all available suites and tools",
|
||||
DisableFlagsInUseLine: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Tools\n\n")
|
||||
fmt.Printf("Suites\n\n")
|
||||
suitesTable := [][]string{
|
||||
{"Name/ID", "Provides", "Security Level", "Tools", "Notes"},
|
||||
}
|
||||
for _, suite := range jess.Suites() {
|
||||
suitesTable = append(suitesTable, []string{
|
||||
suite.ID,
|
||||
suite.Provides.ShortString(),
|
||||
formatSecurityLevel(suite.SecurityLevel),
|
||||
strings.Join(suite.Tools, ", "),
|
||||
formatSuiteStatus(suite),
|
||||
})
|
||||
}
|
||||
for _, line := range formatColumns(suitesTable) {
|
||||
fmt.Println(line)
|
||||
}
|
||||
|
||||
fmt.Printf("\n\nTools\n\n")
|
||||
toolTable := [][]string{
|
||||
{"Name/ID", "Purpose", "Security Level", "Author", "Comment"},
|
||||
}
|
||||
|
@ -34,7 +54,7 @@ var toolsCmd = &cobra.Command{
|
|||
fmt.Println(line)
|
||||
}
|
||||
|
||||
fmt.Printf("\nHashTools\n\n")
|
||||
fmt.Printf("\n\nHashTools\n\n")
|
||||
hashToolTable := [][]string{
|
||||
{"Name/ID", "Security Level", "Author", "Comment"},
|
||||
}
|
|
@ -19,10 +19,11 @@ func init() {
|
|||
}
|
||||
|
||||
var manageCmd = &cobra.Command{
|
||||
Use: "manage",
|
||||
Short: "manage a trust store",
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
PreRunE: requireTrustStore,
|
||||
Use: "manage",
|
||||
Short: "manage a trust store",
|
||||
DisableFlagsInUseLine: true,
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
PreRunE: requireTrustStore,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// select action
|
||||
var selectedAction string
|
||||
|
@ -117,7 +118,11 @@ func manageEnvelopes() error {
|
|||
for _, envelope := range all {
|
||||
selection = append(selection, []string{
|
||||
envelope.Name,
|
||||
envelope.Requirements().ShortString(),
|
||||
envelope.SuiteID,
|
||||
fmt.Sprintf("provides %s and %s",
|
||||
envelope.Suite().Provides.ShortString(),
|
||||
formatSecurityLevel(envelope.Suite().SecurityLevel),
|
||||
),
|
||||
formatEnvelopeSignets(envelope),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import (
|
|||
|
||||
func init() {
|
||||
rootCmd.AddCommand(openCmd)
|
||||
openCmd.Flags().StringVarP(&openFlagOutput, "output", "o", "", "specify output file")
|
||||
openCmd.Flags().StringVarP(&openFlagOutput, "output", "o", "", "specify output file (`-` for stdout")
|
||||
}
|
||||
|
||||
var (
|
||||
|
@ -25,8 +25,9 @@ var (
|
|||
openCmdHelp = "usage: jess open <file>"
|
||||
|
||||
openCmd = &cobra.Command{
|
||||
Use: "open",
|
||||
Short: "decrypt a file",
|
||||
Use: "open <file>",
|
||||
Short: "decrypt file",
|
||||
Long: "decrypt file with the given envelope. Use `-` to use stdin",
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
registerPasswordCallbacks()
|
||||
|
||||
|
@ -39,7 +40,7 @@ var (
|
|||
filename := args[0]
|
||||
outputFilename := openFlagOutput
|
||||
if outputFilename == "" {
|
||||
if !strings.HasSuffix(filename, ".letter") || len(outputFilename) < 8 {
|
||||
if !strings.HasSuffix(filename, ".letter") || len(filename) < 8 {
|
||||
return errors.New("cannot automatically derive output filename, please specify with --output")
|
||||
}
|
||||
outputFilename = strings.TrimSuffix(filename, ".letter")
|
||||
|
|
|
@ -21,8 +21,9 @@ var (
|
|||
verifyCmdHelp = "usage: jess verify <file>"
|
||||
|
||||
verifyCmd = &cobra.Command{
|
||||
Use: "verify",
|
||||
Short: "verify a file",
|
||||
Use: "verify <file>",
|
||||
Short: "verify file",
|
||||
DisableFlagsInUseLine: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
||||
// check args
|
||||
if len(args) != 1 {
|
||||
|
|
|
@ -38,6 +38,10 @@ func formatColumns(table [][]string) []string {
|
|||
return lines
|
||||
}
|
||||
|
||||
func formatSecurityLevel(securityLevel int) string {
|
||||
return fmt.Sprintf("%d b/s", securityLevel)
|
||||
}
|
||||
|
||||
func formatToolSecurityLevel(tool *tools.Tool) string {
|
||||
if tool.Info.HasOption(tools.OptionNeedsSecurityLevel) {
|
||||
return "dynamic b/s (set manually via --seclevel)"
|
||||
|
@ -45,7 +49,7 @@ func formatToolSecurityLevel(tool *tools.Tool) string {
|
|||
if tool.Info.SecurityLevel == 0 {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%d b/s", tool.Info.SecurityLevel)
|
||||
return formatSecurityLevel(tool.Info.SecurityLevel)
|
||||
}
|
||||
|
||||
func formatSignetName(signet *jess.Signet) string {
|
||||
|
@ -116,12 +120,11 @@ func formatSignetSecurityLevel(signet *jess.Signet) string {
|
|||
return fmt.Sprintf("%d b/s", securityLevel)
|
||||
}
|
||||
|
||||
func formatRequirements(envelope *jess.Envelope) string {
|
||||
attrs := envelope.Requirements()
|
||||
if attrs == nil || attrs.Empty() {
|
||||
func formatRequirements(reqs *jess.Requirements) string {
|
||||
if reqs == nil || reqs.Empty() {
|
||||
return "none (unsafe)"
|
||||
}
|
||||
return attrs.String()
|
||||
return reqs.String()
|
||||
}
|
||||
|
||||
func formatSignetNames(signets []*jess.Signet) string {
|
||||
|
@ -145,3 +148,14 @@ func formatEnvelopeSignets(envelope *jess.Envelope) string {
|
|||
}
|
||||
return strings.Join(sections, ", ")
|
||||
}
|
||||
|
||||
func formatSuiteStatus(suite *jess.Suite) string {
|
||||
switch suite.Status {
|
||||
case jess.SuiteStatusDeprecated:
|
||||
return "DEPRECATED"
|
||||
case jess.SuiteStatusRecommended:
|
||||
return "recommended"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue