Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@

### Fixed

- Issue descriptions get the provider's template text again. `update` wrote `catalogFile`, `patchHint` and the `update*` strings to `dependencies.json`. Reading the file back removed them. A Linear or GitHub issue then said to edit `undefined` to add a dependency to the catalog.
- Each clause of the "Why this matters" sentence starts with a capital letter. It read `4 Snyk advisories. fix available in a newer version.`
- The owner line in an issue's summary has a label, like the lines around it.
- Links from a grouping page to a dependency's page work when the grouping spans ecosystems. Those pages sit at the site root, so the `../details/…` path a plugin builds leads nowhere, which broke `BasicCompliancePlugin`'s flagged entries. `GroupingFlag.detailLink` is optional now and Dependicus resolves it, and `GroupingDetailContext.detailLinkFor` resolves one for a link inside a section's own HTML.

- The due date was wrong on an issue that covers two versions of one dependency. Say `react` 17 needs 17.0.5 with a 28-day threshold and `react` 18 needs 18.2.1 with a 10-day threshold. The issue asked for 18.2.1 but added the 28 days to its release date. It now uses the threshold that belongs to the version it asks for.
Expand Down
26 changes: 26 additions & 0 deletions src/core/schema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,32 @@ describe('schema', () => {
expect(result.facts.dependency.react).toBeDefined();
});

it('keeps the provider fields the issue templates read', () => {
const provider = {
...validInput.providers[0],
installCommand: 'pnpm install',
catalogFile: 'pnpm-workspace.yaml',
updatePrefix: 'Update the version in:',
updateSuffix: 'Then run `pnpm install`.',
updateInstructions: 'Bump each package.',
patchHint: 'Check the patches in `patches/` still apply.',
};
const result = parseDependicusOutput({ ...validInput, providers: [provider] });
const { dependencies: _deps, ...kept } = result.providers[0]!;
expect(kept).toEqual({
name: 'pnpm',
ecosystem: 'npm',
supportsCatalog: true,
installCommand: 'pnpm install',
urlPatterns: {},
catalogFile: 'pnpm-workspace.yaml',
updatePrefix: 'Update the version in:',
updateSuffix: 'Then run `pnpm install`.',
updateInstructions: 'Bump each package.',
patchHint: 'Check the patches in `patches/` still apply.',
});
});

it('accepts input with empty facts', () => {
const minimal = {
metadata: {
Expand Down
5 changes: 5 additions & 0 deletions src/core/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ const providerOutputSchema = z.object({
supportsCatalog: z.boolean(),
installCommand: z.string().default('install'),
urlPatterns: z.record(z.string(), z.string()).default({}),
updatePrefix: z.string().optional(),
updateSuffix: z.string().optional(),
updateInstructions: z.string().optional(),
catalogFile: z.string().optional(),
patchHint: z.string().optional(),
dependencies: z.array(directDependencySchema),
});

Expand Down
2 changes: 1 addition & 1 deletion src/github-issues/templates/issue-description.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## Summary

{{#if ownerLabel}}
- {{ownerLabel}}
- **Owner:** {{ownerLabel}}
{{/if}}
{{#if multiVersion}}
- **Current versions in monorepo:**
Expand Down
2 changes: 1 addition & 1 deletion src/linear/issueDescriptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ describe('buildIssueDescription', () => {
getDetailUrl: testGetDetailUrl,
providerInfo: npmProviderInfo,
});
expect(result).toContain('- Web App (Frontend)');
expect(result).toContain('- **Owner:** Web App (Frontend)');
});

it('shows catalog How to Update', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/linear/templates/issue-description.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
## Summary

{{#if ownerLabel}}
- {{ownerLabel}}
- **Owner:** {{ownerLabel}}
{{/if}}
{{#if multiVersion}}
- **Current versions in monorepo:**
Expand Down
4 changes: 2 additions & 2 deletions src/security/SecurityPlugin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ describe('SecurityPlugin', () => {
severity: 'high',
advisoryCount: 2,
fixAvailable: true,
rationale: ['2 advisories', 'fix available in a newer version'],
rationale: ['2 advisories', 'Fix available in a newer version'],
sourceLinks: [{ label: 'GHSA-1234', url: 'https://osv.dev/vulnerability/GHSA-1234' }],
};
scoped.setVersionFact('react', '1.0.0', SECURITY_FINDINGS_KEY, [finding]);
Expand All @@ -155,7 +155,7 @@ describe('SecurityPlugin', () => {
const whyValue = whyCol.getValue(ctx);
expect(whyValue).toContain('<a href=');
expect(whyValue).toContain('GHSA-1234');
expect(whyValue).toContain('fix available');
expect(whyValue).toContain('Fix available');
});

it('getLinearIssueSpec returns description sections for findings', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/security/sources/OsvSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ export class OsvSource implements DataSource {
rationale.push(`${vulns.length} advisories`);
}
if (anyFixAvailable) {
rationale.push('fix available in a newer version');
rationale.push('Fix available in a newer version');
}

const sourceLinks = vulns.map((v) => ({
Expand Down
15 changes: 15 additions & 0 deletions src/security/sources/SnykSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,21 @@ describe('toFinding', () => {
expect(toFinding([issue])?.fixAvailable).toBe(true);
});

it('lists a fix version once when Snyk repeats it', () => {
// Snyk can name the same release from two coordinates of one advisory.
const issue: SnykIssue = {
id: 'SNYK-JS-THING-2',
attributes: {
effective_severity_level: 'medium',
coordinates: [
{ remedies: [{ details: { upgrade_package: '15.5.27,16.3.8' } }] },
{ remedies: [{ details: { upgrade_package: '16.3.8' } }] },
],
},
};
expect(toFinding([issue])?.advisories?.[0]?.fixVersions).toEqual(['15.5.27', '16.3.8']);
});

it('links each advisory to its Snyk page', () => {
expect(toFinding(gin)?.sourceLinks?.[0]?.url).toMatch(
/^https:\/\/security\.snyk\.io\/vuln\/SNYK-GOLANG-/,
Expand Down
4 changes: 2 additions & 2 deletions src/security/sources/SnykSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function fixVersions(issue: SnykIssue): string[] {
for (const remedy of coordinate.remedies ?? []) {
for (const part of remedy.details?.upgrade_package?.split(',') ?? []) {
const trimmed = part.trim();
if (trimmed) versions.push(trimmed);
if (trimmed && !versions.includes(trimmed)) versions.push(trimmed);
}
}
}
Expand Down Expand Up @@ -300,7 +300,7 @@ export function toFinding(
: `${ordered.length} Snyk advisories`,
];
if (maturity) rationale.push(`Snyk exploit maturity: ${maturity}`);
if (fixAvailable) rationale.push('fix available in a newer version');
if (fixAvailable) rationale.push('Fix available in a newer version');

return {
source: 'snyk',
Expand Down
Loading