Skip to content

Commit 8fd23f4

Browse files
authored
Check:links and insecure HTTP usage (#120)
1 parent 7634898 commit 8fd23f4

14 files changed

Lines changed: 519 additions & 304 deletions

File tree

.devcontainer/ubuntu-24.04/.p10k.zsh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1399,7 +1399,7 @@
13991399

14001400
# Use POWERLEVEL9K_KUBECONTEXT_CONTENT_EXPANSION to specify the content displayed by kubecontext
14011401
# segment. Parameter expansions are very flexible and fast, too. See reference:
1402-
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
1402+
# https://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
14031403
#
14041404
# Within the expansion the following parameters are always available:
14051405
#
@@ -1618,7 +1618,7 @@
16181618

16191619
# Use POWERLEVEL9K_GOOGLE_APP_CRED_CONTENT_EXPANSION to specify the content displayed by
16201620
# google_app_cred segment. Parameter expansions are very flexible and fast, too. See reference:
1621-
# http://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
1621+
# https://zsh.sourceforge.net/Doc/Release/Expansion.html#Parameter-Expansion.
16221622
#
16231623
# You can use the following parameters in the expansion. Each of them corresponds to one of the
16241624
# fields in the JSON file pointed to by GOOGLE_APPLICATION_CREDENTIALS.

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ jobs:
7070
- name: Check copyright
7171
run: npm run copyright:check
7272

73+
- name: Check links for insecure http URLs (only changed files)
74+
run: npm run check:links -- -m false
75+
7376
- name: Lint check
7477
run: npm run lint
7578

.github/workflows/nightly.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,24 @@ defaults:
2222
shell: bash
2323

2424
jobs:
25+
validate-links:
26+
name: Validate Markdown Links
27+
runs-on: ubuntu-latest
28+
steps:
29+
- name: Harden the runner (Audit all outbound calls)
30+
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
31+
with:
32+
egress-policy: audit
33+
34+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
35+
36+
- name: Validate Links
37+
uses: tcort/github-action-markdown-link-check@e7c7a18363c842693fadde5d41a3bd3573a7a225 # v1.1.2
38+
with:
39+
use-quiet-mode: 'no'
40+
use-verbose-mode: 'yes'
41+
config-file: '.github/markdown-link-check.jsonc'
42+
2543
build:
2644
if: github.repository == 'Open-CMSIS-Pack/vscode-cmsis-solution'
2745
permissions:

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ The full license text of applicable licenses is provided below.
1717

1818
Apache License
1919
Version 2.0, January 2004
20-
http://www.apache.org/licenses/
20+
https://www.apache.org/licenses/
2121

2222
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
2323

LICENSE-Apache-2.0

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Apache License
22
Version 2.0, January 2004
3-
http://www.apache.org/licenses/
3+
https://www.apache.org/licenses/
44

55
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
66

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"version": "1.67.1",
66
"preview": false,
77
"publisher": "Arm",
8-
"author": "Jens Reinecke <jens.reinecke@arm.com>",
8+
"author": "Jonatan Antoni <jonatan.antoni@arm.com>",
99
"license": "SEE LICENSE IN LICENSE",
1010
"main": "dist/extension.js",
1111
"repository": "https://github.com/Open-CMSIS-Pack/vscode-cmsis-solution",
@@ -42,7 +42,7 @@
4242
"scripts": {
4343
"preprepare": "npm run download:rpc-interface",
4444
"prepare": "concurrently npm:download:uv2csolution npm:download:debug-adapters npm:download:toolbox",
45-
"clean": "git clean -f -x ./dist ./out ./tools",
45+
"clean": "git clean -f -x ./dist ./out ./tools ./coverage ./e2e-report ./e2e-screenshots ./test-results",
4646
"prebuild": "npm run font",
4747
"build": "webpack --mode production",
4848
"prewatch": "npm run font",
@@ -68,7 +68,8 @@
6868
"font": "fantasticon",
6969
"lint:md": "markdownlint **/*.md -c ./.github/markdownlint.jsonc -i ./node_modules -i ./dist -i ./coverage -i ./tools -i ./test-data -i ./src/e2e-tests/data -i CHANGELOG.md",
7070
"copyright:check": "tsx scripts/copyright-manager.ts --mode=check",
71-
"copyright:fix": "tsx scripts/copyright-manager.ts --mode=fix"
71+
"copyright:fix": "tsx scripts/copyright-manager.ts --mode=fix",
72+
"check:links": "tsx scripts/check-links.ts -i ./node_modules/** -i ./.github/** -c ./.github/markdown-link-check.jsonc"
7273
},
7374
"dependencies": {
7475
"@ant-design/icons": "^5.6.1",

scripts/check-links.ts

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#!npx tsx
2+
3+
/**
4+
* Copyright 2026 Arm Limited
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
/*
20+
*Usage:
21+
* check-links.ts [options]
22+
23+
* Options:
24+
* --version Show version number [boolean]
25+
* -c, --config Path to markdown-link-check config file [string] [default: ".github/markdown-link-check.jsonc"]
26+
* -i, --ignore Directories to ignore [array] [default: ["node_modules/**"]]
27+
* -m, --checkMarkdown Validate links in markdown files [boolean] [default: true]
28+
* -u, --checkHttp Scan changed files for insecure http URLs [boolean] [default: true]
29+
* -h, --help Show help [boolean]
30+
*
31+
* Example usage:
32+
* npm run check:links
33+
* npm run check:links -- -i "./dist/**" -i "./coverage/**"
34+
* npm run check:links -- -c "./.github/markdown-link-check.jsonc"
35+
* npm run check:links -- -m false -i "./dist/**" -c "./.github/markdown-link-check.jsonc"
36+
*/
37+
38+
import { execFile as execFileCallback } from "child_process";
39+
import { readFile as readFileCallback } from "fs";
40+
import { glob } from "glob";
41+
import { resolve } from "path";
42+
import { promisify } from "util";
43+
import yargs from "yargs";
44+
import { hideBin } from "yargs/helpers";
45+
46+
const execFile = promisify(execFileCallback);
47+
const readFile = promisify(readFileCallback);
48+
49+
50+
async function getChangedFiles(): Promise<string[]> {
51+
const changedFiles = new Set<string>();
52+
53+
const unstaged = await execFile("git", [
54+
"diff",
55+
"--name-only",
56+
"--diff-filter=ACMR"
57+
]);
58+
unstaged.stdout
59+
.split(/\r?\n/)
60+
.map((f) => f.trim())
61+
.filter(Boolean)
62+
.forEach((f) => changedFiles.add(f));
63+
64+
const staged = await execFile("git", [
65+
"diff",
66+
"--name-only",
67+
"--diff-filter=ACMR",
68+
"--cached"
69+
]);
70+
staged.stdout
71+
.split(/\r?\n/)
72+
.map((f) => f.trim())
73+
.filter(Boolean)
74+
.forEach((f) => changedFiles.add(f));
75+
76+
// Explicitly include newly added files from the index.
77+
const stagedAdded = await execFile("git", [
78+
"diff",
79+
"--name-only",
80+
"--diff-filter=A",
81+
"--cached"
82+
]);
83+
stagedAdded.stdout
84+
.split(/\r?\n/)
85+
.map((f) => f.trim())
86+
.filter(Boolean)
87+
.forEach((f) => changedFiles.add(f));
88+
89+
return Array.from(changedFiles);
90+
}
91+
92+
async function checkForInsecureUrls(files: string[]): Promise<void> {
93+
const insecurePattern = /http:\/\//;
94+
95+
for (const file of files) {
96+
try {
97+
const content = await readFile(file, "utf8");
98+
99+
// Skip likely binary files.
100+
if (content.includes("\0")) {
101+
continue;
102+
}
103+
104+
const lines = content.split(/\r?\n/);
105+
for (let i = 0; i < lines.length; i++) {
106+
if (!insecurePattern.test(lines[i])) {
107+
continue;
108+
}
109+
110+
console.error(`Insecure URL found in ${file}:${i + 1}`);
111+
console.error(` ${lines[i].trim()}`);
112+
process.exitCode = 1;
113+
}
114+
} catch {
115+
// Ignore unreadable/non-text files.
116+
}
117+
}
118+
}
119+
120+
async function main() {
121+
const argv = yargs(hideBin(process.argv))
122+
.option("config", {
123+
alias: "c",
124+
type: "string",
125+
description: "Path to markdown-link-check config file",
126+
default: ".github/markdown-link-check.jsonc",
127+
})
128+
.option("ignore", {
129+
alias: "i",
130+
type: "array",
131+
description: "Directories to ignore",
132+
default: ["node_modules/**"],
133+
})
134+
.option("checkMarkdown", {
135+
alias: "m",
136+
type: "boolean",
137+
description: "Validate links in markdown files",
138+
default: true,
139+
})
140+
.option("checkHttp", {
141+
alias: "u",
142+
type: "boolean",
143+
description: "Scan changed files for insecure http URLs",
144+
default: true,
145+
})
146+
.help()
147+
.alias("help", "h")
148+
.parseSync();
149+
150+
if (argv.checkMarkdown) {
151+
const configPath = resolve(argv.config);
152+
const mdFiles = await glob("**/*.md", {
153+
ignore: argv.ignore as string[],
154+
});
155+
156+
if (mdFiles.length === 0) {
157+
console.log("No markdown files found.");
158+
} else {
159+
console.log(`Checking ${mdFiles.length} markdown file(s)...`);
160+
for (const file of mdFiles) {
161+
try {
162+
const { stdout } = await execFile(
163+
"npx", ["markdown-link-check", "-v", "-c", configPath, file], { shell: true }
164+
);
165+
console.log(stdout);
166+
} catch (err: any) {
167+
console.error(`Error in file: ${file}`);
168+
console.error(err.stdout || err.message);
169+
process.exitCode = 1;
170+
}
171+
}
172+
}
173+
} else {
174+
console.log("Skipping markdown link validation.");
175+
}
176+
177+
if (argv.checkHttp) {
178+
const changedFiles = await getChangedFiles();
179+
180+
if (changedFiles.length === 0) {
181+
console.log("No changed files found for insecure URL check.");
182+
return;
183+
}
184+
185+
console.log(`Checking ${changedFiles.length} changed file(s) for insecure URLs...`);
186+
await checkForInsecureUrls(changedFiles);
187+
}
188+
}
189+
190+
main().catch((error) => {
191+
console.error(error);
192+
process.exit(1);
193+
});

0 commit comments

Comments
 (0)