Skip to content

Commit 6124acc

Browse files
committed
fix: add project-id and monitor-id to analytics
1 parent d009e2f commit 6124acc

File tree

5 files changed

+271
-0
lines changed

5 files changed

+271
-0
lines changed

pkg/analytics/instrumentation_collector.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type InstrumentationCollector interface {
3838
SetStatus(s Status)
3939
SetTestSummary(s json_schemas.TestSummary)
4040
SetTargetId(t string) // maybe use package-url library and types
41+
SetProjectId(projectId string)
42+
SetMonitorId(monitorId string)
4143
AddError(err error)
4244
AddExtension(key string, value interface{})
4345
}
@@ -62,6 +64,8 @@ type instrumentationCollectorImpl struct {
6264
status Status
6365
testSummary json_schemas.TestSummary
6466
targetId string
67+
projectId string
68+
monitorId string
6569
instrumentationErr []error
6670
extension map[string]interface{}
6771
}
@@ -129,6 +133,14 @@ func (ic *instrumentationCollectorImpl) SetTargetId(t string) {
129133
ic.targetId = t
130134
}
131135

136+
func (ic *instrumentationCollectorImpl) SetProjectId(id string) {
137+
ic.projectId = id
138+
}
139+
140+
func (ic *instrumentationCollectorImpl) SetMonitorId(id string) {
141+
ic.monitorId = id
142+
}
143+
132144
func (ic *instrumentationCollectorImpl) AddError(err error) {
133145
ic.instrumentationErr = append(ic.instrumentationErr, err)
134146
}
@@ -219,6 +231,21 @@ func (ic *instrumentationCollectorImpl) getV2Attributes() api.AnalyticsAttribute
219231

220232
func (ic *instrumentationCollectorImpl) getV2Interaction() api.Interaction {
221233
stage := toInteractionStage(ic.stage)
234+
235+
if len(ic.projectId) > 0 {
236+
if ic.extension == nil {
237+
ic.extension = make(map[string]interface{})
238+
}
239+
ic.extension["project_id"] = ic.projectId
240+
}
241+
242+
if len(ic.monitorId) > 0 {
243+
if ic.extension == nil {
244+
ic.extension = make(map[string]interface{})
245+
}
246+
ic.extension["monitor_id"] = ic.monitorId
247+
}
248+
222249
return api.Interaction{
223250
Categories: &ic.category,
224251
Errors: toInteractionErrors(ic.instrumentationErr),

pkg/analytics/instrumentation_collector_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,34 @@ func Test_InstrumentationCollector(t *testing.T) {
256256
actualCategory := ic.GetCategory()
257257
assert.Equal(t, mockCategory, actualCategory)
258258
})
259+
260+
t.Run("it should add project id to extension", func(t *testing.T) {
261+
ic := setupBaseCollector(t)
262+
expectedV2InstrumentationObject := buildExpectedBaseObject(t)
263+
264+
mockProjectId := "123e4567-e89b-12d3-a456-426614174000"
265+
mockMonitorId := "89674498-4fcc-4af3-9a80-0582a74614d5"
266+
ic.SetProjectId(mockProjectId)
267+
ic.SetMonitorId(mockMonitorId)
268+
269+
mockExtension := map[string]interface{}{
270+
"strings": "hello world",
271+
"project_id": mockProjectId,
272+
"monitor_id": mockMonitorId,
273+
}
274+
275+
expectedV2InstrumentationObject.Data.Attributes.Interaction.Extension = &mockExtension
276+
277+
actualV2InstrumentationObject, err := GetV2InstrumentationObject(ic)
278+
assert.NoError(t, err)
279+
280+
expectedV2InstrumentationJson, err := json.Marshal(expectedV2InstrumentationObject)
281+
assert.NoError(t, err)
282+
actualV2InstrumentationJson, err := json.Marshal(actualV2InstrumentationObject)
283+
assert.NoError(t, err)
284+
285+
assert.JSONEq(t, string(expectedV2InstrumentationJson), string(actualV2InstrumentationJson))
286+
})
259287
}
260288

