Skip to content

Commit 0b83ace

Browse files
feat: add connectivity check extension (#383)
1 parent 7496bd6 commit 0b83ace

File tree

13 files changed

+2574
-1
lines changed

13 files changed

+2574
-1
lines changed

.cursorrules

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
** general **
2+
- always be concise, direct and don't try to appease me.
3+
- DOUBLE CHECK THAT YOUR CHANGES ARE REALLY NEEDED. ALWAYS STICK TO THE GIVEN GOAL, NOT MORE.
4+
- I repeat: don't optimize, don't refactor if not needed.
5+
- Adhere to the rules, fix linting & test issues that are newly introduced.
6+
- Maintain existing code patterns and conventions
7+
- minimize changes to existing code
8+
9+
** how to implement **
10+
- always create an implementation plan and save it to the directory under ${issueID}_implementation_plan but never commit it. take it as a reference for each step and how to proceed. Get confirmation that the plan is ok. update it as needed and get it approved, before you start changing files. the `issueID` is usually specified in the current branch in the format `XXX-XXXX`.
11+
- don't comment what is done, instead comment why something is done if the code is not clear
12+
- always write and update test cases. iterate until they pass.
13+
- to manipulate the environment in tests, please use t.SetEnv, never use os.SetEnv
14+
- follow clean code
15+
- follow golang best practices, produce idiomatic code
16+
- please check the makefile - make lint runs the linter, other targets might be relevant as well.
17+
- use existing mocks, don't write new ones.
18+
- if you use mocks, use gomock to generate them.
19+
- always run the tests after editing.
20+
- if you add debug logging to tests, ensure log messages are not using a Nop logger.
21+
- always use the linter
22+
- don't change code that does not need to be changed. only do the minimum changes.
23+
- this is not a library. if files are not used or needed anymore, delete them instead of deprecating them.
24+
- if a tool call fails, analyze why it failed and correct your approach. don't prompt the user for help.
25+
- if you don't know something, read the code instead of assuming it.
26+
- commenting out code to fix errors is not a solution. instead, fix the error.
27+
28+
** security **
29+
- determine the absolute path of the project directory. you can do that e.g. by executing pwd on the shell within the directory.
30+
- always use snyk for sca and code scanning. scan with both tools. you need to pass the absolute path of the directory we are scanning.
31+
- run snyk code tests after each edit. pass the absolute path of the project directory as a parameter
32+
- run snyk sca tests after updating go.mod and pass the absolute path of the project directory as a parameter.
33+
- run snyk sca and code test before committing. if not test data, fix issues before committing.
34+
35+
** fixing issues **
36+
- fix security issues if they are fixable. take the snyk scan results and the test results as input
37+
- don't fix test data
38+
39+
** committing **
40+
- when asked to commit, always use conventional commit messages (Conventional Commit Style (Subject + Body)). be descriptive in the body. if you find a JIRA issue (XXX-XXXX) in the branch name, use it as a postfix to the subject line in the format [XXX-XXXX]
41+
- consider all commits in the current branch when committing, to have the context of the current changes.
42+
when asked to push, always use 'git push --set-upstream origin $(git_current_branch)' with git_current_branch being the current branch we are on
43+
- never force push
44+
- never push without asking
45+
- never commit the hashicorp gomod
46+
- regularly fetch main branch and offer to merge it into git_current_branch
47+
- after pushing offer to create a PR on github. analyze the changes by comparing the current branch ($(git_current_branch)) with origin/main, and craft a PR description and title.
48+
use the github pr template in this repository

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ require (
3333
github.com/patrickmn/go-cache v2.1.0+incompatible
3434
github.com/snyk/error-catalog-golang-public v0.0.0-20250429130542-564b0605020e
3535
github.com/subosito/gotenv v1.4.1
36+
golang.org/x/net v0.38.0
3637
golang.org/x/sync v0.13.0
38+
golang.org/x/term v0.31.0
3739
gopkg.in/yaml.v3 v3.0.1
3840
)
3941

@@ -110,7 +112,6 @@ require (
110112
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
111113
golang.org/x/crypto v0.37.0 // indirect
112114
golang.org/x/mod v0.24.0 // indirect
113-
golang.org/x/net v0.38.0 // indirect
114115
golang.org/x/sys v0.32.0 // indirect
115116
golang.org/x/text v0.24.0 // indirect
116117
golang.org/x/tools v0.31.0 // indirect

internal/api/api.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ type ApiClient interface {
1919
GetDefaultOrgId() (orgID string, err error)
2020
GetOrgIdFromSlug(slugName string) (string, error)
2121
GetSlugFromOrgId(orgID string) (string, error)
22+
GetOrganizations(limit int) (*contract.OrganizationsResponse, error)
2223
Init(url string, client *http.Client)
2324
GetFeatureFlag(flagname string, origId string) (bool, error)
2425
GetUserMe() (string, error)
@@ -93,6 +94,32 @@ func (a *snykApiClient) GetOrgIdFromSlug(slugName string) (string, error) {
9394
return "", fmt.Errorf("org ID not found for slug %v", slugName)
9495
}
9596

97+
// GetOrganizations retrieves organizations accessible to the authenticated user.
98+
//
99+
// Parameters:
100+
// - limit: Maximum number of organizations to return
101+
//
102+
// Returns:
103+
// - A pointer to OrganizationsResponse containing organizations.
104+
// - An error object (if an error occurred during the API request or response parsing).
105+
func (a *snykApiClient) GetOrganizations(limit int) (*contract.OrganizationsResponse, error) {
106+
endpoint := "/rest/orgs"
107+
version := "2024-10-15"
108+
109+
body, err := clientGet(a, endpoint, &version, "limit", fmt.Sprintf("%d", limit))
110+
if err != nil {
111+
return nil, err
112+
}
113+
114+
var response contract.OrganizationsResponse
115+
err = json.Unmarshal(body, &response)
116+
if err != nil {
117+
return nil, err
118+
}
119+
120+
return &response, nil
121+
}
122+
96123
// GetDefaultOrgId retrieves the default organization ID associated with the authenticated user.
97124
//
98125
// Returns:

internal/api/api_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,30 @@ func Test_GetSlugFromOrgId_ReturnsCorrectSlug(t *testing.T) {
5555
assert.Equal(t, expectedSlug, actualSlug)
5656
}
5757

58+
func Test_GetOrganizations_ReturnsOrganizations(t *testing.T) {
59+
// Arrange
60+
t.Parallel()
61+
orgResponse := newMockOrgResponse(t)
62+
limit := 50
63+
u := fmt.Sprintf("/rest/orgs?limit=%d&version=2024-10-15", limit)
64+
65+
server := setupSingleReponseServer(t, u, orgResponse)
66+
client := api.NewApi(server.URL, http.DefaultClient)
67+
68+
// Act
69+
response, err := client.GetOrganizations(limit)
70+
if err != nil {
71+
t.Error(err)
72+
}
73+
74+
// Assert
75+
assert.NotNil(t, response)
76+
assert.Equal(t, 2, len(response.Organizations))
77+
assert.Equal(t, "27ce75bf-5794-4bfd-9ae7-e779f465abdf", response.Organizations[0].Id)
78+
assert.Equal(t, "defaultOrg", response.Organizations[0].Attributes.Name)
79+
assert.Equal(t, "default-org", response.Organizations[0].Attributes.Slug)
80+
}
81+
5882
func Test_GetOrgIdFromSlug_ReturnsCorrectOrgId(t *testing.T) {
5983
// Arrange
6084
t.Parallel()

internal/mocks/api.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# Snyk Connectivity Check Extension
2+
3+
A Go-based extension for the Snyk CLI that performs comprehensive network connectivity diagnostics. This tool helps troubleshoot connectivity issues between your environment and Snyk's services.
4+
5+
## Features
6+
7+
- **Comprehensive Endpoint Testing**: Tests connectivity to all Snyk API endpoints and services
8+
- **Proxy Detection & Validation**: Automatically detects and validates proxy configurations
9+
- **Organization Listing**: Displays your Snyk organizations when authenticated (with default organization highlighted)
10+
- **Multiple Output Formats**: Human-readable (with color support) and JSON formats
11+
- **Actionable Diagnostics**: Provides specific recommendations based on connectivity issues
12+
- **Integration Ready**: Built as a workflow extension for the Snyk CLI using go-application-framework
13+
14+
### Environment Variables
15+
16+
The tool respects standard proxy environment variables:
17+
- `HTTPS_PROXY` / `https_proxy`
18+
- `HTTP_PROXY` / `http_proxy`
19+
- `NO_PROXY` / `no_proxy`
20+
21+
For custom certificates:
22+
- `NODE_EXTRA_CA_CERTS` - Path to additional CA certificates bundle
23+
24+
### Authentication
25+
26+
The tool uses Snyk authentication from the go-application-framework configuration:
27+
- API tokens via `snyk auth`
28+
- OAuth tokens
29+
- Bearer tokens
30+
31+
When authenticated, the tool will display your organizations with their IDs. The default organization is highlighted with a `[DEFAULT]` marker.
32+
33+
### Command Line Options
34+
35+
- `--json` - Output results in JSON format
36+
- `--no-color` - Disable colored output
37+
- `--timeout <seconds>` - Timeout in seconds for each connection test (default: 10)
38+
- `--max-org-count <number>` - Maximum number of organizations to retrieve (default: 100)
39+
40+
## Output Examples
41+
42+
### Human-Readable Output
43+
44+
```
45+
Checking for proxy configuration...
46+
47+
Environment variables:
48+
HTTPS_PROXY: (not set)
49+
https_proxy: (not set)
50+
HTTP_PROXY: (not set)
51+
http_proxy: (not set)
52+
NO_PROXY: (not set)
53+
no_proxy: (not set)
54+
55+
ℹ No proxy detected - Testing direct connection...
56+
57+
Testing connectivity to Snyk endpoints...
58+
59+
Host Result
60+
----------------------------------------------------------------------
61+
api.snyk.io OK (HTTP 204)
62+
app.snyk.io OK (HTTP 200)
63+
api.eu.snyk.io OK (HTTP 204)
64+
app.eu.snyk.io OK (HTTP 200)
65+
api.us.snyk.io OK (HTTP 204)
66+
app.us.snyk.io OK (HTTP 200)
67+
api.au.snyk.io OK (HTTP 204)
68+
app.au.snyk.io OK (HTTP 200)
69+
api.snykgov.io OK (HTTP 204)
70+
app.snykgov.io OK (HTTP 200)
71+
deeproxy.snyk.io/filters OK (HTTP 200)
72+
downloads.snyk.io:443/cli/wasm/bundle.tar.gz OK (HTTP 200)
73+
learn.snyk.io OK (HTTP 200)
74+
static.snyk.io/cli/latest/version OK (HTTP 200)
75+
snyk.io OK (HTTP 200)
76+
sentry.io REACHABLE (HTTP 405)
77+
78+
--- Actionable TODOs ---
79+
All checks passed. Your network configuration appears to be compatible with Snyk CLI.
80+
81+
ℹ Certificate Configuration:
82+
If you need to trust custom certificates, set NODE_EXTRA_CA_CERTS environment variable
83+
pointing to your CA bundle file.
84+
85+
--- Snyk Token and Organizations ---
86+
✓ Authentication token is configured
87+
88+
Found 2 organizations:
89+
Group ID Org ID Name Slug Default
90+
---------------------------------------------------------------------------------------------------------------------------------------
91+
a1b2c3d4-e5f6-7890-abcd-ef1234567890 d4e5f6a7-b890-cdef-1234-567890abcdef My Organization my-organization Yes
92+
b2c3d4e5-f6a7-8901-bcde-f23456789012 e5f6a7b8-c901-def2-3456-7890abcdef12 Another Org another-org
93+
```
94+
95+
### JSON Output
96+
97+
```bash
98+
snyk connectivity-check --json
99+
```
100+
101+
```json
102+
{
103+
"proxyConfig": {
104+
"detected": false,
105+
"url": "",
106+
"variable": ""
107+
},
108+
"hostResults": [
109+
{
110+
"host": "api.snyk.io",
111+
"displayHost": "api.snyk.io",
112+
"url": "https://api.snyk.io",
113+
"statusCode": 204,
114+
"status": 0,
115+
"responseTime": 150000000
116+
}
117+
],
118+
"todos": [],
119+
"organizations": [
120+
{
121+
"id": "org-uuid-1",
122+
"groupId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
123+
"name": "My Organization",
124+
"slug": "my-organization",
125+
"isDefault": true
126+
},
127+
{
128+
"id": "org-uuid-2",
129+
"groupId": "b2c3d4e5-f6a7-8901-bcde-f23456789012",
130+
"name": "Another Org",
131+
"slug": "another-org",
132+
"isDefault": false
133+
}
134+
],
135+
"tokenPresent": true,
136+
"startTime": "2024-01-15T10:00:00Z",
137+
"endTime": "2024-01-15T10:00:05Z"
138+
}
139+
```
140+
141+
## Troubleshooting
142+
143+
### Common Issues
144+
145+
#### Proxy Authentication Required
146+
If you see "PROXY AUTH REQUIRED", the tool has detected your proxy but needs authentication:
147+
- For NTLM/Negotiate proxies: The Snyk CLI supports these authentication methods
148+
- For other proxy auth types: Configure proxy credentials in your proxy URL
149+
150+
#### DNS Errors
151+
If you see "DNS_ERROR" for multiple hosts:
152+
- Check your DNS configuration
153+
- Verify you can resolve external domains
154+
- Check if you need to use a corporate DNS server
155+
156+
#### TLS/SSL Errors
157+
If you see "TLS/SSL_ERROR":
158+
- You may need to configure custom CA certificates
159+
- Set `NODE_EXTRA_CA_CERTS` to point to your CA bundle
160+
- Ensure your proxy (if any) isn't intercepting SSL
161+
162+
#### No Organizations Displayed
163+
If authenticated but no organizations shown:
164+
- Verify your token has the correct permissions
165+
- Check if you belong to any organizations
166+
- Try re-authenticating with `snyk auth`
167+
168+
### Status Meanings
169+
170+
- **OK**: Full connectivity verified
171+
- **REACHABLE**: Host is reachable but returned unexpected status
172+
- **BLOCKED**: Connection refused or blocked
173+
- **DNS_ERROR**: Cannot resolve hostname
174+
- **TLS/SSL_ERROR**: Certificate or TLS handshake issues
175+
- **TIMEOUT**: Connection timed out
176+
- **PROXY AUTH REQUIRED (SUPPORTED)**: Proxy needs auth, type is supported
177+
- **PROXY AUTH REQUIRED (UNSUPPORTED)**: Proxy needs auth, type not supported

0 commit comments

Comments
 (0)