[Feat] Added Dropbox API OAuth2 Token Analyzer (#4080)

* temp

* temp

* adeded analysis info in dropbox detector

* removed unused function

* added scope capabilities in terminal results

* updated dropbox analyzer expected_output.json

* updated dropbox analyzer test expected output

* updated dropbox analyzer test expected output
This commit is contained in:
Nabeel Alam
2025-05-19 15:08:51 -05:00
committed by GitHub
parent 33f46b1236
commit e23cd77f84
10 changed files with 775 additions and 0 deletions
+2
View File
@@ -100,6 +100,7 @@ const (
AnalyzerTypeNgrok
AnalyzerTypeMux
AnalyzerTypePosthog
AnalyzerTypeDropbox
// Add new items here with AnalyzerType prefix
)
@@ -145,6 +146,7 @@ var analyzerTypeStrings = map[AnalyzerType]string{
AnalyzerTypeNgrok: "Ngrok",
AnalyzerTypeMux: "Mux",
AnalyzerTypePosthog: "Posthog",
AnalyzerTypeDropbox: "Dropbox",
// Add new mappings here
}
+354
View File
@@ -0,0 +1,354 @@
//go:generate generate_permissions permissions.yaml permissions.go dropbox
package dropbox
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"strings"
"github.com/fatih/color"
"github.com/jedib0t/go-pretty/v6/table"
_ "embed"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/config"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
)
var _ analyzers.Analyzer = (*Analyzer)(nil)
//go:embed scopes.json
var scopeConfigJson []byte
type Analyzer struct {
Cfg *config.Config
}
type PermissionStatus string
const (
StatusGranted PermissionStatus = "Granted"
StatusDenied PermissionStatus = "Denied"
StatusUnverified PermissionStatus = "Unverified"
)
func (a Analyzer) Type() analyzers.AnalyzerType {
return analyzers.AnalyzerTypeDropbox
}
func (a Analyzer) Analyze(_ context.Context, credInfo map[string]string) (*analyzers.AnalyzerResult, error) {
token, exist := credInfo["token"]
if !exist {
return nil, errors.New("token not found in credentials info")
}
info, err := AnalyzePermissions(a.Cfg, token)
if err != nil {
return nil, err
}
return secretInfoToAnalyzerResult(info), nil
}
func AnalyzeAndPrintPermissions(cfg *config.Config, token string) {
info, err := AnalyzePermissions(cfg, token)
if err != nil {
color.Red("[x] Invalid Dropbox Token\n")
color.Red("[x] Error : %s", err.Error())
return
}
if info == nil {
color.Red("[x] Error : %s", "No information found")
return
}
color.Green("[i] Valid Dropbox OAuth2 Credentials\n")
printAccountAndPermissions(info)
}
func AnalyzePermissions(cfg *config.Config, token string) (*secretInfo, error) {
// Dropbox API uses POST requests for all requests, so we need to use an unrestricted client
client := analyzers.NewAnalyzeClientUnrestricted(cfg)
scopeConfigMap, err := getScopeConfigMap()
if err != nil {
return nil, err
}
secretInfo := &secretInfo{}
accountInfoPermission := PermissionStrings[AccountInfoRead]
for _, perm := range PermissionStrings {
scopeDetails := scopeConfigMap.Scopes[perm]
status := StatusUnverified
if perm == accountInfoPermission {
// Account Info Read permission is always enabled
status = StatusGranted
}
secretInfo.Permissions = append(secretInfo.Permissions, accountPermission{
Name: perm,
Status: status,
Actions: scopeDetails.Actions,
})
}
if err := populateAccountInfo(client, secretInfo, token); err != nil {
return nil, err
}
if err := testAllPermissions(client, secretInfo, scopeConfigMap, token); err != nil {
return nil, err
}
return secretInfo, nil
}
func populateAccountInfo(client *http.Client, info *secretInfo, token string) error {
endpoint := "/2/users/get_current_account"
body, statusCode, err := callDropboxAPIEndpoint(client, endpoint, token)
if err != nil {
return err
}
switch statusCode {
case http.StatusOK:
if err := json.Unmarshal([]byte(body), &info.Account); err != nil {
return fmt.Errorf("failed to unmarshal account info: %w", err)
}
return nil
default:
return fmt.Errorf("failed to validate scope. Status %d: %s", statusCode, body)
}
}
func testAllPermissions(client *http.Client, info *secretInfo, scopeConfigMap *scopeConfig, token string) error {
permissionStatuses := make(map[string]PermissionStatus)
for _, perm := range PermissionStrings {
scopeDetails := scopeConfigMap.Scopes[perm]
if _, ok := permissionStatuses[perm]; ok || scopeDetails.TestEndpoint == "" {
// Skip if the scope has already been determined or has no test endpoint
continue
}
if perm == PermissionStrings[Openid] {
// The OpenID permission can be validated using the "/2/users/get_current_account" endpoint
// If the response contains the "email" key, that implies that the "email" permission is also granted
// Similar case for the "given_name" key and the "profile" permission
body, statusCode, err := callDropboxAPIEndpoint(client, scopeDetails.TestEndpoint, token)
if err != nil {
return err
}
switch statusCode {
case http.StatusOK, http.StatusConflict:
// The endpoint responds with 409 Conflict if the openid scope
// is granted but the email and profile scopes are not granted
permissionStatuses[perm] = StatusGranted
// Check for the "email" key in the response body
if strings.Contains(body, "\"email\":") {
permissionStatuses[PermissionStrings[Email]] = StatusGranted
} else {
permissionStatuses[PermissionStrings[Email]] = StatusDenied
}
// Check for the "given_name" key in the response body
if strings.Contains(body, "\"given_name\":") {
permissionStatuses[PermissionStrings[Profile]] = StatusGranted
} else {
permissionStatuses[PermissionStrings[Profile]] = StatusDenied
}
case http.StatusUnauthorized:
permissionStatuses[perm] = StatusDenied
permissionStatuses[PermissionStrings[Email]] = StatusDenied
permissionStatuses[PermissionStrings[Profile]] = StatusDenied
}
continue
}
isGranted, err := testPermission(client, scopeDetails.TestEndpoint, token)
if err != nil {
return err
}
if !isGranted {
permissionStatuses[perm] = StatusDenied
continue
}
permissionStatuses[perm] = StatusGranted
for _, impliedScope := range scopeDetails.ImpliedScopes {
permissionStatuses[impliedScope] = StatusGranted
}
}
for idx, permission := range info.Permissions {
permission.Status = permissionStatuses[permission.Name]
info.Permissions[idx] = permission
}
return nil
}
func testPermission(client *http.Client, testEndpoint string, token string) (bool, error) {
body, statusCode, err := callDropboxAPIEndpoint(client, testEndpoint, token)
if err != nil {
return false, err
}
switch statusCode {
case http.StatusUnauthorized:
return false, nil
case http.StatusBadRequest:
if strings.Contains(body, "does not have the required scope") {
return false, nil
}
if strings.Contains(body, "your request body is empty") {
return true, nil
}
}
return false, fmt.Errorf("failed to validate scope. Status %d: %s", statusCode, body)
}
func callDropboxAPIEndpoint(client *http.Client, endpoint string, token string) (string, int, error) {
baseURL := "https://api.dropboxapi.com"
req, err := http.NewRequest(http.MethodPost, baseURL+endpoint, nil)
if err != nil {
return "", 0, err
}
req.Header.Set("Authorization", "Bearer "+token)
res, err := client.Do(req)
if err != nil {
return "", 0, err
}
defer func() {
_, _ = io.Copy(io.Discard, res.Body)
_ = res.Body.Close()
}()
bodyBytes, err := io.ReadAll(res.Body)
if err != nil {
return "", 0, fmt.Errorf("failed to read response body: %w", err)
}
return string(bodyBytes), res.StatusCode, nil
}
func getScopeConfigMap() (*scopeConfig, error) {
var scopeConfigMap scopeConfig
if err := json.Unmarshal(scopeConfigJson, &scopeConfigMap); err != nil {
return nil, errors.New("failed to unmarshal scopes.json: " + err.Error())
}
return &scopeConfigMap, nil
}
func secretInfoToAnalyzerResult(info *secretInfo) *analyzers.AnalyzerResult {
if info == nil {
return nil
}
account := info.Account
accountID := account.AccountID
allPermissions := getValidatedPermissions(info)
resource := analyzers.Resource{
Name: fmt.Sprintf("%s %s", account.Name.GivenName, account.Name.Surname),
FullyQualifiedName: accountID,
Type: "account",
Metadata: map[string]any{
"email": account.Email,
"emailVerified": account.EmailVerified,
"disabled": account.Disabled,
"country": account.Country,
"accountType": account.AccountType.Tag,
},
}
analyzers.BindAllPermissions(resource, allPermissions...)
result := analyzers.AnalyzerResult{
AnalyzerType: analyzers.AnalyzerTypeDropbox,
Metadata: nil,
Bindings: analyzers.BindAllPermissions(resource, allPermissions...),
}
return &result
}
func getValidatedPermissions(info *secretInfo) []analyzers.Permission {
permissions := []analyzers.Permission{}
for _, permission := range info.Permissions {
if permission.Status != StatusGranted {
continue
}
permissions = append(permissions, analyzers.Permission{
Value: permission.Name,
})
}
return permissions
}
func printAccountAndPermissions(info *secretInfo) {
color.Yellow("\n[i] Accounts Info:")
t1 := table.NewWriter()
t1.SetOutputMirror(os.Stdout)
t1.AppendHeader(table.Row{"ID", "Name", "Email", "Email Verified", "Disabled", "Country", "Account Type"})
emailVerified := "No"
disabled := "No"
if info.Account.EmailVerified {
emailVerified = "Yes"
}
if info.Account.Disabled {
disabled = "Yes"
}
t1.AppendRow(table.Row{
color.GreenString(info.Account.AccountID),
color.GreenString(info.Account.Name.GivenName + " " + info.Account.Name.Surname),
color.GreenString(info.Account.Email),
color.GreenString(emailVerified),
color.GreenString(disabled),
color.GreenString(info.Account.Country),
color.GreenString(info.Account.AccountType.Tag),
})
t1.SetOutputMirror(os.Stdout)
t1.Render()
color.Yellow("\n[i] Permissions:")
t2 := table.NewWriter()
t2.AppendHeader(table.Row{"Permission", "Access", "Actions"})
permissions := info.Permissions
for _, permission := range permissions {
access := "Denied"
permissionStatus := permission.Status
if permissionStatus == StatusGranted {
access = "Granted"
}
if permissionStatus == StatusUnverified {
access = "Unverified"
}
for idx, action := range permission.Actions {
permissionCell := ""
accessCell := ""
if idx == 0 {
permissionCell = color.GreenString(permission.Name)
accessCell = color.GreenString(access)
}
t2.AppendRow(table.Row{
permissionCell,
accessCell,
action,
})
}
t2.AppendSeparator()
}
t2.SetOutputMirror(os.Stdout)
t2.Render()
fmt.Printf("%s: https://www.dropbox.com/developers/documentation\n\n", color.GreenString("Ref"))
}
@@ -0,0 +1,77 @@
package dropbox
import (
_ "embed"
"encoding/json"
"fmt"
"testing"
"time"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/config"
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
)
//go:embed expected_output.json
var expectedOutput []byte
func TestAnalyzer_Analyze(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*5)
defer cancel()
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors5")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
token := testSecrets.MustGetField("DROPBOX")
tests := []struct {
name string
secret string
want string
wantErr bool
}{
{
name: "valid dropbox credentials",
secret: token,
want: string(expectedOutput),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
a := Analyzer{Cfg: &config.Config{}}
got, err := a.Analyze(ctx, map[string]string{
"token": tt.secret,
})
if (err != nil) != tt.wantErr {
t.Errorf("Analyzer.Analyze() error = %v, wantErr %v", err, tt.wantErr)
return
}
// marshal the actual result to JSON
gotJSON, err := json.Marshal(got)
if err != nil {
t.Fatalf("could not marshal got to JSON: %s", err)
}
fmt.Println(string(gotJSON))
// compare the JSON strings
if string(gotJSON) != string(tt.want) {
// pretty-print both JSON strings for easier comparison
var gotIndented, wantIndented []byte
gotIndented, err = json.MarshalIndent(got, "", " ")
if err != nil {
t.Fatalf("could not marshal got to indented JSON: %s", err)
}
wantIndented, err = json.MarshalIndent(tt.want, "", " ")
if err != nil {
t.Fatalf("could not marshal want to indented JSON: %s", err)
}
t.Errorf("Analyzer.Analyze() = %s, want %s", gotIndented, wantIndented)
}
})
}
}
@@ -0,0 +1 @@
{"AnalyzerType":40,"Bindings":[{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"accounts_info.read","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"files.metadata.write","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"sharing.read","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"contacts.read","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"files.content.read","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"sharing.write","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"contacts.write","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"files.metadata.read","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"files.content.write","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"openid","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"file_requests.read","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"file_requests.write","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"account_info.write","Parent":null}},{"Resource":{"Name":"Truffle Detectors","FullyQualifiedName":"dbid:AACfhSAzNq2rEGFtyIKeEchJumee8_A8Iq0","Type":"account","Metadata":{"accountType":"basic","country":"PK","disabled":false,"email":"[email protected]","emailVerified":true},"Parent":null},"Permission":{"Value":"profile","Parent":null}}],"UnboundedResources":null,"Metadata":null}
+41
View File
@@ -0,0 +1,41 @@
package dropbox
type scopeConfig struct {
Scopes map[string]scope `json:"scopes"`
}
type scope struct {
TestEndpoint string `json:"test_endpoint"`
ImpliedScopes []string `json:"implied_scopes"`
Actions []string `json:"actions"`
}
type account struct {
AccountID string `json:"account_id"`
Name name `json:"name"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Disabled bool `json:"disabled"`
Country string `json:"country"`
AccountType accountType `json:"account_type"`
}
type accountType struct {
Tag string `json:".tag"`
}
type name struct {
GivenName string `json:"given_name"`
Surname string `json:"surname"`
}
type accountPermission struct {
Name string
Status PermissionStatus
Actions []string
}
type secretInfo struct {
Account account
Permissions []accountPermission
}
@@ -0,0 +1,131 @@
// Code generated by go generate; DO NOT EDIT.
package dropbox
import "errors"
type Permission int
const (
Invalid Permission = iota
AccountInfoWrite Permission = iota
AccountInfoRead Permission = iota
FilesMetadataWrite Permission = iota
FilesMetadataRead Permission = iota
FilesContentWrite Permission = iota
FilesContentRead Permission = iota
SharingWrite Permission = iota
SharingRead Permission = iota
FileRequestsWrite Permission = iota
FileRequestsRead Permission = iota
ContactsWrite Permission = iota
ContactsRead Permission = iota
Openid Permission = iota
Profile Permission = iota
Email Permission = iota
)
var (
PermissionStrings = map[Permission]string{
AccountInfoWrite: "account_info.write",
AccountInfoRead: "account_info.read",
FilesMetadataWrite: "files.metadata.write",
FilesMetadataRead: "files.metadata.read",
FilesContentWrite: "files.content.write",
FilesContentRead: "files.content.read",
SharingWrite: "sharing.write",
SharingRead: "sharing.read",
FileRequestsWrite: "file_requests.write",
FileRequestsRead: "file_requests.read",
ContactsWrite: "contacts.write",
ContactsRead: "contacts.read",
Openid: "openid",
Profile: "profile",
Email: "email",
}
StringToPermission = map[string]Permission{
"account_info.write": AccountInfoWrite,
"account_info.read": AccountInfoRead,
"files.metadata.write": FilesMetadataWrite,
"files.metadata.read": FilesMetadataRead,
"files.content.write": FilesContentWrite,
"files.content.read": FilesContentRead,
"sharing.write": SharingWrite,
"sharing.read": SharingRead,
"file_requests.write": FileRequestsWrite,
"file_requests.read": FileRequestsRead,
"contacts.write": ContactsWrite,
"contacts.read": ContactsRead,
"openid": Openid,
"profile": Profile,
"email": Email,
}
PermissionIDs = map[Permission]int{
AccountInfoWrite: 1,
AccountInfoRead: 2,
FilesMetadataWrite: 3,
FilesMetadataRead: 4,
FilesContentWrite: 5,
FilesContentRead: 6,
SharingWrite: 7,
SharingRead: 8,
FileRequestsWrite: 9,
FileRequestsRead: 10,
ContactsWrite: 11,
ContactsRead: 12,
Openid: 13,
Profile: 14,
Email: 15,
}
IdToPermission = map[int]Permission{
1: AccountInfoWrite,
2: AccountInfoRead,
3: FilesMetadataWrite,
4: FilesMetadataRead,
5: FilesContentWrite,
6: FilesContentRead,
7: SharingWrite,
8: SharingRead,
9: FileRequestsWrite,
10: FileRequestsRead,
11: ContactsWrite,
12: ContactsRead,
13: Openid,
14: Profile,
15: Email,
}
)
// ToString converts a Permission enum to its string representation
func (p Permission) ToString() (string, error) {
if str, ok := PermissionStrings[p]; ok {
return str, nil
}
return "", errors.New("invalid permission")
}
// ToID converts a Permission enum to its ID
func (p Permission) ToID() (int, error) {
if id, ok := PermissionIDs[p]; ok {
return id, nil
}
return 0, errors.New("invalid permission")
}
// PermissionFromString converts a string representation to its Permission enum
func PermissionFromString(s string) (Permission, error) {
if p, ok := StringToPermission[s]; ok {
return p, nil
}
return 0, errors.New("invalid permission string")
}
// PermissionFromID converts an ID to its Permission enum
func PermissionFromID(id int) (Permission, error) {
if p, ok := IdToPermission[id]; ok {
return p, nil
}
return 0, errors.New("invalid permission ID")
}
@@ -0,0 +1,16 @@
permissions:
- account_info.write
- account_info.read
- files.metadata.write
- files.metadata.read
- files.content.write
- files.content.read
- sharing.write
- sharing.read
- file_requests.write
- file_requests.read
- contacts.write
- contacts.read
- openid
- profile
- email
+147
View File
@@ -0,0 +1,147 @@
{
"scopes": {
"account_info.write": {
"test_endpoint": "/2/account/set_profile_photo",
"actions": [
"Set a user's profile photo"
]
},
"account_info.read": {
"test_endpoint": "/2/account/set_profile_photo",
"actions": [
"Validate user access token",
"Get a list of feature values for the current account",
"Get information about the current user's account",
"Get the space usage information for the current user's account"
]
},
"files.metadata.write": {
"test_endpoint": "/2/file_properties/properties/add",
"implied_scopes": [
"files.metadata.read"
],
"actions": [
"Add, update or remove property groups associated with files",
"Add, update or remove properties associated with files and templates",
"Add, update or remove templates associated with a user",
"Add or remove tags from items"
]
},
"files.metadata.read": {
"test_endpoint": "/2/file_properties/properties/search",
"actions": [
"Search across property templates for particular property field values",
"Get the schema for a specified template",
"Get the template identifiers for a team",
"Get the metadata for a file or folder",
"Get files, revisions, and folder contents",
"Monitor for file changes",
"Get tags from items",
"Get file metadata",
"Get user templates",
"Get user Paper docs"
]
},
"files.content.write": {
"test_endpoint": "/2/files/copy_v2",
"implied_scopes": [
"files.metadata.read"
],
"actions": [
"Add, update, move, or remove files",
"Add, update, move, or remove folders",
"Upload file content",
"Lock/unlock files for writing",
"Restore files to previous versions",
"Add, update, or archive Paper docs",
"Save URLs to Dropbox"
]
},
"files.content.read": {
"test_endpoint": "/2/files/get_file_lock_batch",
"actions": [
"Export or download files",
"Get lock information for files and folders",
"Get file previews",
"Stream file content",
"Get image file thumbnails",
"Export or download Paper docs"
]
},
"sharing.write": {
"test_endpoint": "/2/sharing/add_file_member",
"implied_scopes": [
"sharing.read"
],
"actions": [
"Add, update, or remove file members",
"Add, update, or remove folder members",
"Get status of all asynchronous jobs",
"Add, update, or remove shared links",
"Share or unshare folders",
"Add, update, or remove shared folder access policies",
"Mount or unmount folders",
"Add or remove users from Paper docs"
]
},
"sharing.read": {
"test_endpoint": "/2/sharing/get_file_metadata",
"actions": [
"Get file metadata",
"Get folder metadata",
"Get shared link metadata",
"Get file members",
"Get folder members",
"Get shared files",
"Get shared folders",
"Get mountable shared folders",
"Get shared links",
"Get information about the user's account",
"Get file and folder information for Paper doc",
"Get all users with Paper doc access"
]
},
"file_requests.write": {
"test_endpoint": "/2/file_requests/update",
"implied_scopes": [
"file_requests.read"
],
"actions": [
"Add, update, or remove file requests"
]
},
"file_requests.read": {
"test_endpoint": "/2/file_requests/list/continue",
"actions": [
"Get file requests",
"Get file request count"
]
},
"contacts.write": {
"test_endpoint": "/2/contacts/delete_manual_contacts_batch",
"implied_scopes": [
"contacts.read"
],
"actions": [
"Remove manually added contacts"
]
},
"contacts.read": {},
"openid": {
"test_endpoint": "/2/openid/userinfo",
"actions": [
"Get OpenID Connect user info"
]
},
"profile": {
"actions": [
"Get name in user info"
]
},
"email": {
"actions": [
"Get email address in user info"
]
}
}
}
+3
View File
@@ -13,6 +13,7 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/bitbucket"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/digitalocean"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/dockerhub"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/dropbox"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/elevenlabs"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/fastly"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers/figma"
@@ -139,5 +140,7 @@ func Run(keyType string, secretInfo SecretInfo) {
mux.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"], secretInfo.Parts["secret"])
case "posthog":
posthog.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"])
case "dropbox":
dropbox.AnalyzeAndPrintPermissions(secretInfo.Cfg, secretInfo.Parts["key"])
}
}
+3
View File
@@ -57,6 +57,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
isVerified, verificationErr := verifyDropboxToken(ctx, client, key)
s1.Verified = isVerified
s1.SetVerificationError(verificationErr)
if s1.Verified {
s1.AnalysisInfo = map[string]string{"token": key}
}
}
results = append(results, s1)