261289
func setupBaseCollector(t *testing.T) InstrumentationCollector {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package instrumentation
2+
3+
import (
4+
"regexp"
5+
)
6+
7+
func GetProjectIdAndMonitorIdFromText(text string) (string, string, error) {
8+
// Pattern: /org/{org-slug}/project/{project-uuid}/history/{history--uuidd}
9+
re := regexp.MustCompile(`/org/[^/]+/project/([0-9a-fA-F-]{36})/history/([0-9a-fA-F-]{36})`)
10+
matches := re.FindStringSubmatch(text)
11+
12+
if len(matches) < 3 {
13+
return "", "", nil
14+
}
15+
16+
projectUuid := matches[1]
17+
historyUuid := matches[2]
18+
19+
return projectUuid, historyUuid, nil
20+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
package instrumentation
2+
3+
import "testing"
4+
5+
func Test_GetProjectIdAndMonitorIdFromMonitorUrl(t *testing.T) {
6+
text := `Testing /home/antoine/Documents/SnykSB/goof ...
7+
8+
Open Issues
9+
10+
✗ [LOW] Use of Hardcoded Credentials
11+
Finding ID: 56fdeb92-25b8-4e4a-a062-fd1b6a68e346
12+
Path: typeorm-db.js, line 11
13+
Info: Do not hardcode credentials in code. Found hardcoded credential used in typeorm.createConnection.
14+
15+
✗ [LOW] Use of Hardcoded Passwords
16+
Finding ID: 4d0edc8e-b007-4fbd-96ad-5f8439152c7a
17+
Path: tests/authentication.component.spec.js, line 48
18+
Info: Do not hardcode passwords in code. Found hardcoded password used in changePassword.
19+
20+
✗ [LOW] Use of Hardcoded Passwords
21+
Finding ID: 3d75a56b-9732-4d84-8348-1d5b8c3ebc2b
22+
Path: tests/authentication.component.spec.js, line 48
23+
Info: Do not hardcode passwords in code. Found hardcoded password used in changePassword.
24+
25+
✗ [LOW] Use of Hardcoded Passwords
26+
Finding ID: 3ff041bb-45bd-4f13-b08b-ed5fb56cdb4e
27+
Path: tests/authentication.component.spec.js, line 35
28+
Info: Do not hardcode passwords in code. Found hardcoded password used in changePassword.
29+
30+
✗ [LOW] Use of Hardcoded Passwords
31+
Finding ID: 0afb365f-5b54-4630-beea-b987bd8c7560
32+
Path: tests/authentication.component.spec.js, line 35
33+
Info: Do not hardcode passwords in code. Found hardcoded password used in changePassword.
34+
35+
✗ [LOW] Sensitive Cookie in HTTPS Session Without 'Secure' Attribute
36+
Finding ID: 0862b4f4-2d1d-4014-934c-6175c8bc3115
37+
Path: app.js, line 45
38+
Info: Cookie misses the Secure attribute (it is false by default). Set it to true to protect the cookie from man-in-the-middle attacks.
39+
40+
✗ [LOW] Use of Hardcoded Passwords
41+
Finding ID: 1ba6abc7-31a1-4768-a6e4-84d76aa1aa1b
42+
Path: tests/authentication.component.spec.js, line 24
43+
Info: Do not hardcode passwords in code. Found hardcoded password used in changePassword.
44+
45+
✗ [LOW] Use of Hardcoded Passwords
46+
Finding ID: bbfea606-d013-42e3-b26c-07985f5eae6a
47+
Path: tests/authentication.component.spec.js, line 24
48+
Info: Do not hardcode passwords in code. Found hardcoded password used in changePassword.
49+
50+
✗ [MEDIUM] Allocation of Resources Without Limits or Throttling
51+
Finding ID: 6f5840f0-b4d1-41bf-9047-ba8698ad5314
52+
Path: routes/index.js, line 75
53+
Info: Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.
54+
55+
✗ [MEDIUM] Allocation of Resources Without Limits or Throttling
56+
Finding ID: df6bf797-fb70-4107-b9ff-aca8c30b7115
57+
Path: routes/index.js, line 241
58+
Info: Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.
59+
60+
✗ [MEDIUM] Use of Hardcoded Passwords
61+
Finding ID: 09fa8eb9-117f-42a6-90fb-24eaadd60fbf
62+
Path: typeorm-db.js, line 12
63+
Info: Do not hardcode passwords in code. Found hardcoded password used in typeorm.createConnection.
64+
65+
✗ [MEDIUM] Cleartext Transmission - HTTP Instead of HTTPS
66+
Finding ID: e5a806b4-30d1-47ec-8bd3-13d11e5e7f0b
67+
Path: app.js, line 86
68+
Info: http.createServer uses HTTP which is an insecure protocol and should not be used in code due to cleartext transmission of information. Data in cleartext in a communication channel can be sniffed by unauthorized actors. Consider using the https module instead.
69+
70+
✗ [MEDIUM] Use of Hardcoded Passwords
71+
Finding ID: 1cecd1d1-2097-4a3a-904f-baa188c00468
72+
Path: mongoose-db.js, line 52
73+
Info: Do not hardcode passwords in code. Found hardcoded password used in password.
74+
75+
✗ [MEDIUM] Cross-Site Request Forgery (CSRF)
76+
Finding ID: 25c696b5-6ab0-4cbb-ae06-44c3b696e7f0
77+
Path: app.js, line 28
78+
Info: CSRF protection is disabled for your Express app. This allows the attackers to execute requests on a user's behalf.
79+
80+
✗ [MEDIUM] Open Redirect
81+
Finding ID: 353708b0-064f-4af9-95f7-ea9c246f8ee2
82+
Path: routes/index.js, line 61
83+
Info: Unsanitized input from the HTTP request body flows into redirect, where it is used as input for request redirection. This may result in an Open Redirect vulnerability.
84+
85+
✗ [MEDIUM] Allocation of Resources Without Limits or Throttling
86+
Finding ID: 303714ee-79e3-4cfa-96e8-d1a237476701
87+
Path: routes/index.js, line 152
88+
Info: Expensive operation (a system command execution) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.
89+
90+
✗ [MEDIUM] Allocation of Resources Without Limits or Throttling
91+
Finding ID: d64d17f7-6441-43c9-82a1-c6d8dfa6f145
92+
Path: routes/index.js, line 298
93+
Info: Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.
94+
95+
✗ [MEDIUM] Allocation of Resources Without Limits or Throttling
96+
Finding ID: 6f75850c-a2fa-4550-baab-76b8b4cccb14
97+
Path: routes/index.js, line 67
98+
Info: Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.
99+
100+
✗ [MEDIUM] Information Exposure - X-Powered-By Header
101+
Finding ID: 02e7cd17-b381-46dd-9909-9722ffe036c2
102+
Path: app.js, line 28
103+
Info: Disable X-Powered-By header for your Express app (consider using Helmet middleware), because it exposes information about the used framework to potential attackers.
104+
105+
✗ [MEDIUM] Allocation of Resources Without Limits or Throttling
106+
Finding ID: e746b22c-83e0-489c-a1fa-8e60a0b19079
107+
Path: routes/index.js, line 82
108+
Info: Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.
109+
110+
✗ [MEDIUM] Allocation of Resources Without Limits or Throttling
111+
Finding ID: 477afacb-d530-4119-ad55-c9513f1ef49f
112+
Path: routes/index.js, line 89
113+
Info: Expensive operation (a file system operation) is performed by an endpoint handler which does not use a rate-limiting mechanism. It may enable the attackers to perform Denial-of-service attacks. Consider using a rate-limiting middleware such as express-limit.
114+
115+
✗ [HIGH] Hardcoded Non-Cryptographic Secret
116+
Finding ID: 746d0c10-032e-47f1-83d0-147d4d5107a7
117+
Path: app.js, line 83
118+
Info: Avoid hardcoding values that are meant to be secret. Found a hardcoded string used in here.
119+
120+
✗ [HIGH] Hardcoded Non-Cryptographic Secret
121+
Finding ID: 709d9187-8a5d-4109-836a-0951c4ec6b32
122+
Path: app.js, line 42
123+
Info: Avoid hardcoding values that are meant to be secret. Found a hardcoded string used in express-session.
124+
125+
✗ [HIGH] NoSQL Injection
126+
Finding ID: 24f3f971-1729-4201-98a3-79a736ca7f52
127+
Path: routes/index.js, line 39
128+
Info: Unsanitized input from the HTTP request body flows into find, where it is used in an NoSQL query. This may result in an NoSQL Injection vulnerability.
129+
130+
131+
132+
╭─────────────────────────────────────────────────────────────╮
133+
│ Test Summary │
134+
│ │
135+
│ Organization: antoine-playground │
136+
│ Test type: Static code analysis │
137+
│ Project path: /home/antoine/Documents/SnykSB/goof │
138+
│ │
139+
│ Total issues: 24 │
140+
│ Ignored issues: 0 [ 0 HIGH 0 MEDIUM 0 LOW ] │
141+
│ Open issues: 24 [ 3 HIGH 13 MEDIUM 8 LOW ] │
142+
╰─────────────────────────────────────────────────────────────╯
143+
144+
Report
145+
146+
Your test results are available at:
147+
https://app.snyk.io/org/antoine-playground/project/1f94159a-ba57-4447-a2e8-4611a9509794/history/36cca17a-44c0-496d-824b-091a641306c3
148+
149+
💡 Tip
150+
151+
To view ignored issues, use the --include-ignores option.
152+
`
153+
154+
projectID, monitorID, err := GetProjectIdAndMonitorIdFromText(text)
155+
if err != nil {
156+
t.Fatalf("unexpected error: %v", err)
157+
}
158+
159+
expectedProjectID := "1f94159a-ba57-4447-a2e8-4611a9509794"
160+
expectedMonitorID := "36cca17a-44c0-496d-824b-091a641306c3"
161+
162+
if projectID != expectedProjectID {
163+
t.Errorf("projectID mismatch: got %q, want %q", projectID, expectedProjectID)
164+
}
165+
166+
if monitorID != expectedMonitorID {
167+
t.Errorf("monitorID mismatch: got %q, want %q", monitorID, expectedMonitorID)
168+
}
169+
}

pkg/local_workflows/output_workflow.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
iUtils "github.com/snyk/go-application-framework/internal/utils"
1111
"github.com/snyk/go-application-framework/pkg/configuration"
12+
"github.com/snyk/go-application-framework/pkg/instrumentation"
1213
"github.com/snyk/go-application-framework/pkg/local_workflows/content_type"
1314
"github.com/snyk/go-application-framework/pkg/local_workflows/output_workflow"
1415
"github.com/snyk/go-application-framework/pkg/workflow"
@@ -42,6 +43,32 @@ func outputWorkflowEntryPoint(invocation workflow.InvocationContext, input []wor
4243
config := invocation.GetConfiguration()
4344
debugLogger := invocation.GetEnhancedLogger()
4445

46+
for i := range input {
47+
if input[i].GetContentType() != content_type.LOCAL_FINDING_MODEL && input[i].GetContentType() != "text/hidden" {
48+
continue
49+
}
50+
outputText := string(input[i].GetPayload().([]byte))
51+
52+
analytics := invocation.GetAnalytics()
53+
projectID, monitorID, err := instrumentation.GetProjectIdAndMonitorIdFromText(outputText)
54+
if err != nil {
55+
debugLogger.Printf("Error parsing monitor URL: %v", err)
56+
continue
57+
}
58+
59+
if projectID != "" {
60+
analytics.GetInstrumentation().SetProjectId(projectID)
61+
}
62+
if monitorID != "" {
63+
analytics.GetInstrumentation().SetMonitorId(monitorID)
64+
}
65+
66+
if input[i].GetContentType() == "text/hidden" {
67+
input[i].SetPayload([]byte{})
68+
}
69+
break
70+
}
71+
4572
// Handle UFM models, if none found, continue with the rest
4673
input, err := output_workflow.HandleContentTypeUnifiedModel(input, invocation, outputDestination)
4774
if err != nil {

0 commit comments

Comments
 (0)