Skip to content

Commit 665b663

Browse files
caffixlexlex3l337
andcommitted
initial commit of company search handler
Co-authored-by: lexlex3l337 <[email protected]>
1 parent 7fba935 commit 665b663

File tree

4 files changed

+242
-11
lines changed

4 files changed

+242
-11
lines changed
+168
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright © by Jeff Foley 2017-2025. All rights reserved.
2+
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
package aviato
6+
7+
import (
8+
"context"
9+
"encoding/json"
10+
"errors"
11+
"fmt"
12+
"log/slog"
13+
"time"
14+
15+
"github.com/owasp-amass/amass/v4/engine/plugins/support"
16+
et "github.com/owasp-amass/amass/v4/engine/types"
17+
"github.com/owasp-amass/amass/v4/utils/net/http"
18+
dbt "github.com/owasp-amass/asset-db/types"
19+
oam "github.com/owasp-amass/open-asset-model"
20+
"github.com/owasp-amass/open-asset-model/general"
21+
"github.com/owasp-amass/open-asset-model/org"
22+
)
23+
24+
func (cs *companySearch) check(e *et.Event) error {
25+
_, ok := e.Entity.Asset.(*org.Organization)
26+
if !ok {
27+
return errors.New("failed to extract the Organization asset")
28+
}
29+
30+
ds := e.Session.Config().GetDataSourceConfig(cs.plugin.name)
31+
if ds == nil || len(ds.Creds) == 0 {
32+
return nil
33+
}
34+
35+
var keys []string
36+
for _, cr := range ds.Creds {
37+
if cr != nil && cr.Apikey != "" {
38+
keys = append(keys, cr.Apikey)
39+
}
40+
}
41+
42+
since, err := support.TTLStartTime(e.Session.Config(), string(oam.Organization), string(oam.Organization), cs.name)
43+
if err != nil {
44+
return err
45+
}
46+
47+
var id *dbt.Entity
48+
if support.AssetMonitoredWithinTTL(e.Session, e.Entity, cs.plugin.source, since) {
49+
id = cs.lookup(e, e.Entity, since)
50+
} else {
51+
id = cs.query(e, e.Entity, keys)
52+
support.MarkAssetMonitored(e.Session, e.Entity, cs.plugin.source)
53+
}
54+
55+
if id != nil {
56+
cs.process(e, e.Entity, id)
57+
}
58+
return nil
59+
}
60+
61+
func (cs *companySearch) lookup(e *et.Event, orgent *dbt.Entity, since time.Time) *dbt.Entity {
62+
if edges, err := e.Session.Cache().OutgoingEdges(orgent, since, "id"); err == nil {
63+
for _, edge := range edges {
64+
if tags, err := e.Session.Cache().GetEdgeTags(edge,
65+
since, cs.plugin.source.Name); err != nil || len(tags) == 0 {
66+
continue
67+
}
68+
if a, err := e.Session.Cache().FindEntityById(edge.ToEntity.ID); err == nil && a != nil {
69+
if id, ok := a.Asset.(*general.Identifier); ok && id != nil && id.Type == AviatoCompanyID {
70+
return a
71+
}
72+
}
73+
}
74+
}
75+
return nil
76+
}
77+
78+
func (cs *companySearch) query(e *et.Event, orgent *dbt.Entity, apikey []string) *dbt.Entity {
79+
o := orgent.Asset.(*org.Organization)
80+
brand := support.ExtractBrandName(o.Name)
81+
82+
var body string
83+
success := false
84+
for _, key := range apikey {
85+
headers := http.Header{"Content-Type": []string{"application/json"}}
86+
headers["Authorization"] = []string{"Bearer " + key}
87+
88+
_ = cs.plugin.rlimit.Wait(context.TODO())
89+
90+
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
91+
defer cancel()
92+
93+
reqDSL := &dsl{
94+
Offset: 0,
95+
Limit: 10,
96+
Filters: make(map[string]interface{}),
97+
}
98+
99+
reqDSL.Filters["name"] = &dslEvalObj{
100+
Operation: "eq",
101+
Value: brand,
102+
}
103+
104+
dslJSON, err := json.Marshal(reqDSL)
105+
if err != nil {
106+
return nil
107+
}
108+
109+
if resp, err := http.RequestWebPage(ctx, &http.Request{
110+
URL: "https://data.api.aviato.co/company/search",
111+
Method: "POST",
112+
Header: headers,
113+
Body: string(dslJSON),
114+
}); err == nil && resp.StatusCode == 200 {
115+
success = true
116+
body = resp.Body
117+
break
118+
}
119+
}
120+
121+
if !success {
122+
return nil
123+
}
124+
125+
var result companySearchResult
126+
if err := json.Unmarshal([]byte(body), &result); err != nil || len(result.Items) == 0 {
127+
return nil
128+
}
129+
130+
return cs.store(e, orgent, result.Items[0].ID, 90)
131+
}
132+
133+
func (cs *companySearch) store(e *et.Event, orgent *dbt.Entity, companyID string, conf int) *dbt.Entity {
134+
oamid := &general.Identifier{
135+
UniqueID: fmt.Sprintf("%s:%s", AviatoCompanyID, companyID),
136+
ID: companyID,
137+
Type: AviatoCompanyID,
138+
}
139+
140+
ident, err := e.Session.Cache().CreateAsset(oamid)
141+
if err != nil || ident == nil {
142+
return nil
143+
}
144+
145+
_, _ = e.Session.Cache().CreateEntityProperty(ident, &general.SourceProperty{
146+
Source: cs.plugin.source.Name,
147+
Confidence: conf,
148+
})
149+
150+
if err := cs.plugin.createRelation(e.Session, orgent, general.SimpleRelation{Name: "id"}, ident, conf); err != nil {
151+
return nil
152+
}
153+
return ident
154+
}
155+
156+
func (cs *companySearch) process(e *et.Event, orgent, ident *dbt.Entity) {
157+
id := ident.Asset.(*general.Identifier)
158+
159+
_ = e.Dispatcher.DispatchEvent(&et.Event{
160+
Name: id.UniqueID,
161+
Entity: ident,
162+
Session: e.Session,
163+
})
164+
165+
o := orgent.Asset.(*org.Organization)
166+
e.Session.Log().Info("relationship discovered", "from", o.Name, "relation", "id",
167+
"to", id.UniqueID, slog.Group("plugin", "name", cs.plugin.name, "handler", cs.name))
168+
}

