Add canvas schema validation to extension submission workflow (#2161)

* Add canvas schema and extension submission checks

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* fix: use namespace import for js-yaml

Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com>

* Fix contributors page build markup

Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com>

* Address PR feedback on canvas schema validation

- Add ajv-cli@5 as a pinned devDependency; install via npm ci in CI instead of npx --yes
- Fix screenshot path regex to prevent .. traversal segments
- Validate canvas.schema.json is parseable JSON even on schema-only PRs

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Harden canvas extension workflow against injection attacks

Switch from newline to null-terminated git diff output (git diff -z) so filenames
containing newlines are read atomically, matching the existing skill-check.yml pattern.

Add an allowlist regex guard on the extracted extension directory name immediately after
it is parsed from git diff output. Any name not matching ^[a-z0-9][a-z0-9-]*$ (e.g.
names containing dollar signs, parentheses, spaces, or other shell metacharacters) is
silently skipped before being used anywhere in the script.

Add a matching allowlist guard on each screenshot path extracted from canvas.json before
the file-existence check, so a crafted manifest cannot supply a path with shell
metacharacters or traversal segments even after the schema check passes.

Follows the same defence-in-depth pattern introduced after the injection PoCs in #1236
and #1240.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Replace ajv-cli with in-repo schema validator

- Remove ajv-cli to avoid vulnerable/deprecated transitive dependencies
- Add eng/validate-json-schema.mjs using ajv + ajv-formats
- Update validate-canvas-extensions workflow to use local script
- Use npm ci --ignore-scripts in PR validation job

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: aaronpowell <434140+aaronpowell@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Aaron Powell
2026-07-01 10:40:40 +10:00
committed by GitHub
parent 28c3a14af4
commit 79cda6bb19
8 changed files with 426 additions and 146 deletions
+2
View File
@@ -11,6 +11,8 @@ on:
- instructions - instructions
- hooks - hooks
- workflows - workflows
- extensions
- .schemas/canvas.schema.json
permissions: permissions:
contents: read contents: read
+103 -100
View File
@@ -6,130 +6,133 @@ on:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
paths: paths:
- "extensions/**" - "extensions/**"
- ".schemas/canvas.schema.json"
permissions: permissions:
contents: read contents: read
pull-requests: write
jobs: jobs:
validate: validate:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Validate changed canvas extensions - name: Setup Node.js
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with: with:
script: | node-version: "22"
const fs = require('fs'); cache: "npm"
const path = require('path');
// Collect changed extension directories from the PR diff - name: Install dependencies
const { execSync } = require('child_process'); run: npm ci --ignore-scripts
const changedFiles = execSync(
`git diff --name-only origin/${{ github.base_ref }}...HEAD`
).toString().trim().split('\n').filter(Boolean);
const EXTENSIONS_DIR = 'extensions'; - name: Validate changed canvas extensions
const EXTERNAL_ASSETS_DIR = 'external-assets'; run: |
set -euo pipefail
const changedExtDirs = new Set(); # Validate schema structure once, even for schema-only PRs.
for (const file of changedFiles) { if ! node ./eng/validate-json-schema.mjs --schema .schemas/canvas.schema.json; then
const parts = file.split('/'); echo "❌ .schemas/canvas.schema.json failed schema compilation"
if (parts[0] === EXTENSIONS_DIR && parts.length >= 2) { exit 1
const extName = parts[1]; fi
// Skip the external-assets directory — it's not a canvas extension
// Also skip external.json and other files at extensions root level
if (extName !== EXTERNAL_ASSETS_DIR && !extName.includes('.')) {
changedExtDirs.add(path.join(EXTENSIONS_DIR, extName));
}
}
}
if (changedExtDirs.size === 0) { # Collect changed extension directories.
console.log('No canvas extension directories changed — skipping validation.'); # Use null-terminated (-z) output from git diff so filenames containing newlines
return; # or other special characters are read atomically (matches the pattern in skill-check.yml).
} # Each extracted name is then validated against a strict allowlist regex before use,
# rejecting anything containing shell metacharacters ($, (, ), spaces, etc.).
declare -A seen_dirs=()
changed_extensions=()
errors=()
console.log(`Validating ${changedExtDirs.size} extension(s): ${[...changedExtDirs].join(', ')}`); while IFS= read -r -d '' file; do
case "$file" in
extensions/*)
ext_name="${file#extensions/}"
ext_name="${ext_name%%/*}"
const errors = []; if [ "$ext_name" = "external-assets" ]; then
continue
fi
for (const extDir of changedExtDirs) { # Allowlist: extension directory names must be lowercase alphanumeric + hyphens only.
if (!fs.existsSync(extDir)) { # Fail for disallowed names so a PR cannot bypass validation by using a nonconforming folder.
// Directory was deleted — skip if [[ ! "$ext_name" =~ ^[a-z0-9][a-z0-9-]*$ ]]; then
console.log(`${extDir} no longer exists (deleted?), skipping.`); errors+=("\`extensions/$ext_name\`: invalid extension directory name (must match ^[a-z0-9][a-z0-9-]*$).")
continue; continue
} fi
const extName = path.basename(extDir); if [ -z "${seen_dirs[$ext_name]+x}" ]; then
seen_dirs["$ext_name"]=1
changed_extensions+=("extensions/$ext_name")
fi
;;
esac
done < <(git diff --name-only -z "origin/${{ github.base_ref }}...HEAD")
// Rule 1: must contain extension.mjs if [ "${#changed_extensions[@]}" -eq 0 ]; then
const mainFile = path.join(extDir, 'extension.mjs'); if [ "${#errors[@]}" -ne 0 ]; then
if (!fs.existsSync(mainFile)) { echo "❌ Canvas extension validation failed:"
errors.push( for error in "${errors[@]}"; do
`**\`${extDir}\`**: missing required \`extension.mjs\`. ` + echo "- $error"
`Canvas extensions must have their entry point named \`extension.mjs\`.` done
); exit 1
} fi
echo "No canvas extension directories changed — skipping validation."
exit 0
fi
// Rule 2: must contain assets/preview.png echo "Validating ${#changed_extensions[@]} extension(s): ${changed_extensions[*]}"
const previewFile = path.join(extDir, 'assets', 'preview.png');
if (!fs.existsSync(previewFile)) {
errors.push(
`**\`${extDir}\`**: missing required \`assets/preview.png\`. ` +
`Canvas extensions must include a screenshot at \`assets/preview.png\` ` +
`so reviewers and users can preview the extension before installing it.`
);
}
}
if (errors.length === 0) { for ext_dir in "${changed_extensions[@]}"; do
console.log('✅ All changed canvas extensions pass validation.'); if [ ! -d "$ext_dir" ]; then
return; echo "$ext_dir no longer exists (deleted?), skipping."
} continue
fi
const isFork = context.payload.pull_request.head.repo.fork; if [ ! -f "$ext_dir/extension.mjs" ]; then
const body = [ errors+=("\`$ext_dir\`: missing required \`extension.mjs\`.")
'❌ **Canvas extension validation failed**', fi
'',
'The following issue(s) were found in changed canvas extension(s):',
'',
...errors.map(e => `- ${e}`),
'',
'---',
'',
'### Required structure for canvas extensions',
'',
'Each extension folder under `extensions/` must contain:',
'',
'| Path | Required | Description |',
'|------|----------|-------------|',
'| `extension.mjs` | ✅ | Entry point for the canvas extension |',
'| `assets/preview.png` | ✅ | Screenshot shown on the website and in the marketplace |',
'',
'Please add the missing file(s) and push an update to this PR.',
].join('\n');
if (!isFork) { if [ ! -f "$ext_dir/canvas.json" ]; then
try { errors+=("\`$ext_dir\`: missing required \`canvas.json\`.")
await github.rest.pulls.createReview({ continue
owner: context.repo.owner, fi
repo: context.repo.repo,
pull_number: context.issue.number,
event: 'REQUEST_CHANGES',
body
});
} catch (error) {
core.warning(`Could not post PR review: ${error.message}`);
core.warning(body);
}
} else {
core.warning('PR is from a fork — skipping createReview to avoid permission errors.');
core.warning(body);
}
core.setFailed(`Canvas extension validation failed with ${errors.length} error(s).`); if [ ! -f "$ext_dir/assets/preview.png" ]; then
errors+=("\`$ext_dir\`: missing required \`assets/preview.png\`.")
fi
if ! schema_output="$(node ./eng/validate-json-schema.mjs --schema .schemas/canvas.schema.json --data "$ext_dir/canvas.json" 2>&1)"; then
condensed_output="$(echo "$schema_output" | tr '\n' ' ' | sed 's/[[:space:]]\+/ /g')"
errors+=("\`$ext_dir/canvas.json\`: schema validation failed against \`.schemas/canvas.schema.json\` ($condensed_output).")
continue
fi
mapfile -t screenshot_paths < <(
node -e 'const fs = require("fs"); const manifest = JSON.parse(fs.readFileSync(process.argv[1], "utf8")); const paths = [manifest?.screenshots?.icon?.path, manifest?.screenshots?.gallery?.path].filter((value) => typeof value === "string" && value.trim().length > 0); for (const value of [...new Set(paths)]) { console.log(value); }' "$ext_dir/canvas.json"
)
for screenshot_path in "${screenshot_paths[@]}"; do
if [[ ! "$screenshot_path" =~ ^assets/([A-Za-z0-9_-]+/)*[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)*\.(png|jpe?g|gif|webp|svg)$ ]]; then
errors+=("\`$ext_dir/canvas.json\`: screenshot path \`$screenshot_path\` is not a valid assets path.")
continue
fi
if [ ! -f "$ext_dir/$screenshot_path" ]; then
errors+=("\`$ext_dir/canvas.json\`: screenshot path \`$screenshot_path\` does not exist in the extension directory.")
fi
done
done
if [ "${#errors[@]}" -ne 0 ]; then
echo "❌ Canvas extension validation failed:"
for error in "${errors[@]}"; do
echo "- $error"
done
exit 1
fi
echo "✅ All changed canvas extensions passed validation."
+121
View File
@@ -0,0 +1,121 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Canvas Extension Manifest",
"description": "Schema for extensions/<name>/canvas.json files",
"type": "object",
"required": [
"id",
"name",
"description",
"version",
"keywords",
"screenshots"
],
"additionalProperties": false,
"properties": {
"id": {
"type": "string",
"description": "Unique identifier for the canvas extension",
"pattern": "^[a-z0-9-]+$",
"minLength": 1,
"maxLength": 100
},
"name": {
"type": "string",
"description": "Display name for the extension",
"minLength": 1,
"maxLength": 100
},
"description": {
"type": "string",
"description": "Human-friendly description of what the extension does",
"minLength": 1,
"maxLength": 500
},
"version": {
"type": "string",
"description": "Semantic version of the extension metadata",
"pattern": "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?(?:\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?$"
},
"author": {
"type": "object",
"description": "Optional author metadata",
"required": [
"name"
],
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"url": {
"type": "string",
"format": "uri",
"maxLength": 2048
}
}
},
"keywords": {
"type": "array",
"description": "Keywords used for search and filtering",
"items": {
"type": "string",
"pattern": "^[a-z0-9-]+$",
"minLength": 1,
"maxLength": 50
},
"minItems": 1,
"maxItems": 20,
"uniqueItems": true
},
"screenshots": {
"type": "object",
"description": "Screenshot metadata for icon and gallery cards",
"required": [
"icon",
"gallery"
],
"additionalProperties": false,
"properties": {
"icon": {
"$ref": "#/definitions/screenshot"
},
"gallery": {
"$ref": "#/definitions/screenshot"
}
}
}
},
"definitions": {
"screenshot": {
"type": "object",
"required": [
"path",
"type"
],
"additionalProperties": false,
"properties": {
"path": {
"type": "string",
"description": "Path relative to the extension root",
"pattern": "^assets/(?:[A-Za-z0-9_-]+/)*[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)*\\.(png|jpg|jpeg|gif|webp|svg)$",
"minLength": 1,
"maxLength": 200
},
"type": {
"type": "string",
"description": "MIME type for the referenced image",
"enum": [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"image/svg+xml"
]
}
}
}
}
}
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env node
import fs from "fs";
import path from "path";
import Ajv from "ajv";
import addFormats from "ajv-formats";
function parseArgs(argv) {
const args = { schema: null, data: null };
for (let i = 2; i < argv.length; i += 1) {
const arg = argv[i];
if (arg === "--schema") {
args.schema = argv[i + 1] || null;
i += 1;
} else if (arg === "--data") {
args.data = argv[i + 1] || null;
i += 1;
}
}
return args;
}
function readJson(filePath) {
const content = fs.readFileSync(filePath, "utf8");
return JSON.parse(content);
}
const args = parseArgs(process.argv);
if (!args.schema) {
console.error("Missing required argument: --schema <path>");
process.exit(1);
}
const schemaPath = path.resolve(process.cwd(), args.schema);
let schema;
try {
schema = readJson(schemaPath);
} catch (error) {
console.error(`Invalid schema JSON at ${args.schema}: ${error.message}`);
process.exit(1);
}
const ajv = new Ajv({ strict: false, allErrors: true });
addFormats(ajv);
let validate;
try {
validate = ajv.compile(schema);
} catch (error) {
console.error(`Invalid schema at ${args.schema}: ${error.message}`);
process.exit(1);
}
if (!args.data) {
console.log(`Schema is valid: ${args.schema}`);
process.exit(0);
}
const dataPath = path.resolve(process.cwd(), args.data);
let data;
try {
data = readJson(dataPath);
} catch (error) {
console.error(`Invalid data JSON at ${args.data}: ${error.message}`);
process.exit(1);
}
const valid = validate(data);
if (!valid) {
const message = ajv.errorsText(validate.errors, { separator: "; " });
console.error(`Schema validation failed for ${args.data}: ${message}`);
process.exit(1);
}
console.log(`Schema validation passed: ${args.data}`);
+1 -1
View File
@@ -1,6 +1,6 @@
// YAML parser for frontmatter parsing using vfile-matter // YAML parser for frontmatter parsing using vfile-matter
import fs from "fs"; import fs from "fs";
import yaml from "js-yaml"; import * as yaml from "js-yaml";
import path from "path"; import path from "path";
import { VFile } from "vfile"; import { VFile } from "vfile";
import { matter } from "vfile-matter"; import { matter } from "vfile-matter";
+120 -42
View File
@@ -15,6 +15,8 @@
}, },
"devDependencies": { "devDependencies": {
"@microsoft/vally": "^0.6.0", "@microsoft/vally": "^0.6.0",
"ajv": "^8.20.0",
"ajv-formats": "^3.0.1",
"all-contributors-cli": "^6.26.1" "all-contributors-cli": "^6.26.1"
} }
}, },
@@ -29,9 +31,9 @@
} }
}, },
"node_modules/@github/copilot": { "node_modules/@github/copilot": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.67.tgz",
"integrity": "sha512-Q9nBMYEHX1bJLXzzJocQx2nZvORJ0E9gvK6ly/FCtmtA7ad96BWZvf4EHzkCNDsn56aI3zNaUSfKHUKcmIAzSg==", "integrity": "sha512-5YEY9LNXBT9Q8uShjCdYcornJJJhGtdIzSYla2+pjfXYpHsDVibqYubzYjfgffOUKFChyzOpH7n/868+t56iIg==",
"dev": true, "dev": true,
"license": "SEE LICENSE IN LICENSE.md", "license": "SEE LICENSE IN LICENSE.md",
"dependencies": { "dependencies": {
@@ -41,20 +43,20 @@
"copilot": "npm-loader.js" "copilot": "npm-loader.js"
}, },
"optionalDependencies": { "optionalDependencies": {
"@github/copilot-darwin-arm64": "1.0.64-3", "@github/copilot-darwin-arm64": "1.0.67",
"@github/copilot-darwin-x64": "1.0.64-3", "@github/copilot-darwin-x64": "1.0.67",
"@github/copilot-linux-arm64": "1.0.64-3", "@github/copilot-linux-arm64": "1.0.67",
"@github/copilot-linux-x64": "1.0.64-3", "@github/copilot-linux-x64": "1.0.67",
"@github/copilot-linuxmusl-arm64": "1.0.64-3", "@github/copilot-linuxmusl-arm64": "1.0.67",
"@github/copilot-linuxmusl-x64": "1.0.64-3", "@github/copilot-linuxmusl-x64": "1.0.67",
"@github/copilot-win32-arm64": "1.0.64-3", "@github/copilot-win32-arm64": "1.0.67",
"@github/copilot-win32-x64": "1.0.64-3" "@github/copilot-win32-x64": "1.0.67"
} }
}, },
"node_modules/@github/copilot-darwin-arm64": { "node_modules/@github/copilot-darwin-arm64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.67.tgz",
"integrity": "sha512-wlV6mRoAd/wG2V08TG+BOJ0nyOjroya24FSyA5A49z7PnUUuQXYRpa/GljvI5j3PM8aUl0DyBkXuB/DcFU818g==", "integrity": "sha512-CO3mpgFXcN6e7ZsSmjMkt1AKxMfb1+mjdn3yrf2DRnnWIURSK9kGvw+E+E1+YE37D1MBiUn/VOBmhRad5+vl0A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -69,9 +71,9 @@
} }
}, },
"node_modules/@github/copilot-darwin-x64": { "node_modules/@github/copilot-darwin-x64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.67.tgz",
"integrity": "sha512-mk48PIESL2keeemX7tLRmWRDxKwl0q3cFI1ORD2QcrieNK7pSqI3eVbfoB7MqoUUI27yzIkl67xqgl8Qq28IUQ==", "integrity": "sha512-M20Hpn3bOJRkVwAIVRK4ZlX66AqtmGfXZRxZBRFQC045QIwcfmVUP45sTSgXDb4uHWeK0cZgdTdniHwKGtMplw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -86,9 +88,9 @@
} }
}, },
"node_modules/@github/copilot-linux-arm64": { "node_modules/@github/copilot-linux-arm64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.67.tgz",
"integrity": "sha512-rCgtK3/rofQW5StSbeU0TwDUlOl2bvS2HGKyapVxow1Nvz3Q/TDB+eFRQc5ocBdv5tNSor+Caw2JGkRx5v508w==", "integrity": "sha512-b4ePtFBow+Ior+aVLKA1hHxhR5wF+ql5CD7TSg/NHGYgc1kwD+3a9uKSENy05J5Lit/G/DZ9C6JwowvvdMWSKg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -103,9 +105,9 @@
} }
}, },
"node_modules/@github/copilot-linux-x64": { "node_modules/@github/copilot-linux-x64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.67.tgz",
"integrity": "sha512-FAiBMw1h07mURSBLi3ztj5yzbP+uTbo9mhxOym1Xysut5LDpO2kYUzTYk2DlIyLGZhmH/HDOZE+b6U7lOUQy0g==", "integrity": "sha512-4ynZyfKnWAdvEPAFDDBIz1wpFttcOTJu4Y8Mlz5oXCBA0NM/rwr8K4l7Adp8UzwbfmdrMJ9y+zivqRBMDbPInA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -120,9 +122,9 @@
} }
}, },
"node_modules/@github/copilot-linuxmusl-arm64": { "node_modules/@github/copilot-linuxmusl-arm64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.67.tgz",
"integrity": "sha512-vn8P6grPf0y2mNskkdVbAz0i46b1sP9uSXv7z6kgycjprl0CdIYPDf3WEkG60vpyopfQna+iCqCLMWRnNyCk3g==", "integrity": "sha512-IjezxBU8fYUr/b5hEiniXqzwoOrJ4egrQSBbG96M+roLTqd9txP0MgxZtcRtKV7phRIdIGE109wwrn4H6hSqmA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -137,9 +139,9 @@
} }
}, },
"node_modules/@github/copilot-linuxmusl-x64": { "node_modules/@github/copilot-linuxmusl-x64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.67.tgz",
"integrity": "sha512-atdHimNd6nzRRwHybXUY6/84bYzXeKbDOeYN/N/DsX23+AQOPSu5BD8MD8166I/5kNHui0XOmeTSydVNBUwcJw==", "integrity": "sha512-Zy/rbja1lnhzDoNfn051H0EybCseCvjvH7WmbcHCayjXUjzXeKF6OmAt4hvqFZH87ttT3KbKtQ8/6oDUhhM2YQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -154,13 +156,13 @@
} }
}, },
"node_modules/@github/copilot-sdk": { "node_modules/@github/copilot-sdk": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-sdk/-/copilot-sdk-1.0.4.tgz",
"integrity": "sha512-ujnH2QVw3+xvjgo9cbpY0wik4fNxAmdMDSFnxGScDSvRuK2vUCL2xWW4V2ANc9pWwRHPBpEpMuNJMtmydmLCIQ==", "integrity": "sha512-c6/gpatP7LAuP9stlc2uXXOrB+TJMFSs3Jrzu9FG8lJJYFGmdzbBvGz2UdL1jww84fp6D7cohEZa4UGq1+iuyA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@github/copilot": "^1.0.64-1", "@github/copilot": "^1.0.65",
"vscode-jsonrpc": "^8.2.1", "vscode-jsonrpc": "^8.2.1",
"zod": "^4.3.6" "zod": "^4.3.6"
}, },
@@ -169,9 +171,9 @@
} }
}, },
"node_modules/@github/copilot-win32-arm64": { "node_modules/@github/copilot-win32-arm64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.67.tgz",
"integrity": "sha512-jUTS9meoHEXQR8nMDOjwC0baqV273lYtLxY46W7TiOFszhsqhbhWxQMkNQBfT3GEfPp+40igzMPq3reaUTuvag==", "integrity": "sha512-O3VFRS5v9NXRP8o+N1SvcFbBqECDzZP7XQBeBj2Vcrma80gdJc5GQub/w2mwmr1w5UbwgzJkRasm0Ec/jxbcoA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -186,9 +188,9 @@
} }
}, },
"node_modules/@github/copilot-win32-x64": { "node_modules/@github/copilot-win32-x64": {
"version": "1.0.64-3", "version": "1.0.67",
"resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.64-3.tgz", "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.67.tgz",
"integrity": "sha512-gHUhS500Q91hjtH9fqKDblaIs0mO09G4ifpZ1woDPXbkdKe/W29uwB7g2fn0+KczNRyPxFSWlqjnOon4Fe8svA==", "integrity": "sha512-td5tQ/nve5dB7RPvNglBZwa/6DJqiOBgacXXa1GpYcohqpCzoI8gONNkeaeyr6oF4iu5wXJ9krUNr6QXL4yB5Q==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -221,6 +223,41 @@
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/ajv": {
"version": "8.20.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
"integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/all-contributors-cli": { "node_modules/all-contributors-cli": {
"version": "6.26.1", "version": "6.26.1",
"resolved": "https://registry.npmjs.org/all-contributors-cli/-/all-contributors-cli-6.26.1.tgz", "resolved": "https://registry.npmjs.org/all-contributors-cli/-/all-contributors-cli-6.26.1.tgz",
@@ -473,6 +510,30 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz",
"integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/figures": { "node_modules/figures": {
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
@@ -618,6 +679,13 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"dev": true,
"license": "MIT"
},
"node_modules/locate-path": { "node_modules/locate-path": {
"version": "5.0.0", "version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@@ -632,9 +700,9 @@
} }
}, },
"node_modules/lodash": { "node_modules/lodash": {
"version": "4.18.1", "version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
@@ -817,6 +885,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/require-main-filename": { "node_modules/require-main-filename": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+2
View File
@@ -38,6 +38,8 @@
"author": "GitHub", "author": "GitHub",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"ajv": "^8.20.0",
"ajv-formats": "^3.0.1",
"@microsoft/vally": "^0.6.0", "@microsoft/vally": "^0.6.0",
"all-contributors-cli": "^6.26.1" "all-contributors-cli": "^6.26.1"
}, },
+2 -3
View File
@@ -499,9 +499,8 @@ import PageHeader from '../components/PageHeader.astro';
<tfoot> <tfoot>
<tr> <tr>
<td align="center" size="13px" colspan="7"> <td align="center" size="13px" colspan="7">
<img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg"> <img src="https://raw.githubusercontent.com/all-contributors/all-contributors-cli/1b8533af435da9854653492b1327a23a4dbd0a10/assets/logo-small.svg" alt="" />
<a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a> <a href="https://all-contributors.js.org/docs/en/bot/usage">Add your contributions</a>
</img>
</td> </td>
</tr> </tr>
</tfoot> </tfoot>