[analyze] Add Analyzer for Slack (#3207)

* implement analyzer interface for slack

* slack analyzer adjusted for new changes in main, unit test added

* link detector with analyzer for slack

* added generated permissions for slack analyzer

* generate permission fix, keep dot in permissions intact

* removed scope from permission and put it metadata.

* [chore]
moved expected output of test in json file to neat the code.
added team id in fully qualified name of user resource.
check permissions before adding it in bindings.

---------

Co-authored-by: Abdul Basit <abasit@folio3.com>
This commit is contained in:
Abdul Basit 2024-09-07 00:33:06 +05:00 committed by GitHub
parent c449129d75
commit f235b8a442
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 2814 additions and 0 deletions

View file

@ -0,0 +1,667 @@
{
"AnalyzerType": 16,
"Bindings": [
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "conversations.history",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "conversations.replies",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "channels.info",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "conversations.info",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "conversations.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "conversations.members",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "groups.info",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "im.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "mpim.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "users.conversations",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "emoji.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "files.info",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "files.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "stars.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "pins.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "usergroups.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "usergroups.users.list",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "dnd.info",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "dnd.teamInfo",
"Parent": null
}
},
{
"Resource": {
"Name": "marge.haskell.bridge",
"FullyQualifiedName": "TSMCXP5FH/USMD5JM0F",
"Type": "user",
"Metadata": {
"scopes": [
"identify",
"channels:history",
"groups:history",
"im:history",
"channels:read",
"emoji:read",
"files:read",
"groups:read",
"im:read",
"stars:read",
"pins:read",
"usergroups:read",
"dnd:read",
"calls:read"
],
"team": "ct.org",
"team_id": "TSMCXP5FH",
"url": "https://ctorgworkspace.slack.com/"
},
"Parent": null
},
"Permission": {
"Value": "calls.info",
"Parent": null
}
}
],
"UnboundedResources": null,
"Metadata": null
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,263 @@
permissions:
- admin.analytics.getFile
- admin.apps.activities.list
- admin.apps.approve
- admin.apps.clearResolution
- admin.apps.config.set
- admin.apps.requests.cancel
- admin.apps.restrict
- admin.apps.uninstall
- admin.apps.approved.list
- admin.apps.config.lookup
- admin.apps.requests.list
- admin.apps.restricted.list
- admin.auth.policy.assignEntities
- admin.auth.policy.removeEntities
- admin.users.assign
- admin.users.invite
- admin.users.remove
- admin.users.session.clearSettings
- admin.users.session.invalidate
- admin.users.session.reset
- admin.users.session.resetBulk
- admin.users.session.setSettings
- admin.users.setAdmin
- admin.users.setExpiration
- admin.users.setOwner
- admin.users.setRegular
- admin.auth.policy.getEntities
- admin.users.list
- admin.users.session.getSettings
- admin.users.session.list
- admin.users.unsupportedVersions.export
- admin.barriers.create
- admin.barriers.delete
- admin.barriers.update
- admin.barriers.list
- admin.conversations.archive
- admin.conversations.bulkArchive
- admin.conversations.bulkDelete
- admin.conversations.bulkMove
- admin.conversations.convertToPrivate
- admin.conversations.convertToPublic
- admin.conversations.create
- admin.conversations.delete
- admin.conversations.disconnectShared
- admin.conversations.invite
- admin.conversations.removeCustomRetention
- admin.conversations.rename
- admin.conversations.restrictAccess.addGroup
- admin.conversations.restrictAccess.removeGroup
- admin.conversations.setConversationPrefs
- admin.conversations.setCustomRetention
- admin.conversations.setTeams
- admin.conversations.unarchive
- admin.conversations.ekm.listOriginalConnectedChannelInfo
- admin.conversations.getConversationPrefs
- admin.conversations.getCustomRetention
- admin.conversations.getTeams
- admin.conversations.lookup
- admin.conversations.restrictAccess.listGroups
- admin.conversations.search
- admin.emoji.add
- admin.emoji.addAlias
- admin.emoji.remove
- admin.teams.create
- admin.teams.settings.setDefaultChannels
- admin.teams.settings.setDescription
- admin.teams.settings.setDiscoverability
- admin.teams.settings.setIcon
- admin.teams.settings.setName
- admin.usergroups.addTeams
- admin.emoji.list
- admin.teams.admins.list
- admin.teams.list
- admin.teams.owners.list
- admin.teams.settings.info
- admin.functions.list
- admin.functions.permissions.lookup
- admin.workflows.permissions.lookup
- admin.workflows.search
- admin.functions.permissions.set
- admin.workflows.collaborators.add
- admin.workflows.collaborators.remove
- admin.workflows.unpublish
- admin.inviteRequests.approve
- admin.inviteRequests.deny
- admin.inviteRequests.approved.list
- admin.inviteRequests.denied.list
- admin.inviteRequests.list
- admin.roles.addAssignments
- admin.roles.removeAssignments
- admin.roles.listAssignments
- admin.usergroups.addChannels
- admin.usergroups.removeChannels
- admin.usergroups.listChannels
- apps.activities.list
- apps.connections.open
- token
- apps.datastore.bulkDelete
- apps.datastore.bulkGet
- apps.datastore.bulkPut
- apps.datastore.delete
- apps.datastore.get
- apps.datastore.put
- apps.datastore.query
- apps.datastore.update
- apps.datastore.count
- apps.event.authorizations.list
- bot
- auth.revoke
- auth.test
- chat.getPermalink
- chat.scheduledMessages.list
- dialog.open
- functions.completeError
- functions.completeSuccess
- rtm.connect
- rtm.start
- views.open
- views.publish
- views.push
- views.update
- bookmarks.add
- bookmarks.edit
- bookmarks.remove
- bookmarks.list
- bots.info
- users.getPresence
- users.info
- users.list
- calls.add
- calls.end
- calls.participants.add
- calls.participants.remove
- calls.update
- calls.info
- channels.create
- channels.mark
- conversations.archive
- conversations.close
- conversations.create
- conversations.kick
- conversations.leave
- conversations.mark
- conversations.open
- conversations.rename
- conversations.unarchive
- groups.create
- groups.mark
- im.mark
- im.open
- mpim.mark
- mpim.open
- channels.info
- conversations.info
- conversations.list
- conversations.members
- groups.info
- im.list
- mpim.list
- users.conversations
- channels.invite
- conversations.invite
- groups.invite
- chat.delete
- chat.deleteScheduledMessage
- chat.meMessage
- chat.postEphemeral
- chat.postMessage
- chat.scheduleMessage
- chat.update
- chat.unfurl
- conversations.acceptSharedInvite
- conversations.inviteShared
- conversations.approveSharedInvite
- conversations.declineSharedInvite
- conversations.listConnectInvites
- conversations.history
- conversations.replies
- conversations.join
- conversations.setPurpose
- conversations.setTopic
- dnd.endDnd
- dnd.endSnooze
- dnd.setSnooze
- dnd.info
- dnd.teamInfo
- emoji.list
- files.comments.delete
- files.completeUploadExternal
- files.delete
- files.getUploadURLExternal
- files.revokePublicURL
- files.sharedPublicURL
- files.upload
- files.info
- files.list
- files.remote.add
- files.remote.remove
- files.remote.update
- files.remote.info
- files.remote.list
- files.remote.share
- functions.distributions.permissions.add
- functions.distributions.permissions.remove
- functions.distributions.permissions.set
- functions.distributions.permissions.list
- conversations
- groups.open
- tokens.basic
- migration.exchange
- email
- openid.connect.userInfo
- pins.add
- pins.remove
- pins.list
- reactions.add
- reactions.remove
- reactions.get
- reactions.list
- reminders.add
- reminders.complete
- reminders.delete
- reminders.info
- reminders.list
- search.all
- search.files
- search.messages
- stars.add
- stars.remove
- stars.list
- admin
- team.accessLogs
- team.billableInfo
- team.integrationLogs
- team.billing.info
- team.info
- team.preferences.list
- team.profile.get
- users.profile.get
- usergroups.create
- usergroups.disable
- usergroups.enable
- usergroups.update
- usergroups.users.update
- usergroups.list
- usergroups.users.list
- users.deletePhoto
- users.profile.set
- users.setPhoto
- identity.basic
- users.identity
- users.lookupByEmail
- users.setActive
- users.setPresence
- workflows.stepCompleted
- workflows.stepFailed
- workflows.updateStep
- workflows.triggers.permissions.add
- workflows.triggers.permissions.remove
- workflows.triggers.permissions.set
- workflows.triggers.permissions.list

