Skip to content

Commit b9f75a1

Browse files
authored
Support Multiple Authentication protocols (#114)
* update the cli tool logic * update the auth logic * add user description
1 parent 1e5431c commit b9f75a1

File tree

5 files changed

+117
-13
lines changed

5 files changed

+117
-13
lines changed

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Commands:
5656
## Usage: DAST Scans
5757

5858
```
59-
Usage: nullify dast [--app-name APP-NAME] [--spec-path SPEC-PATH] [--target-host TARGET-HOST] [--header HEADER] [--github-owner GITHUB-OWNER] [--github-repo GITHUB-REPO] [--local] [--image-label IMAGE-LABEL] [--force-pull] [--use-host-network]
59+
Usage: nullify dast [--app-name APP-NAME] [--spec-path SPEC-PATH] [--target-host TARGET-HOST] [--header HEADER] [--auth-config AUTH-CONFIG] [--github-owner GITHUB-OWNER] [--github-repo GITHUB-REPO] [--local] [--image-label IMAGE-LABEL] [--force-pull] [--use-host-network]
6060
6161
Options:
6262
--app-name APP-NAME The unique name of the app to be scanned, you can set this to anything e.g. Core API
@@ -65,6 +65,8 @@ Options:
6565
--target-host TARGET-HOST
6666
The base URL of the API to be scanned e.g. https://api.nullify.ai
6767
--header HEADER List of headers for the DAST agent to authenticate with your API, separated by commas e.g. "Authorization: Bearer 1234,X-Custom-Header: abcxyz"
68+
--auth-config AUTH-CONFIG
69+
The path to the auth config file
6870
--github-owner GITHUB-OWNER
6971
The GitHub username or organisation
7072
--github-repo GITHUB-REPO
@@ -160,4 +162,5 @@ The locally hosted scan can be run from within private networks to test private
160162
| **`header`** | List of headers for the DAST agent to authenticate with your API, string seperated by commas | `false` | |
161163
| **`local`** | Test the given app locally for bugs and vulnerabilities in private networks | `false` | |
162164
| **`version`** | Version of the DAST local image that is used for scanning [default: ] | `false` | latest |
165+
| **`auth-config`** | The path to the auth config file | `false` | |
163166

cmd/cli/main.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import (
1414
)
1515

1616
type args struct {
17-
DAST *dast.DAST `arg:"subcommand:dast" help:"Test the given app for bugs and vulnerabilities"`
18-
Host string `arg:"--host" default:"api.nullify.ai" help:"The base URL of your Nullify API instance"`
19-
Verbose bool `arg:"-v" help:"Enable verbose logging"`
20-
Debug bool `arg:"-d" help:"Enable debug logging"`
21-
17+
DAST *dast.DAST `arg:"subcommand:dast" help:"Test the given app for bugs and vulnerabilities"`
18+
Host string `arg:"--host" default:"api.nullify.ai" help:"The base URL of your Nullify API instance"`
19+
Verbose bool `arg:"-v" help:"Enable verbose logging"`
20+
Debug bool `arg:"-d" help:"Enable debug logging"`
21+
AuthConfig string `arg:"--auth-config" help:"The path to the auth config file"`
2222
models.AuthSources
2323
}
2424

internal/dast/dast.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ type DAST struct {
2323
ImageLabel string `arg:"--image-label" default:"latest" help:"Version of the DAST local image that is used for scanning"`
2424
ForcePullImage bool `arg:"--force-pull" help:"Force a docker pull of the latest version of the DAST local image"`
2525
UseHostNetwork bool `arg:"--use-host-network" help:"Use the host network for the DAST local scan"`
26+
27+
AuthConfig string `arg:"--auth-config" help:"The path to the auth config file"`
2628
}
2729

2830
func RunDASTScan(ctx context.Context, dast *DAST, nullifyClient *client.NullifyClient, logLevel string) error {
@@ -38,6 +40,21 @@ func RunDASTScan(ctx context.Context, dast *DAST, nullifyClient *client.NullifyC
3840
return err
3941
}
4042

43+
// Create auth config
44+
authConfig := models.AuthConfig{
45+
Headers: authHeaders,
46+
}
47+
48+
// Read auth config file
49+
if dast.AuthConfig != "" {
50+
fileAuthConfig, err := lib.ParseAuthConfig(ctx, dast.AuthConfig)
51+
if err != nil {
52+
logger.L(ctx).Error("failed to parse auth config", logger.Err(err))
53+
return err
54+
}
55+
authConfig = *fileAuthConfig
56+
}
57+
4158
if dast.Local {
4259
logger.L(ctx).Info("starting local scan")
4360
err = RunLocalScan(
@@ -49,9 +66,7 @@ func RunDASTScan(ctx context.Context, dast *DAST, nullifyClient *client.NullifyC
4966
AppName: dast.AppName,
5067
TargetHost: dast.TargetHost,
5168
OpenAPISpec: spec,
52-
AuthConfig: models.AuthConfig{
53-
Headers: authHeaders,
54-
},
69+
AuthConfig: authConfig,
5570
},
5671
dast.ImageLabel,
5772
dast.ForcePullImage,
@@ -69,9 +84,7 @@ func RunDASTScan(ctx context.Context, dast *DAST, nullifyClient *client.NullifyC
6984
Host: dast.TargetHost,
7085
TargetHost: dast.TargetHost,
7186
OpenAPISpec: spec,
72-
AuthConfig: models.AuthConfig{
73-
Headers: authHeaders,
74-
},
87+
AuthConfig: authConfig,
7588
RequestProvider: models.RequestProvider{
7689
GitHubOwner: dast.GitHubOwner,
7790
},

internal/lib/auth_config.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package lib
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"os"
8+
9+
"github.com/nullify-platform/cli/internal/models"
10+
"github.com/nullify-platform/logger/pkg/logger"
11+
)
12+
13+
// ParseAuthConfig reads and parses an authentication configuration file
14+
func ParseAuthConfig(ctx context.Context, configPath string) (*models.AuthConfig, error) {
15+
if configPath == "" {
16+
return &models.AuthConfig{}, nil
17+
}
18+
19+
logger.L(ctx).Debug("parsing auth config file", logger.String("path", configPath))
20+
21+
data, err := os.ReadFile(configPath)
22+
if err != nil {
23+
return nil, fmt.Errorf("failed to read auth config file: %w", err)
24+
}
25+
26+
var authConfig models.AuthConfig
27+
if err := json.Unmarshal(data, &authConfig); err != nil {
28+
return nil, fmt.Errorf("failed to parse auth config file: %w", err)
29+
}
30+
31+
return &authConfig, nil
32+
}

internal/models/auth.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,62 @@ type AuthSources struct {
55
GitHubToken string `json:"githubToken" arg:"--github-token" help:"GitHub actions job token to exchange for a Nullify API token"`
66
}
77

8+
type AuthMethod string
9+
10+
const (
11+
AuthMethodNone AuthMethod = "none"
12+
AuthMethodBasic AuthMethod = "basic"
13+
AuthMethodBearer AuthMethod = "bearer"
14+
AuthMethodSession AuthMethod = "session"
15+
AuthMethodOAuth AuthMethod = "oauth"
16+
AuthMethodSAML AuthMethod = "saml"
17+
AuthMethodJWT AuthMethod = "jwt"
18+
AuthMethodCustom AuthMethod = "custom"
19+
)
20+
21+
// AuthConfig represents the authentication configuration for Nullify DAST
822
type AuthConfig struct {
9-
Headers map[string]string `json:"headers"`
23+
// Single user authentication fields
24+
Method AuthMethod `json:"method,omitempty"`
25+
Username string `json:"username,omitempty"`
26+
UserDescription string `json:"userDescription,omitempty"`
27+
Headers map[string]string `json:"headers,omitempty"`
28+
Password string `json:"password,omitempty"`
29+
Token string `json:"token,omitempty"`
30+
ClientID string `json:"clientId,omitempty"`
31+
ClientSecret string `json:"clientSecret,omitempty"`
32+
TokenURL string `json:"tokenUrl,omitempty"`
33+
Scope string `json:"scope,omitempty"`
34+
LoginURL string `json:"loginUrl,omitempty"`
35+
LoginBody interface{} `json:"loginBody,omitempty"`
36+
LoginSelector string `json:"loginSelector,omitempty"`
37+
CustomHeaders map[string]string `json:"customHeaders,omitempty"`
38+
CustomParams map[string]interface{} `json:"customParams,omitempty"`
39+
40+
// Multi-user authentication fields
41+
AuthorizationModel bool `json:"authorizationModel,omitempty"`
42+
Users []UserAuth `json:"users,omitempty"`
43+
}
44+
45+
type UserAuth struct {
46+
RoleName string `json:"roleName"`
47+
RoleDescription string `json:"roleDescription,omitempty"`
48+
UserDescription string `json:"userDescription,omitempty"`
49+
AuthConfig MultiUserAuthConfig `json:"authConfig"`
50+
}
51+
52+
type MultiUserAuthConfig struct {
53+
Method AuthMethod `json:"method,omitempty"`
54+
Username string `json:"username,omitempty"`
55+
Password string `json:"password,omitempty"`
56+
Token string `json:"token,omitempty"`
57+
ClientID string `json:"clientId,omitempty"`
58+
ClientSecret string `json:"clientSecret,omitempty"`
59+
TokenURL string `json:"tokenUrl,omitempty"`
60+
Scope string `json:"scope,omitempty"`
61+
LoginURL string `json:"loginUrl,omitempty"`
62+
LoginBody interface{} `json:"loginBody,omitempty"`
63+
LoginSelector string `json:"loginSelector,omitempty"`
64+
CustomHeaders map[string]string `json:"customHeaders,omitempty"`
65+
CustomParams map[string]interface{} `json:"customParams,omitempty"`
1066
}

0 commit comments

Comments
 (0)