Skip to content

Commit cbf8f25

Browse files
authored
Merge branch 'main' into cursor/SOU-718-ask-codebase-tool-visibility-26fc
2 parents d42d3ab + 6b52d37 commit cbf8f25

File tree

8 files changed

+222
-136
lines changed

8 files changed

+222
-136
lines changed

.github/workflows/license-audit.yml

Lines changed: 70 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ on:
1010
- "scripts/fetchLicenses.mjs"
1111
- "scripts/summarizeLicenses.mjs"
1212
- "scripts/npmLicenseMap.json"
13+
- "scripts/licenseAuditPrompt.txt"
14+
- "scripts/runLicenseAudit.sh"
1315
workflow_dispatch:
1416

1517
jobs:
@@ -40,80 +42,21 @@ jobs:
4042
- name: Summarize licenses
4143
run: node scripts/summarizeLicenses.mjs
4244

45+
- name: Read audit prompt
46+
id: read-prompt
47+
run: |
48+
{
49+
echo 'PROMPT<<PROMPT_EOF'
50+
cat scripts/licenseAuditPrompt.txt
51+
echo 'PROMPT_EOF'
52+
} >> "$GITHUB_OUTPUT"
53+
4354
- name: Audit licenses with Claude
4455
uses: anthropics/claude-code-action@v1
4556
with:
4657
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
4758
claude_args: '--allowedTools "Bash,Read,Write,Glob,Grep,WebFetch"'
48-
prompt: |
49-
You are a license compliance auditor. Your job is to review the OSS dependency
50-
licenses in this repository and produce a structured audit result.
51-
52-
## Steps
53-
54-
1. Read the file `oss-licenses.json` in the repo root.
55-
56-
2. Identify every package whose `license` field is:
57-
- `"UNKNOWN"`
58-
- A non-standard SPDX string (e.g., `"SEE LICENSE IN LICENSE"`, `"UNLICENSED"`,
59-
`"SEE LICENSE IN ..."`, or any value that is not a recognized SPDX identifier)
60-
- An object instead of a string (e.g., `{"type":"MIT","url":"..."}`)
61-
62-
3. For each such package, try to resolve the actual license:
63-
- Use WebFetch to visit `https://www.npmjs.com/package/<package-name>` and look
64-
for license information on the npm page.
65-
- If the npm page is inconclusive, check the package's `repository` or `homepage`
66-
URL (from oss-licenses.json) via WebFetch to find a LICENSE file.
67-
- If the license field is an object like `{"type":"MIT","url":"..."}`, extract the
68-
`type` field as the resolved license.
69-
70-
4. Identify all copyleft-licensed packages. Classify them as:
71-
- **Strong copyleft**: GPL-2.0, GPL-3.0, AGPL-1.0, AGPL-3.0, SSPL-1.0, EUPL-1.1,
72-
EUPL-1.2, CPAL-1.0, OSL-3.0 (and any `-only` or `-or-later` variants)
73-
- **Weak copyleft**: LGPL-2.0, LGPL-2.1, LGPL-3.0, MPL-2.0, CC-BY-SA-3.0,
74-
CC-BY-SA-4.0 (and any `-only` or `-or-later` variants)
75-
76-
5. Write a file called `license-audit-result.json` in the repo root with this structure:
77-
```json
78-
{
79-
"status": "PASS or FAIL",
80-
"failReasons": ["list of reasons if FAIL, empty array if PASS"],
81-
"summary": {
82-
"totalPackages": 0,
83-
"resolvedCount": 0,
84-
"unresolvedCount": 0,
85-
"strongCopyleftCount": 0,
86-
"weakCopyleftCount": 0
87-
},
88-
"resolved": [
89-
{ "name": "pkg-name", "version": "1.0.0", "originalLicense": "...", "resolvedLicense": "MIT", "source": "npm page / GitHub repo / extracted from object" }
90-
],
91-
"unresolved": [
92-
{ "name": "pkg-name", "version": "1.0.0", "license": "UNKNOWN", "reason": "why it could not be resolved" }
93-
],
94-
"copyleft": {
95-
"strong": [
96-
{ "name": "pkg-name", "version": "1.0.0", "license": "GPL-3.0" }
97-
],
98-
"weak": [
99-
{ "name": "pkg-name", "version": "1.0.0", "license": "MPL-2.0" }
100-
]
101-
}
102-
}
103-
```
104-
105-
6. Set `status` to `"FAIL"` if `unresolvedCount > 0` OR `strongCopyleftCount > 0`.
106-
Otherwise set it to `"PASS"`.
107-
108-
7. If the status is FAIL, populate `failReasons` with human-readable explanations, e.g.:
109-
- "2 packages have unresolvable licenses: pkg-a, pkg-b"
110-
- "1 package uses strong copyleft license: pkg-c (GPL-3.0)"
111-
112-
## Important Notes
113-
- Do NOT modify any source files. Only write `license-audit-result.json`.
114-
- Be thorough: check every non-standard license, not just a sample.
115-
- If a package's license object has a `type` field, that counts as resolved.
116-
- Weak copyleft licenses (LGPL, MPL) are flagged but do NOT cause a FAIL.
59+
prompt: ${{ steps.read-prompt.outputs.PROMPT }}
11760

