📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -17,10 +17,306 @@ const REFERENCES_FILES = new Set([
|
||||
"package-lock.json",
|
||||
]);
|
||||
|
||||
const REGULAR_FILE_MODE = "100644";
|
||||
const ABSENT_FILE_MODE = "000000";
|
||||
const MAX_APPROVAL_BLOB_BYTES = 1024 * 1024;
|
||||
const MAX_APPROVAL_CHANGE_RECORDS = 500;
|
||||
const MAX_APPROVAL_TOTAL_BLOB_BYTES = 8 * 1024 * 1024;
|
||||
const SKILL_PATH_SEGMENT = "[a-z0-9]+(?:-[a-z0-9]+)*";
|
||||
const CANONICAL_SKILL_PATH = new RegExp(`^skills\\/(?:${SKILL_PATH_SEGMENT}\\/)+SKILL\\.md$`, "u");
|
||||
const SKILL_SUPPORT_PATH = new RegExp(
|
||||
`^skills\\/(?:${SKILL_PATH_SEGMENT}\\/)+(?:assets|references|resources)\\/(?:[^/]+\\/)*[^/]+$`,
|
||||
"u",
|
||||
);
|
||||
const NARROW_DOC_PATH = /^(?:README\.md|CONTRIBUTING\.md|docs\/(?:[^/]+\/)*[^/]+\.md)$/;
|
||||
const LOW_RISK_EXTENSIONS = new Set([
|
||||
".csv",
|
||||
".gif",
|
||||
".jpeg",
|
||||
".jpg",
|
||||
".json",
|
||||
".md",
|
||||
".pdf",
|
||||
".png",
|
||||
".txt",
|
||||
".webp",
|
||||
".yaml",
|
||||
".yml",
|
||||
]);
|
||||
const CHANGE_STATUS_SIDES = Object.freeze({
|
||||
A: { old: false, new: true },
|
||||
C: { old: true, new: true },
|
||||
D: { old: true, new: false },
|
||||
M: { old: true, new: true },
|
||||
R: { old: true, new: true },
|
||||
});
|
||||
|
||||
function normalizeRepoPath(filePath) {
|
||||
return String(filePath || "").replace(/\\/g, "/").replace(/^\.\//, "");
|
||||
}
|
||||
|
||||
function validateRawRepoPath(filePath) {
|
||||
const value = typeof filePath === "string" ? filePath : "";
|
||||
const reasons = [];
|
||||
|
||||
if (!value) {
|
||||
reasons.push("missing_path");
|
||||
}
|
||||
if (/[\u0000-\u001f\u007f]/u.test(value)) {
|
||||
reasons.push("control_character_path");
|
||||
}
|
||||
if (value.includes("\\")) {
|
||||
reasons.push("backslash_path");
|
||||
}
|
||||
if (value.startsWith("/") || /^[A-Za-z]:/u.test(value)) {
|
||||
reasons.push("absolute_path");
|
||||
}
|
||||
if (value.startsWith("./") || value.includes("//")) {
|
||||
reasons.push("noncanonical_path");
|
||||
}
|
||||
|
||||
const segments = value.split("/");
|
||||
if (segments.some((segment) => (
|
||||
!segment ||
|
||||
segment === "." ||
|
||||
segment === ".." ||
|
||||
segment.trim() !== segment ||
|
||||
segment.endsWith(".") ||
|
||||
segment.includes(":") ||
|
||||
Buffer.byteLength(segment, "utf8") > 255
|
||||
))) {
|
||||
reasons.push("noncanonical_path");
|
||||
}
|
||||
if (Buffer.byteLength(value, "utf8") > 4096) {
|
||||
reasons.push("noncanonical_path");
|
||||
}
|
||||
if (value && path.posix.normalize(value) !== value) {
|
||||
reasons.push("noncanonical_path");
|
||||
}
|
||||
if (value && value.normalize("NFC") !== value) {
|
||||
reasons.push("noncanonical_unicode_path");
|
||||
}
|
||||
|
||||
return {
|
||||
safe: reasons.length === 0,
|
||||
reasons: [...new Set(reasons)],
|
||||
};
|
||||
}
|
||||
|
||||
function classifyPathPolicy(filePath) {
|
||||
const pathValidation = validateRawRepoPath(filePath);
|
||||
if (!pathValidation.safe) {
|
||||
return {
|
||||
safe: false,
|
||||
sensitive: true,
|
||||
approvalSafe: false,
|
||||
kind: "invalid",
|
||||
reasons: pathValidation.reasons,
|
||||
};
|
||||
}
|
||||
|
||||
let kind = "unknown";
|
||||
if (CANONICAL_SKILL_PATH.test(filePath)) {
|
||||
kind = "canonical_skill";
|
||||
} else if (SKILL_SUPPORT_PATH.test(filePath)) {
|
||||
kind = "skill_support";
|
||||
} else if (NARROW_DOC_PATH.test(filePath)) {
|
||||
kind = "documentation";
|
||||
}
|
||||
|
||||
const extension = path.posix.extname(filePath).toLowerCase();
|
||||
const recognizedExtension = LOW_RISK_EXTENSIONS.has(extension);
|
||||
const reasons = [];
|
||||
if (kind === "unknown") {
|
||||
reasons.push("unapproved_path");
|
||||
}
|
||||
if (!recognizedExtension) {
|
||||
reasons.push("unknown_extension");
|
||||
}
|
||||
|
||||
const approvalSafe = reasons.length === 0;
|
||||
return {
|
||||
safe: approvalSafe,
|
||||
sensitive: !approvalSafe,
|
||||
approvalSafe,
|
||||
kind,
|
||||
reasons,
|
||||
};
|
||||
}
|
||||
|
||||
function isFullObjectId(value, options = {}) {
|
||||
const allowZero = Boolean(options.allowZero);
|
||||
if (typeof value !== "string" || !/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u.test(value)) {
|
||||
return false;
|
||||
}
|
||||
return allowZero || !/^0+$/u.test(value);
|
||||
}
|
||||
|
||||
function readBlobSize(record, side, blobSizes) {
|
||||
const inlineSize = record[`${side}_size`];
|
||||
if (Object.prototype.hasOwnProperty.call(record, `${side}_size`)) {
|
||||
return Number.isSafeInteger(inlineSize) && inlineSize >= 0 ? inlineSize : undefined;
|
||||
}
|
||||
|
||||
const oid = record[`${side}_oid`];
|
||||
if (blobSizes instanceof Map) {
|
||||
return blobSizes.get(oid);
|
||||
}
|
||||
if (blobSizes && typeof blobSizes === "object") {
|
||||
return blobSizes[oid];
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function classifyChangeRecords(records, options = {}) {
|
||||
const requireBlobSizes = options.requireBlobSizes !== false;
|
||||
const maxBlobBytes = Number.isSafeInteger(options.maxBlobBytes) && options.maxBlobBytes >= 0
|
||||
? options.maxBlobBytes
|
||||
: MAX_APPROVAL_BLOB_BYTES;
|
||||
const maxChangeRecords = Number.isSafeInteger(options.maxChangeRecords) && options.maxChangeRecords >= 0
|
||||
? options.maxChangeRecords
|
||||
: MAX_APPROVAL_CHANGE_RECORDS;
|
||||
const maxTotalBlobBytes = Number.isSafeInteger(options.maxTotalBlobBytes) && options.maxTotalBlobBytes >= 0
|
||||
? options.maxTotalBlobBytes
|
||||
: MAX_APPROVAL_TOTAL_BLOB_BYTES;
|
||||
const reasons = [];
|
||||
const paths = [];
|
||||
const canonicalSkillChanges = [];
|
||||
const skillContentChanges = [];
|
||||
let sensitive = false;
|
||||
let totalBlobBytes = 0;
|
||||
|
||||
if (!Array.isArray(records) || records.length === 0) {
|
||||
return {
|
||||
safe: false,
|
||||
sensitive: true,
|
||||
approvalSafe: false,
|
||||
reasons: ["missing_change_records"],
|
||||
paths,
|
||||
requiresHumanReview: false,
|
||||
canonicalSkillChanges,
|
||||
skillContentChanges,
|
||||
};
|
||||
}
|
||||
if (records.length > maxChangeRecords) {
|
||||
reasons.push("too_many_change_records");
|
||||
sensitive = true;
|
||||
}
|
||||
|
||||
records.forEach((record, index) => {
|
||||
const prefix = `record_${index}`;
|
||||
if (!record || typeof record !== "object") {
|
||||
reasons.push(`${prefix}:malformed_record`);
|
||||
sensitive = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const status = String(record.status || "");
|
||||
const expectedSides = CHANGE_STATUS_SIDES[status];
|
||||
if (!expectedSides) {
|
||||
reasons.push(`${prefix}:unknown_status`);
|
||||
sensitive = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const side of ["old", "new"]) {
|
||||
const expected = expectedSides[side];
|
||||
const filePath = record[`${side}_path`];
|
||||
const mode = String(record[`${side}_mode`] || "");
|
||||
const oid = String(record[`${side}_oid`] || "");
|
||||
|
||||
if (!expected) {
|
||||
if (filePath !== null) {
|
||||
reasons.push(`${prefix}:${side}_invalid_absent_path`);
|
||||
}
|
||||
if (mode !== ABSENT_FILE_MODE || !isFullObjectId(oid, { allowZero: true }) || !/^0+$/u.test(oid)) {
|
||||
reasons.push(`${prefix}:${side}_invalid_absent_side`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof filePath !== "string" || !filePath) {
|
||||
reasons.push(`${prefix}:${side}_missing_path`);
|
||||
sensitive = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const pathPolicy = classifyPathPolicy(filePath);
|
||||
paths.push({ record: index, side, path: filePath, ...pathPolicy });
|
||||
if (!pathPolicy.approvalSafe) {
|
||||
sensitive = true;
|
||||
for (const reason of pathPolicy.reasons) {
|
||||
reasons.push(`${prefix}:${side}_${reason}`);
|
||||
}
|
||||
}
|
||||
if (pathPolicy.kind === "canonical_skill") {
|
||||
canonicalSkillChanges.push(filePath);
|
||||
}
|
||||
if (["canonical_skill", "skill_support"].includes(pathPolicy.kind)) {
|
||||
skillContentChanges.push(filePath);
|
||||
}
|
||||
|
||||
if (mode !== REGULAR_FILE_MODE) {
|
||||
sensitive = true;
|
||||
if (mode === "100755") {
|
||||
reasons.push(`${prefix}:${side}_executable_mode`);
|
||||
} else if (mode === "120000") {
|
||||
reasons.push(`${prefix}:${side}_symlink_mode`);
|
||||
} else if (mode === "160000") {
|
||||
reasons.push(`${prefix}:${side}_gitlink_mode`);
|
||||
} else {
|
||||
reasons.push(`${prefix}:${side}_unknown_mode`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isFullObjectId(oid)) {
|
||||
sensitive = true;
|
||||
reasons.push(`${prefix}:${side}_invalid_object_id`);
|
||||
} else {
|
||||
const blobSize = readBlobSize(record, side, options.blobSizes);
|
||||
if ((!Number.isSafeInteger(blobSize) || blobSize < 0) && requireBlobSizes) {
|
||||
sensitive = true;
|
||||
reasons.push(`${prefix}:${side}_missing_blob_size`);
|
||||
} else if (Number.isSafeInteger(blobSize) && blobSize > maxBlobBytes) {
|
||||
sensitive = true;
|
||||
reasons.push(`${prefix}:${side}_oversized_blob`);
|
||||
} else if (Number.isSafeInteger(blobSize)) {
|
||||
totalBlobBytes += blobSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status === "M" && record.old_path !== record.new_path) {
|
||||
reasons.push(`${prefix}:path_changed_without_rename`);
|
||||
sensitive = true;
|
||||
}
|
||||
if ((status === "R" || status === "C") && record.old_path === record.new_path) {
|
||||
reasons.push(`${prefix}:rename_copy_path_unchanged`);
|
||||
sensitive = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (totalBlobBytes > maxTotalBlobBytes) {
|
||||
reasons.push("oversized_total_diff");
|
||||
sensitive = true;
|
||||
}
|
||||
|
||||
const uniqueReasons = [...new Set(reasons)];
|
||||
const uniqueCanonicalSkillChanges = [...new Set(canonicalSkillChanges)].sort();
|
||||
const uniqueSkillContentChanges = [...new Set(skillContentChanges)].sort();
|
||||
const approvalSafe = uniqueReasons.length === 0 && !sensitive;
|
||||
return {
|
||||
safe: approvalSafe,
|
||||
sensitive: !approvalSafe,
|
||||
approvalSafe,
|
||||
reasons: uniqueReasons,
|
||||
paths,
|
||||
requiresHumanReview: uniqueSkillContentChanges.length > 0,
|
||||
canonicalSkillChanges: uniqueCanonicalSkillChanges,
|
||||
skillContentChanges: uniqueSkillContentChanges,
|
||||
};
|
||||
}
|
||||
|
||||
function matchesContractEntry(filePath, entry) {
|
||||
const normalizedPath = normalizeRepoPath(filePath);
|
||||
const normalizedEntry = normalizeRepoPath(entry);
|
||||
@@ -182,7 +478,9 @@ function hasIssueLink(body) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
classifyChangeRecords,
|
||||
classifyChangedFiles,
|
||||
classifyPathPolicy,
|
||||
extractChangelogSection,
|
||||
getDirectDerivedChanges,
|
||||
getManagedFiles,
|
||||
@@ -191,7 +489,11 @@ module.exports = {
|
||||
isDerivedFile,
|
||||
isMixedFile,
|
||||
loadWorkflowContract,
|
||||
maxApprovalBlobBytes: MAX_APPROVAL_BLOB_BYTES,
|
||||
maxApprovalChangeRecords: MAX_APPROVAL_CHANGE_RECORDS,
|
||||
maxApprovalTotalBlobBytes: MAX_APPROVAL_TOTAL_BLOB_BYTES,
|
||||
normalizeRepoPath,
|
||||
matchesContractEntry,
|
||||
requiresReferencesValidation,
|
||||
validateRawRepoPath,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user