Skip to content

Commit

Permalink
Merge pull request #51 from cerberauth/cli-token-generate
Browse files Browse the repository at this point in the history
Cli token generate
  • Loading branch information
emmanuelgautier authored Feb 28, 2024
2 parents 0058c93 + 0a2e591 commit e9465cd
Show file tree
Hide file tree
Showing 16 changed files with 229 additions and 71 deletions.
124 changes: 124 additions & 0 deletions cmd/jwt/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package jwt

import (
"fmt"
"os"
"strings"

"github.com/cerberauth/vulnapi/jwt"
jwtlib "github.com/golang-jwt/jwt/v5"
"github.com/spf13/cobra"
)

type Algorithm string

const (
None Algorithm = "NONE"
HS256 Algorithm = "HS256"
HS384 Algorithm = "HS384"
HS512 Algorithm = "HS512"
RS256 Algorithm = "RS256"
RS384 Algorithm = "RS384"
RS512 Algorithm = "RS512"
ES256 Algorithm = "ES256"
ES384 Algorithm = "ES384"
)

var (
secret string
alg string
aud string
)

func GetAlgorithm(alg string) (jwtlib.SigningMethod, error) {
switch strings.ToUpper(alg) {
case string(HS256):
return jwtlib.SigningMethodHS256, nil
case string(HS384):
return jwtlib.SigningMethodHS384, nil
case string(HS512):
return jwtlib.SigningMethodHS512, nil
case string(RS256):
return jwtlib.SigningMethodRS256, nil
case string(RS384):
return jwtlib.SigningMethodRS384, nil
case string(RS512):
return jwtlib.SigningMethodRS512, nil
case string(ES256):
return jwtlib.SigningMethodES256, nil
case string(ES384):
return jwtlib.SigningMethodES384, nil
case string(None):
return jwtlib.SigningMethodNone, nil
default:
return nil, fmt.Errorf("invalid algorithm: %s", alg)
}
}

func NewRootCmd() (cmd *cobra.Command) {
rootCmd := &cobra.Command{
Use: "jwt",
Short: "Generate JWT tokens",
}

generateCmd := &cobra.Command{
Use: "generate [token]",
Short: "Generate a new JWT token from an existing token",
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
tokenString := args[0]
var key interface{}
var newTokenString string
tokenWriter, err := jwt.NewJWTWriter(tokenString)
if err != nil {
fmt.Println("Error:", err)
return
}

if secret != "" {
key = []byte(secret)
}

var signingMethod jwtlib.SigningMethod
if alg != "" {
if signingMethod, err = GetAlgorithm(alg); err != nil {
fmt.Println("Error:", err)
return
}
}

if signingMethod == jwtlib.SigningMethodNone {
key = jwtlib.UnsafeAllowNoneSignatureType
}

if signingMethod == nil || key == nil {
fmt.Println("Error: algorithm and secret are required")
return
}

newTokenString, err = tokenWriter.SignWithMethodAndKey(signingMethod, key)
if err != nil {
fmt.Println("Error:", err)
return
}

fmt.Println(newTokenString)
},
}

generateCmd.PersistentFlags().StringVarP(&secret, "secret", "", "", "Secret key to sign the token")
generateCmd.PersistentFlags().StringVarP(&alg, "alg", "", "", "Algorithm to sign the token")
generateCmd.PersistentFlags().StringVarP(&aud, "aud", "", "", "Audience of the token")

rootCmd.AddCommand(generateCmd)

return rootCmd
}

func Execute() {
c := NewRootCmd()

if err := c.Execute(); err != nil {
os.Exit(1)
}
}
6 changes: 3 additions & 3 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@ import (

"github.com/spf13/cobra"

"github.com/cerberauth/vulnapi/cmd/jwt"
"github.com/cerberauth/vulnapi/cmd/scan"
)