View file

@ -1,7 +1,10 @@
//go:generate generate_permissions permissions.yaml permissions.go slack
package slack
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
@ -12,8 +15,91 @@ import (
"github.com/jedib0t/go-pretty/table"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/config"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/pb/analyzerpb"
"github.com/trufflesecurity/trufflehog/v3/pkg/context"
)
var _ analyzers.Analyzer = (*Analyzer)(nil)
type Analyzer struct {
Cfg *config.Config
}
func (Analyzer) Type() analyzerpb.AnalyzerType { return analyzerpb.AnalyzerType_Slack }
func (a Analyzer) Analyze(_ context.Context, credInfo map[string]string) (*analyzers.AnalyzerResult, error) {
key, ok := credInfo["key"]
if !ok {
return nil, errors.New("key not found in credentialInfo")
}
info, err := AnalyzePermissions(a.Cfg, key)
if err != nil {
return nil, err
}
return secretInfoToAnalyzerResult(info), nil
}
func secretInfoToAnalyzerResult(info *SecretInfo) *analyzers.AnalyzerResult {
if info == nil {
return nil
}
result := analyzers.AnalyzerResult{
AnalyzerType: analyzerpb.AnalyzerType_Slack,
Metadata: nil,
}
resourceType := "user"
fullyQualifiedName := info.User.TeamId + "/" + info.User.UserId
if info.User.BotId != "" {
resourceType = "bot"
fullyQualifiedName = info.User.BotId
}
resource := analyzers.Resource{
Name: info.User.User,
FullyQualifiedName: fullyQualifiedName,
Type: resourceType,
Metadata: map[string]any{
"url": info.User.Url,
"team": info.User.Team,
"team_id": info.User.TeamId,
"scopes": strings.Split(info.Scopes, ","),
},
}
// extract all permissions
permissions := extractPermissions(info)
result.Bindings = analyzers.BindAllPermissions(resource, permissions...)
return &result
}
func extractPermissions(info *SecretInfo) []analyzers.Permission {
var permissions []analyzers.Permission
for _, scope := range strings.Split(info.Scopes, ",") {
perms, ok := scope_mapping[scope]
if !ok {
continue
}
for _, perm := range perms {
if _, ok := StringToPermission[perm]; !ok {
// not in out generated permissions,
continue
}
permissions = append(permissions, analyzers.Permission{
Value: perm,
Parent: nil,
})
}
}
return permissions
}
// Add in showAll to printScopes + deal with testing enterprise + add scope details
type SlackUserData struct {

View file

@ -0,0 +1,83 @@
package slack
import (
_ "embed"
"encoding/json"
"testing"
"time"
"github.com/trufflesecurity/trufflehog/v3/pkg/analyzer/analyzers"
"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.Second*5)
defer cancel()
testSecrets, err := common.GetSecret(ctx, "trufflehog-testing", "detectors2")
if err != nil {
t.Fatalf("could not get test secrets from GCP: %s", err)
}
tests := []struct {
name string
key string
want string // JSON string
wantErr bool
}{
{
name: "valid Slack key",
key: testSecrets.MustGetField("SLACK"),
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{"key": tt.key})
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)
}
// Parse the expected JSON string
var wantObj analyzers.AnalyzerResult
if err := json.Unmarshal([]byte(tt.want), &wantObj); err != nil {
t.Fatalf("could not unmarshal want JSON string: %s", err)
}
// Marshal the expected result to JSON (to normalize)
wantJSON, err := json.Marshal(wantObj)
if err != nil {
t.Fatalf("could not marshal want to JSON: %s", err)
}
// Compare the JSON strings
if string(gotJSON) != string(wantJSON) {
// 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(wantObj, "", " ")
if err != nil {
t.Fatalf("could not marshal want to indented JSON: %s", err)
}
t.Errorf("Analyzer.Analyze() = %s, want %s", gotIndented, wantIndented)
}
})
}
}

View file

@ -109,6 +109,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
} else {
s1.SetVerificationError(err, token)
}
s1.AnalysisInfo = map[string]string{
"key": token,
}
}
results = append(results, s1)

View file

@ -141,6 +141,7 @@ func TestSlack_FromChunk(t *testing.T) {
t.Fatal("no raw secret present")
}
got[i].Raw = nil
got[i].AnalysisInfo = nil
if (got[i].VerificationError() != nil) != tt.wantVerificationErr {
t.Fatalf("wantVerificationError = %v, verification error = %v", tt.wantVerificationErr, got[i].VerificationError())