diff --git a/client/command/extensions/extensions.go b/client/command/extensions/extensions.go index acb7191f94..de6913cb6e 100644 --- a/client/command/extensions/extensions.go +++ b/client/command/extensions/extensions.go @@ -129,3 +129,13 @@ func ExtensionsCommandNameCompleter(con *console.SliverConsoleClient) carapace.A return carapace.ActionValuesDescribed(results...).Tag("extension commands") }) } + +func ManifestCompleter() carapace.Action { + return carapace.ActionCallback(func(c carapace.Context) carapace.Action { + results := []string{} + for k := range loadedManifests { + results = append(results, k) + } + return carapace.ActionValues(results...).Tag("extensions") + }) +} diff --git a/client/command/extensions/load.go b/client/command/extensions/load.go index c372cbcab2..92b7770b3a 100644 --- a/client/command/extensions/load.go +++ b/client/command/extensions/load.go @@ -268,13 +268,61 @@ func ExtensionRegisterCommand(extCmd *ExtCommand, cmd *cobra.Command, con *conso } loadedExtensions[extCmd.CommandName] = extCmd - helpMsg := extCmd.Help + //helpMsg := extCmd.Help + + usage := strings.Builder{} + usage.WriteString(extCmd.CommandName) + //build usage including args + for _, arg := range extCmd.Arguments { + usage.WriteString(" ") + if arg.Optional { + usage.WriteString("[") + } + usage.WriteString(strings.ToUpper(arg.Name)) + if arg.Optional { + usage.WriteString("]") + } + } + longHelp := strings.Builder{} + //prepend the help value, because otherwise I don't see where it is meant to be shown + //build the command ref + longHelp.WriteString("[[.Bold]]Command:[[.Normal]]") + longHelp.WriteString(usage.String()) + longHelp.WriteString("\n") + if len(extCmd.Help) > 0 || len(extCmd.LongHelp) > 0 { + longHelp.WriteString("[[.Bold]]About:[[.Normal]]") + if len(extCmd.Help) > 0 { + longHelp.WriteString(extCmd.Help) + longHelp.WriteString("\n") + } + if len(extCmd.LongHelp) > 0 { + longHelp.WriteString(extCmd.LongHelp) + longHelp.WriteString("\n") + } + } + if len(extCmd.Arguments) > 0 { + longHelp.WriteString("[[.Bold]]Arguments:[[.Normal]]") + } + //if more than 0 args specified, describe each arg at the bottom of the long help text (incase the manifest doesn't include it) + for _, arg := range extCmd.Arguments { + longHelp.WriteString("\n\t") + optStr := "" + if arg.Optional { + optStr = "[OPTIONAL]" + } + aType := arg.Type + if aType == "wstring" { + aType = "string" //avoid confusion, as this is mostly for telling operator what to shove into the args + } + //idk how to make this look nice, tabs don't work especially good - maybe should use the table stuff other things do? Pls help. + longHelp.WriteString(fmt.Sprintf("%s (%s):\t%s%s", strings.ToUpper(arg.Name), aType, optStr, arg.Desc)) + } // Command extensionCmd := &cobra.Command{ - Use: extCmd.CommandName, - Short: helpMsg, - Long: help.FormatHelpTmpl(extCmd.LongHelp), + Use: usage.String(), //extCmd.CommandName, + //Short: helpMsg.String(), doesn't appear to be used? + Long: help.FormatHelpTmpl(longHelp.String()), Run: func(cmd *cobra.Command, args []string) { runExtensionCmd(cmd, con, args) }, diff --git a/client/command/server.go b/client/command/server.go index 74a62aae12..9ac6903422 100644 --- a/client/command/server.go +++ b/client/command/server.go @@ -160,22 +160,26 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra. } server.AddCommand(extCmd) - extLoadCmd := &cobra.Command{ - Use: consts.LoadStr + " [EXT]", - Short: "Load a command EXT", - Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.LoadStr}), - Args: cobra.ExactArgs(1), - Run: func(cmd *cobra.Command, args []string) { - extensions.ExtensionLoadCmd(cmd, con, args) - }, - } - carapace.Gen(extLoadCmd).PositionalCompletion( - carapace.ActionDirectories().Tag("ext directory").Usage("path to the ext directory")) - extCmd.AddCommand(extLoadCmd) + /* + parking 'load' for now - the difference between 'load' and 'install' is that 'load' should not move binaries and manifests to the client install dir. + Maybe we can revisit this if it's required - but the usecase I can think of is when developing extensions, and that will also occasionally require a manifest update + // extLoadCmd := &cobra.Command{ + // Use: consts.LoadStr + " [EXT]", + // Short: "Load a command EXT", + // Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.LoadStr}), + // Args: cobra.ExactArgs(1), + // Run: func(cmd *cobra.Command, args []string) { + // extensions.ExtensionLoadCmd(cmd, con, args) + // }, + // } + // carapace.Gen(extLoadCmd).PositionalCompletion( + // carapace.ActionDirectories().Tag("ext directory").Usage("path to the ext directory")) + // extCmd.AddCommand(extLoadCmd) + */ extInstallCmd := &cobra.Command{ - Use: consts.InstallStr + " [EXT]", - Short: "Install a command ext", + Use: consts.InstallStr + " [filepath]", + Short: "Install an extension from a local directory/file.", Long: help.GetHelpFor([]string{consts.ExtensionsStr, consts.InstallStr}), Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { @@ -186,16 +190,15 @@ func ServerCommands(con *client.SliverConsoleClient, serverCmds func() []*cobra. extCmd.AddCommand(extInstallCmd) extendo := &cobra.Command{ - Use: consts.RmStr + " [EXT]", - Short: "Remove an ext", + Use: consts.RmStr + " [Name]", + Short: "Remove extension. Will remove all commands associated with the installed extension. Does not unload the extension from implants that have already loaded it, but removes the command from the client.", Long: help.GetHelpFor([]string{consts.RmStr}), Args: cobra.ExactArgs(1), Run: func(cmd *cobra.Command, args []string) { - //alias.AliasesRemoveCmd(cmd, con, args) extensions.ExtensionsRemoveCmd(cmd, con, args) }, } - carapace.Gen(extendo).PositionalCompletion(carapace.ActionFiles().Tag("ext I guess?")) + carapace.Gen(extendo).PositionalCompletion(extensions.ManifestCompleter()) extCmd.AddCommand(extendo) // [ Armory ] ---------------------------------------------