func NewRootCmd() (cmd *cobra.Command) {
var rootCmd = &cobra.Command{
rootCmd := &cobra.Command{
Use: "vulnapi",
Short: "vulnapi",
}
rootCmd.AddCommand(scan.NewScanCmd())
rootCmd.AddCommand(jwt.NewRootCmd())

return rootCmd
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the RootCmd.
func Execute() {
c := NewRootCmd()

Expand Down
10 changes: 10 additions & 0 deletions internal/auth/bearer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package auth
import (
"fmt"
"net/http"

"github.com/cerberauth/vulnapi/jwt"
)

type BearerSecurityScheme struct {
Expand All @@ -11,18 +13,22 @@ type BearerSecurityScheme struct {
In SchemeIn
Name string
ValidValue *string
TokenWriter *jwt.JWTWriter
AttackValue string
}

var _ SecurityScheme = (*BearerSecurityScheme)(nil)

func NewAuthorizationBearerSecurityScheme(name string, value *string) *BearerSecurityScheme {
tokenWriter, _ := jwt.NewJWTWriter(*value)

return &BearerSecurityScheme{
Type: HttpType,
Scheme: BearerScheme,
In: InHeader,
Name: name,
ValidValue: value,
TokenWriter: tokenWriter,
AttackValue: "",
}
}
Expand All @@ -44,6 +50,10 @@ func (ss *BearerSecurityScheme) GetValidValue() interface{} {
return *ss.ValidValue
}

func (ss *BearerSecurityScheme) GetValidValueWriter() interface{} {
return ss.TokenWriter
}

func (ss *BearerSecurityScheme) SetAttackValue(v interface{}) {
ss.AttackValue = v.(string)
}
Expand Down
11 changes: 11 additions & 0 deletions internal/auth/bearer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ func TestBearerSecurityScheme_GetValidValue(t *testing.T) {
assert.Equal(t, value, validValue)
}

func TestBearerSecurityScheme_GetValidValueWriter(t *testing.T) {
name := "token"
value := "abc123"

ss := auth.NewAuthorizationBearerSecurityScheme(name, &value)

writer := ss.GetValidValueWriter()

assert.Equal(t, ss.TokenWriter, writer)
}

func TestBearerSecurityScheme_SetAttackValue(t *testing.T) {
name := "token"
value := "abc123"
Expand Down
5 changes: 5 additions & 0 deletions internal/auth/security_scheme.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type SecurityScheme interface {
GetHeaders() http.Header
GetCookies() []*http.Cookie
GetValidValue() interface{}
GetValidValueWriter() interface{}
SetAttackValue(v interface{})
GetAttackValue() interface{}
}
Expand All @@ -41,6 +42,10 @@ func (ss *NoAuthSecurityScheme) GetValidValue() interface{} {
return ""
}

func (ss *NoAuthSecurityScheme) GetValidValueWriter() interface{} {
return ""
}

func (ss *NoAuthSecurityScheme) SetAttackValue(v interface{}) {}

func (ss *NoAuthSecurityScheme) GetAttackValue() interface{} {
Expand Down
6 changes: 6 additions & 0 deletions internal/auth/security_scheme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ func TestNoAuthSecurityScheme_GetValidValue(t *testing.T) {
assert.Equal(t, "", validValue)
}

func TestNoAuthSecurityScheme_GetValidValueWriter(t *testing.T) {
ss := &auth.NoAuthSecurityScheme{}
validValueWriter := ss.GetValidValueWriter()
assert.Equal(t, "", validValueWriter)
}

func TestNoAuthSecurityScheme_SetAttackValue(t *testing.T) {
ss := &auth.NoAuthSecurityScheme{}
ss.SetAttackValue("attack value")
Expand Down
7 changes: 7 additions & 0 deletions jwt/alg_none.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package jwt

import jwtlib "github.com/golang-jwt/jwt/v5"

func (j *JWTWriter) WithAlgNone() (string, error) {
return j.SignWithMethodAndKey(jwtlib.SigningMethodNone, jwtlib.UnsafeAllowNoneSignatureType)
}
34 changes: 34 additions & 0 deletions jwt/jwt_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package jwt

import "github.com/golang-jwt/jwt/v5"

type JWTWriter struct {
Token *jwt.Token
}

func NewJWTWriter(token string) (*JWTWriter, error) {
// Parse the original JWT token
originalToken, _, err := new(jwt.Parser).ParseUnverified(token, jwt.MapClaims{})
if err != nil {
return nil, err
}

return &JWTWriter{Token: originalToken}, nil
}

func (j *JWTWriter) SignWithMethodAndKey(method jwt.SigningMethod, key interface{}) (string, error) {
// Create a new token with the new claims
newToken := jwt.NewWithClaims(method, j.Token.Claims)

// Sign the new token with the new secret key
tokenString, err := newToken.SignedString(key)
if err != nil {
return "", err
}

return tokenString, nil
}

func (j *JWTWriter) SignWithKey(key interface{}) (string, error) {
return j.SignWithMethodAndKey(j.Token.Method, key)
}
13 changes: 13 additions & 0 deletions jwt/without_signature.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package jwt

import "strings"

func (j *JWTWriter) WithoutSignature() (string, error) {
newTokenString, err := j.SignWithMethodAndKey(j.Token.Method, []byte(""))
if err != nil {
return "", err
}

parts := strings.Split(newTokenString, ".")
return strings.Join([]string{parts[0], parts[1], ""}, "."), nil
}
3 changes: 1 addition & 2 deletions scan/best_practices/http_headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,8 @@ func CheckCORSAllowOrigin(operation *request.Operation, headers http.Header, r *

func HTTPHeadersBestPracticesScanHandler(operation *request.Operation, ss auth.SecurityScheme) (*report.ScanReport, error) {
r := report.NewScanReport()
token := ss.GetValidValue().(string)

ss.SetAttackValue(token)
ss.SetAttackValue(ss.GetValidValue())
vsa, err := scan.ScanURL(operation, &ss)
r.AddScanAttempt(vsa).End()
if err != nil {
Expand Down
3 changes: 1 addition & 2 deletions scan/best_practices/http_trace_method.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ func HTTPTraceMethodScanHandler(operation *request.Operation, ss auth.SecuritySc
newOperation := operation.Clone()
newOperation.Method = "TRACE"

token := ss.GetValidValue().(string)
ss.SetAttackValue(token)
ss.SetAttackValue(ss.GetValidValue())
vsa, err := scan.ScanURL(newOperation, &ss)
r.AddScanAttempt(vsa).End()
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions scan/jwt/alg_none.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import (
"github.com/cerberauth/vulnapi/internal/auth"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/internal/scan"
"github.com/cerberauth/vulnapi/jwt"
"github.com/cerberauth/vulnapi/report"
"github.com/golang-jwt/jwt/v5"
)

const (
Expand All @@ -16,9 +16,9 @@ const (

func AlgNoneJwtScanHandler(operation *request.Operation, ss auth.SecurityScheme) (*report.ScanReport, error) {
r := report.NewScanReport()
token := ss.GetValidValue().(string)
token := ss.GetValidValueWriter().(*jwt.JWTWriter)

newToken, err := createNewJWTWithClaimsAndMethod(token, jwt.SigningMethodNone, jwt.UnsafeAllowNoneSignatureType)
newToken, err := token.WithAlgNone()
if err != nil {
return r, err
}
Expand Down
9 changes: 5 additions & 4 deletions scan/jwt/not_verified.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import (
"github.com/cerberauth/vulnapi/internal/auth"
"github.com/cerberauth/vulnapi/internal/request"
"github.com/cerberauth/vulnapi/internal/scan"
"github.com/cerberauth/vulnapi/jwt"
"github.com/cerberauth/vulnapi/report"
"github.com/golang-jwt/jwt/v5"
jwtlib "github.com/golang-jwt/jwt/v5"
)

const (
Expand All @@ -16,14 +17,14 @@ const (

func NotVerifiedScanHandler(operation *request.Operation, ss auth.SecurityScheme) (*report.ScanReport, error) {
r := report.NewScanReport()
token := ss.GetValidValue().(string)
token := ss.GetValidValueWriter().(*jwt.JWTWriter)

newTokenA, err := createNewJWTWithClaimsAndMethod(token, jwt.SigningMethodHS256, []byte("a"))
newTokenA, err := token.SignWithMethodAndKey(jwtlib.SigningMethodHS256, []byte("a"))
if err != nil {
return r, err
}

newTokenB, err := createNewJWTWithClaimsAndMethod(token, jwt.SigningMethodHS256, []byte("b"))
newTokenB, err := token.SignWithMethodAndKey(jwtlib.SigningMethodHS256, []byte("b"))
if err != nil {
return r, err
}
Expand Down
Loading

0 comments on commit e9465cd

Please sign in to comment.