11861
- name: Validate audit result
11962
run: |
@@ -148,86 +91,79 @@ jobs:
14891
with:
14992
script: |
15093
const fs = require('fs');
151-
const path = 'license-audit-result.json';
94+
const resultPath = 'license-audit-result.json';
15295
153-
if (!fs.existsSync(path)) {
154-
const body = `## License Audit\n\n:x: Audit failed to produce results. Check the workflow logs for details.`;
155-
const comments = await github.rest.issues.listComments({
156-
owner: context.repo.owner,
157-
repo: context.repo.repo,
158-
issue_number: context.issue.number,
159-
});
160-
const existing = comments.data.find(c => c.body.startsWith('## License Audit'));
161-
if (existing) {
162-
await github.rest.issues.updateComment({
163-
owner: context.repo.owner,
164-
repo: context.repo.repo,
165-
comment_id: existing.id,
166-
body,
167-
});
168-
} else {
169-
await github.rest.issues.createComment({
170-
owner: context.repo.owner,
171-
repo: context.repo.repo,
172-
issue_number: context.issue.number,
173-
body,
174-
});
175-
}
176-
return;
177-
}
96+
// Determine if we should comment
97+
let shouldComment = false;
98+
let body = '';
17899
179-
const result = JSON.parse(fs.readFileSync(path, 'utf-8'));
180-
const icon = result.status === 'PASS' ? ':white_check_mark:' : ':x:';
100+
if (!fs.existsSync(resultPath)) {
101+
shouldComment = true;
102+
body = `## License Audit\n\n:x: Audit failed to produce results. Check the workflow logs for details.`;
103+
} else {
104+
const result = JSON.parse(fs.readFileSync(resultPath, 'utf-8'));
105+
const hasWeakCopyleft = result.summary.weakCopyleftCount > 0;
106+
const isFail = result.status === 'FAIL';
181107
182-
let body = `## License Audit\n\n`;
183-
body += `${icon} **Status: ${result.status}**\n\n`;
184-
body += `| Metric | Count |\n|---|---|\n`;
185-
body += `| Total packages | ${result.summary.totalPackages} |\n`;
186-
body += `| Resolved (non-standard) | ${result.summary.resolvedCount} |\n`;
187-
body += `| Unresolved | ${result.summary.unresolvedCount} |\n`;
188-
body += `| Strong copyleft | ${result.summary.strongCopyleftCount} |\n`;
189-
body += `| Weak copyleft | ${result.summary.weakCopyleftCount} |\n`;
108+
if (!isFail && !hasWeakCopyleft) {
109+
return;
110+
}
190111
191-
if (result.failReasons && result.failReasons.length > 0) {
192-
body += `\n### Fail Reasons\n\n`;
193-
for (const reason of result.failReasons) {
194-
body += `- ${reason}\n`;
112+
shouldComment = true;
113+
const icon = isFail ? ':x:' : ':warning:';
114+
115+
body = `## License Audit\n\n`;
116+
body += `${icon} **Status: ${result.status}**\n\n`;
117+
body += `| Metric | Count |\n|---|---|\n`;
118+
body += `| Total packages | ${result.summary.totalPackages} |\n`;
119+
body += `| Resolved (non-standard) | ${result.summary.resolvedCount} |\n`;
120+
body += `| Unresolved | ${result.summary.unresolvedCount} |\n`;
121+
body += `| Strong copyleft | ${result.summary.strongCopyleftCount} |\n`;
122+
body += `| Weak copyleft | ${result.summary.weakCopyleftCount} |\n`;
123+
124+
if (result.failReasons && result.failReasons.length > 0) {
125+
body += `\n### Fail Reasons\n\n`;
126+
for (const reason of result.failReasons) {
127+
body += `- ${reason}\n`;
128+
}
195129
}
196-
}
197130
198-
if (result.unresolved && result.unresolved.length > 0) {
199-
body += `\n### Unresolved Packages\n\n`;
200-
body += `| Package | Version | License | Reason |\n|---|---|---|---|\n`;
201-
for (const pkg of result.unresolved) {
202-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` | ${pkg.reason} |\n`;
131+
if (result.unresolved && result.unresolved.length > 0) {
132+
body += `\n### Unresolved Packages\n\n`;
133+
body += `| Package | Version | License | Reason |\n|---|---|---|---|\n`;
134+
for (const pkg of result.unresolved) {
135+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` | ${pkg.reason} |\n`;
136+
}
203137
}
204-
}
205138
206-
if (result.copyleft && result.copyleft.strong && result.copyleft.strong.length > 0) {
207-
body += `\n### Strong Copyleft Packages\n\n`;
208-
body += `| Package | Version | License |\n|---|---|---|\n`;
209-
for (const pkg of result.copyleft.strong) {
210-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
139+
if (result.copyleft && result.copyleft.strong && result.copyleft.strong.length > 0) {
140+
body += `\n### Strong Copyleft Packages\n\n`;
141+
body += `| Package | Version | License |\n|---|---|---|\n`;
142+
for (const pkg of result.copyleft.strong) {
143+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
144+
}
211145
}
212-
}
213146
214-
if (result.copyleft && result.copyleft.weak && result.copyleft.weak.length > 0) {
215-
body += `\n### Weak Copyleft Packages (informational)\n\n`;
216-
body += `| Package | Version | License |\n|---|---|---|\n`;
217-
for (const pkg of result.copyleft.weak) {
218-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
147+
if (result.copyleft && result.copyleft.weak && result.copyleft.weak.length > 0) {
148+
body += `\n### Weak Copyleft Packages (informational)\n\n`;
149+
body += `| Package | Version | License |\n|---|---|---|\n`;
150+
for (const pkg of result.copyleft.weak) {
151+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.license}\` |\n`;
152+
}
219153
}
220-
}
221154
222-
if (result.resolved && result.resolved.length > 0) {
223-
body += `\n<details><summary>Resolved Packages (${result.resolved.length})</summary>\n\n`;
224-
body += `| Package | Version | Original | Resolved | Source |\n|---|---|---|---|---|\n`;
225-
for (const pkg of result.resolved) {
226-
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.originalLicense}\` | \`${pkg.resolvedLicense}\` | ${pkg.source} |\n`;
155+
if (result.resolved && result.resolved.length > 0) {
156+
body += `\n<details><summary>Resolved Packages (${result.resolved.length})</summary>\n\n`;
157+
body += `| Package | Version | Original | Resolved | Source |\n|---|---|---|---|---|\n`;
158+
for (const pkg of result.resolved) {
159+
body += `| ${pkg.name} | ${pkg.version} | \`${pkg.originalLicense}\` | \`${pkg.resolvedLicense}\` | ${pkg.source} |\n`;
160+
}
161+
body += `\n</details>\n`;
227162
}
228-
body += `\n</details>\n`;
229163
}
230164
165+
if (!shouldComment) return;
166+
231167
const comments = await github.rest.issues.listComments({
232168
owner: context.repo.owner,
233169
repo: context.repo.repo,

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Changed
11+
- Updated JumpCloud SSO documentation to clarify token endpoint authentication method requirement and AUTH_SECRET configuration. [#1022](https://github.com/sourcebot-dev/sourcebot/pull/1022)
12+
13+
## [4.15.10] - 2026-03-20
14+
1015
### Changed
1116
- Increased `SOURCEBOT_CHAT_MAX_STEP_COUNT` default from 20 to 100 to allow agents to perform more autonomous steps. [#1017](https://github.com/sourcebot-dev/sourcebot/pull/1017)
1217
- The `ask_codebase` MCP tool is now hidden when no language model providers are configured. [#1018](https://github.com/sourcebot-dev/sourcebot/pull/1018)
@@ -37,10 +42,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3742
### Changed
3843
- Deprecated `EXPERIMENT_DISABLE_API_KEY_CREATION_FOR_NON_ADMIN_USERS` in favour of `DISABLE_API_KEY_CREATION_FOR_NON_OWNER_USERS`. The old variable will continue to work as a fallback. [#1007](https://github.com/sourcebot-dev/sourcebot/pull/1007)
3944

45+
### Modified
46+
- Made OSS license audit runnable locally [#1021](https://github.com/sourcebot-dev/sourcebot/pull/1021)
47+
48+
4049
## [4.15.6] - 2026-03-13
4150

4251
### Added
4352
- Added generated OpenAPI documentation for the public search, repo, and file browsing API surface. [#996](https://github.com/sourcebot-dev/sourcebot/pull/996)
53+
- Added OSS license audit github action. [#1003](https://github.com/sourcebot-dev/sourcebot/pull/1003)
4454

4555
### Fixed
4656
- [EE] Fixed account-driven permission sync silently wiping all Bitbucket Server repository permissions when the OAuth token expires on instances with anonymous access enabled. [#998](https://github.com/sourcebot-dev/sourcebot/pull/998)

docs/api-reference/sourcebot-public.openapi.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"openapi": "3.0.3",
33
"info": {
44
"title": "Sourcebot Public API",
5-
"version": "v4.15.9",
5+
"version": "v4.15.10",
66
"description": "OpenAPI description for the public Sourcebot REST endpoints used for search, repository listing, and file browsing."
77
},
88
"tags": [

docs/docs/configuration/idp.mdx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ A JumpCloud connection can be used for [authentication](/docs/configuration/auth
531531

532532
When configuring your application:
533533
- Set the SSO type to "OIDC"
534+
- Set the **Token Endpoint Authentication Method** to `client_secret_basic`. JumpCloud defaults to `client_secret_post`, but Sourcebot requires `client_secret_basic`.
534535
- Add `<sourcebot_url>/api/auth/callback/jumpcloud` to the redirect URIs (ex. https://sourcebot.coolcorp.com/api/auth/callback/jumpcloud)
535536
- Set the login URL to `<sourcebot_url>/login`
536537

@@ -539,6 +540,8 @@ A JumpCloud connection can be used for [authentication](/docs/configuration/auth
539540
<Step title="Define environment variables">
540541
The client id, secret, and issuer URL are provided to Sourcebot via environment variables. These can be named whatever you like
541542
(ex. `JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_ID`, `JUMPCLOUD_IDENTITY_PROVIDER_CLIENT_SECRET`, and `JUMPCLOUD_IDENTITY_PROVIDER_ISSUER`)
543+
544+
You must also set the `AUTH_SECRET` environment variable. Generate one with `openssl rand -base64 33` and pass it to your Sourcebot deployment. While `AUTH_SECRET` is auto-generated if not provided, it must be explicitly set for SSO to work reliably across restarts.
542545
</Step>
543546
<Step title="Define the identity provider config">
544547
Create a `identityProvider` object in the [config file](/docs/configuration/config-file) with the following fields:

packages/shared/src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
// This file is auto-generated by .github/workflows/release-sourcebot.yml
2-
export const SOURCEBOT_VERSION = "v4.15.9";
2+
export const SOURCEBOT_VERSION = "v4.15.10";

packages/web/src/ee/features/sso/sso.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,7 @@ const createJumpCloudProvider = (clientId: string, clientSecret: string, issuer:
442442
clientId: clientId,
443443
clientSecret: clientSecret,
444444
issuer: issuer,
445+
checks: ["pkce", "state"],
445446
allowDangerousEmailAccountLinking: env.AUTH_EE_ALLOW_EMAIL_ACCOUNT_LINKING === 'true',
446447
} as Provider;
447448
}

scripts/licenseAuditPrompt.txt

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
You are a license compliance auditor. Your job is to review the OSS dependency
2+
licenses in this repository and produce a structured audit result.
3+
4+
## Steps
5+
6+
1. Read the file `oss-licenses.json` in the repo root.
7+
8+
2. Identify every package whose `license` field is:
9+
- `"UNKNOWN"`
10+
- A non-standard SPDX string (e.g., `"SEE LICENSE IN LICENSE"`, `"UNLICENSED"`,
11+
`"SEE LICENSE IN ..."`, or any value that is not a recognized SPDX identifier)
12+
- An object instead of a string (e.g., `{"type":"MIT","url":"..."}`)
13+
14+
3. For each such package, try to resolve the actual license:
15+
- Use WebFetch to visit `https://www.npmjs.com/package/<package-name>` and look
16+
for license information on the npm page.
17+
- If the npm page is inconclusive, check the package's `repository` or `homepage`
18+
URL (from oss-licenses.json) via WebFetch to find a LICENSE file.
19+
- If the license field is an object like `{"type":"MIT","url":"..."}`, extract the
20+
`type` field as the resolved license.
21+
22+
4. Identify all copyleft-licensed packages. Classify them as:
23+
- **Strong copyleft**: GPL-2.0, GPL-3.0, AGPL-1.0, AGPL-3.0, SSPL-1.0, EUPL-1.1,
24+
EUPL-1.2, CPAL-1.0, OSL-3.0 (and any `-only` or `-or-later` variants)
25+
- **Weak copyleft**: LGPL-2.0, LGPL-2.1, LGPL-3.0, MPL-2.0, CC-BY-SA-3.0,
26+
CC-BY-SA-4.0 (and any `-only` or `-or-later` variants)
27+
28+
5. Write a file called `license-audit-result.json` in the repo root with this structure:
29+
```json
30+
{
31+
"status": "PASS or FAIL",
32+
"failReasons": ["list of reasons if FAIL, empty array if PASS"],
33+
"summary": {
34+
"totalPackages": 0,
35+
"resolvedCount": 0,
36+
"unresolvedCount": 0,
37+
"strongCopyleftCount": 0,
38+
"weakCopyleftCount": 0
39+
},
40+
"resolved": [
41+
{ "name": "pkg-name", "version": "1.0.0", "originalLicense": "...", "resolvedLicense": "MIT", "source": "npm page / GitHub repo / extracted from object" }
42+
],
43+
"unresolved": [
44+
{ "name": "pkg-name", "version": "1.0.0", "license": "UNKNOWN", "reason": "why it could not be resolved" }
45+
],
46+
"copyleft": {
47+
"strong": [
48+
{ "name": "pkg-name", "version": "1.0.0", "license": "GPL-3.0" }
49+
],
50+
"weak": [
51+
{ "name": "pkg-name", "version": "1.0.0", "license": "MPL-2.0" }
52+
]
53+
}
54+
}
55+
```
56+
57+
6. Set `status` to `"FAIL"` if `unresolvedCount > 0` OR `strongCopyleftCount > 0`.
58+
Otherwise set it to `"PASS"`.
59+
60+
7. If the status is FAIL, populate `failReasons` with human-readable explanations, e.g.:
61+
- "2 packages have unresolvable licenses: pkg-a, pkg-b"
62+
- "1 package uses strong copyleft license: pkg-c (GPL-3.0)"
63+
64+
## Important Notes
65+
- Do NOT modify any source files. Only write `license-audit-result.json`.
66+
- Be thorough: check every non-standard license, not just a sample.
67+
- If a package's license object has a `type` field, that counts as resolved.
68+
- Weak copyleft licenses (LGPL, MPL) are flagged but do NOT cause a FAIL.

0 commit comments

Comments
 (0)