mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-22 15:15:18 +00:00
* chore(deps): Clear high-severity CVE baseline and harden the security gate - Bump OpenTelemetry stack to 0.221.x (fixes @opentelemetry/core advisories) - Bump @larksuiteoapi/node-sdk to ^1.73.0 and override axios to ^1.19.0 - Bump mobilewright to ^0.0.53 (drops vulnerable sharp 0.34.x) - Bump markdown-it to ^15.0.0 (drops vulnerable linkify-it 5.x) - Update undici/fast-uri/brace-expansion/ip-address within range - Adapt telemetry code to OTel API changes (forceFlush, processor options) - Make security-checks a hard gate now that the high baseline is clean * chore(deps): Refresh mobile-mcp vendored lockfile to drop vulnerable sharp * fix(telemetry): stub sdk-node 0.221 env auto-config helper packages sdk-node 0.221 extracted its env-based auto-configuration into @opentelemetry/configuration, otlp-exporter-base, and otlp-grpc-exporter-base, which it now requires eagerly. The existing esbuild stub only covered the exporter-* packages, so the OTLP protocol chain (grpc-js, protobufjs, otlp-transformer) re-entered the sdk-impl static closure and tripped the serve fast-path bundle guard. Stub the three helper packages when imported by sdk-node only; our own protocol modules keep resolving the real packages. qwen-code never reaches these helpers at runtime (explicit exporters + env scrub). * fix(telemetry): disable metrics fallback without reader * fix(vscode): restore nested dependency notices * fix(deps): declare bundled punycode so its notice survives regeneration The CLI esbuild config aliases punycode to the userland package (esbuild.config.js), so the shipped CLI bundle contains MIT-licensed punycode@2.3.1. Its NOTICES.txt section was lost because the only lockfile paths reaching punycode were dev-only; the notice walker (rooted at vscode-ide-companion) never sees a production declaration. Declare punycode as a direct production dependency of the CLI (the bundle input) and of vscode-ide-companion (which packages the bundled CLI into the VSIX and owns NOTICES.txt), then regenerate the lockfile and notices so the MIT notice is restored.
112 lines
4.5 KiB
JavaScript
112 lines
4.5 KiB
JavaScript
/**
|
|
* @license
|
|
* Copyright 2026 Qwen Team
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { getWorkflowJob, getWorkflowStep } from './workflow-helpers.js';
|
|
|
|
const repoRoot = path.resolve(
|
|
path.dirname(fileURLToPath(import.meta.url)),
|
|
'../..',
|
|
);
|
|
|
|
const readWorkflow = (name) =>
|
|
readFileSync(path.join(repoRoot, `.github/workflows/${name}`), 'utf8');
|
|
|
|
describe('security workflows', () => {
|
|
it('keeps Scorecard monthly and reporting-only', () => {
|
|
const workflow = readWorkflow('scorecard-monthly.yml');
|
|
|
|
expect(workflow).toContain("- cron: '0 2 1 * *'");
|
|
expect(workflow).toContain('workflow_dispatch: {}');
|
|
expect(workflow).not.toContain('pull_request');
|
|
expect(workflow).toContain('publish_results: false');
|
|
expect(workflow).toContain('retention-days: 90');
|
|
expect(workflow).toContain(
|
|
'ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc',
|
|
);
|
|
expect(workflow).toContain(
|
|
'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10',
|
|
);
|
|
expect(workflow).toContain(
|
|
'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02',
|
|
);
|
|
expect(workflow).toContain('persist-credentials: false');
|
|
});
|
|
|
|
it('keeps Security Checks a hard gate and audits package locks', () => {
|
|
const workflow = readWorkflow('security-checks.yml');
|
|
const dependencyJob = getWorkflowJob(workflow, 'dependency-cve');
|
|
const dependencyCheckoutStep = getWorkflowStep(dependencyJob, 'Checkout');
|
|
const installStep = getWorkflowStep(dependencyJob, 'Install dependencies');
|
|
const auditStep = getWorkflowStep(
|
|
dependencyJob,
|
|
'Audit production dependencies',
|
|
);
|
|
const secretScanJob = getWorkflowJob(workflow, 'secret-scan');
|
|
const checkoutStep = getWorkflowStep(secretScanJob, 'Checkout');
|
|
const trufflehogStep = getWorkflowStep(
|
|
secretScanJob,
|
|
'Scan for verified secrets',
|
|
);
|
|
|
|
expect(workflow).toContain('pull_request:');
|
|
expect(workflow).toContain('push:');
|
|
expect(workflow).toContain(
|
|
"group: '${{ github.workflow }}-${{ github.event.pull_request.head.repo.full_name || github.repository }}-${{ github.head_ref || github.ref }}'",
|
|
);
|
|
expect(workflow).toContain(
|
|
'cancel-in-progress: "${{ github.event_name == \'pull_request\' }}"',
|
|
);
|
|
expect(workflow).toContain(
|
|
'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10',
|
|
);
|
|
expect(workflow).toContain(
|
|
'actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e',
|
|
);
|
|
expect(dependencyCheckoutStep).toContain('persist-credentials: false');
|
|
expect(checkoutStep).toContain('persist-credentials: false');
|
|
expect(installStep).toContain(
|
|
"run: 'npm ci --ignore-scripts --no-audit --progress=false'",
|
|
);
|
|
expect(auditStep).not.toContain('continue-on-error');
|
|
expect(auditStep).toContain('status=0');
|
|
expect(auditStep).toContain('exit "$status"');
|
|
expect(auditStep).toContain('npm audit --omit=dev --audit-level=high');
|
|
expect(auditStep).toContain(
|
|
'npm audit --omit=dev --audit-level=high || status=$?',
|
|
);
|
|
expect(auditStep).toContain(') || status=$?');
|
|
expect(auditStep).toContain('for lockfile in packages/*/package-lock.json');
|
|
expect(auditStep).toContain('[ -f "$lockfile" ] || continue');
|
|
expect(auditStep).toContain(
|
|
'[ "$lockfile" != "packages/mobile-mcp/package-lock.json" ] || continue',
|
|
);
|
|
expect(auditStep).toContain('cd "$package_dir"');
|
|
expect(auditStep).toContain(
|
|
'npm ci --ignore-scripts --no-audit --progress=false --workspaces=false &&',
|
|
);
|
|
expect(auditStep).toContain(
|
|
'npm audit --omit=dev --audit-level=high --workspaces=false',
|
|
);
|
|
expect(trufflehogStep).not.toContain('continue-on-error');
|
|
const trufflehogPin = trufflehogStep.match(
|
|
/trufflesecurity\/trufflehog@[0-9a-f]{40}' # v([\d.]+)/,
|
|
);
|
|
expect(trufflehogPin).not.toBeNull();
|
|
expect(trufflehogStep).toContain(`version: '${trufflehogPin?.[1]}'`);
|
|
expect(trufflehogStep).toContain(
|
|
"if: \"github.event_name == 'pull_request' || github.event.before != '0000000000000000000000000000000000000000'\"",
|
|
);
|
|
expect(trufflehogStep).toContain("extra_args: '--only-verified'");
|
|
expect(trufflehogStep).toContain(
|
|
'trufflesecurity/trufflehog@6f3c981e7b77f235fd2702dd74af25fc4b72bf11',
|
|
);
|
|
expect(checkoutStep).toContain('fetch-depth: 0');
|
|
});
|
|
});
|