engine/plugins/api/aviato/plugin.go

+17-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
)
1717

1818
func NewAviato() et.Plugin {
19-
limit := rate.Every(3 * time.Second)
19+
limit := rate.Every(time.Second)
2020

2121
return &aviato{
2222
name: "Aviato",
@@ -35,6 +35,22 @@ func (a *aviato) Name() string {
3535
func (a *aviato) Start(r et.Registry) error {
3636
a.log = r.Log().WithGroup("plugin").With("name", a.name)
3737

38+
a.companySearch = &companySearch{
39+
name: a.name + "-Company-Search-Handler",
40+
plugin: a,
41+
}
42+
43+
if err := r.RegisterHandler(&et.Handler{
44+
Plugin: a,
45+
Name: a.companySearch.name,
46+
Priority: 6,
47+
Transforms: []string{string(oam.Identifier)},
48+
EventType: oam.Organization,
49+
Callback: a.companySearch.check,
50+
}); err != nil {
51+
return err
52+
}
53+
3854
a.log.Info("Plugin started")
3955
return nil
4056
}

engine/plugins/api/aviato/types.go

+51-3
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,57 @@ import (
1111
"golang.org/x/time/rate"
1212
)
1313

14+
const (
15+
AviatoCompanyID = "aviato_company_id"
16+
)
17+
1418
type aviato struct {
19+
name string
20+
log *slog.Logger
21+
rlimit *rate.Limiter
22+
companySearch *companySearch
23+
source *et.Source
24+
}
25+
26+
type dsl struct {
27+
Offset int `json:"offset"`
28+
Limit int `json:"limit"`
29+
Filters map[string]interface{} `json:"filters"`
30+
}
31+
32+
type dslEvalObj struct {
33+
Operation string `json:"operation"`
34+
Value string `json:"value"`
35+
}
36+
37+
type companySearchResult struct {
38+
Count struct {
39+
Value string `json:"value"`
40+
IsEstimate bool `json:"isEstimate"`
41+
} `json:"count"`
42+
Items []struct {
43+
ID string `json:"id"`
44+
Name string `json:"name"`
45+
Country string `json:"country"`
46+
Region string `json:"region"`
47+
Locality string `json:"locality"`
48+
URLs struct {
49+
Golden string `json:"golden"`
50+
Contact string `json:"contact"`
51+
Twitter string `json:"twitter"`
52+
Website string `json:"website"`
53+
Facebook string `json:"facebook"`
54+
Linkedin string `json:"linkedin"`
55+
Pitchbook string `json:"pitchbook"`
56+
AngelList string `json:"angelList"`
57+
SignalNFX string `json:"signalNFX"`
58+
Crunchbase string `json:"crunchbase"`
59+
} `json:"URLs"`
60+
IndustryList []string `json:"industryList"`
61+
}
62+
}
63+
64+
type companySearch struct {
1565
name string
16-
log *slog.Logger
17-
rlimit *rate.Limiter
18-
source *et.Source
66+
plugin *aviato
1967
}

engine/plugins/api/gleif/plugin.go

+6-7
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,12 @@ func (g *gleif) Start(r et.Registry) error {
4141
}
4242

4343
if err := r.RegisterHandler(&et.Handler{
44-
Plugin: g,
45-
Name: g.fuzzy.name,
46-
Priority: 6,
47-
MaxInstances: 1,
48-
Transforms: []string{string(oam.Identifier)},
49-
EventType: oam.Organization,
50-
Callback: g.fuzzy.check,
44+
Plugin: g,
45+
Name: g.fuzzy.name,
46+
Priority: 6,
47+
Transforms: []string{string(oam.Identifier)},
48+
EventType: oam.Organization,
49+
Callback: g.fuzzy.check,
5150
}); err != nil {
5251
return err
5352
}

0 commit comments

Comments
 (0)