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
2 changes: 2 additions & 0 deletions packages/core/src/subscription/import-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {
describe("extractHttpStatus", () => {
it("only extracts explicit HTTP error status context", () => {
expect(extractHttpStatus("upstream HTTP 503 then 404")).toBe(503);
expect(extractHttpStatus("upstream HTTP: 503")).toBe(503);
expect(extractHttpStatus("request failed with status code 429")).toBe(429);
expect(extractHttpStatus("request failed with status=404")).toBe(404);
expect(extractHttpStatus("upstream returned 502")).toBe(502);
Comment on lines 17 to 21
expect(extractHttpStatus("成功解析 502 个节点")).toBeNull();
expect(extractHttpStatus("HTTP 200")).toBeNull();
Expand Down
12 changes: 10 additions & 2 deletions packages/core/src/subscription/import-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,18 @@ const NETWORK_CODE_BADGE: Record<string, string> = {
};

export function extractHttpStatus(text: string): number | null {
const match = text.match(
/\b(?:HTTP(?:\/\d(?:\.\d)?)?|status(?:\s+code)?|returned|responded(?:\s+with)?)\s*[:=]?\s*(\d{3})\b/i
const prefix = text.match(
/\b(?:HTTP(?:\/\d(?:\.\d)?)?|status(?:\s+code)?|returned|responded(?:\s+with)?)/i
);
Comment on lines +75 to 77

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Continue scanning after unmatched HTTP prefixes

When the message contains an HTTP-related word before the actual status phrase, e.g. HTTP status code 404 or HTTP request failed with status code 429, this now matches the first HTTP, then line 84 requires the next token to be the number and returns null instead of reaching status code. The previous regex handled those messages, and callers such as classifyStableExternalAutoUpdateFailure rely on extractHttpStatus(text) when no numeric httpStatus/responseStatus is available, so 4xx subscription failures in these common formats can stop being classified as stable external failures.

Useful? React with 👍 / 👎.

if (!prefix || prefix.index === undefined) return null;

let remainder = text.slice(prefix.index + prefix[0].length).trimStart();
if (remainder.startsWith(":") || remainder.startsWith("=")) {
remainder = remainder.slice(1).trimStart();
}
const match = remainder.match(/^(\d{3})\b/);
if (!match) return null;

const code = Number.parseInt(match[1], 10);
return code >= 400 && code < 600 ? code : null;
Comment on lines +75 to 88
}
Expand Down
14 changes: 11 additions & 3 deletions packages/core/src/subscription/node-name-filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ function node(name: string, originName?: string): ParsedNode {
};
}

function unsafeNestedQuantifierPattern(): string {
const plus = String.fromCharCode(43);
return `(a${plus})${plus}$`;
}
Comment on lines +23 to +26

describe("node name filter config", () => {
it("treats a missing config as disabled and disables an empty enabled config", () => {
expect(parseNodeNameFilterConfig(undefined)).toEqual({
Expand Down Expand Up @@ -64,7 +69,7 @@ describe("node name filter config", () => {
it("reports the original line for invalid, unsafe, and non-string rules", () => {
const result = validateNodeNameFilterConfig({
enabled: true,
excludeRegexes: ["valid", 123, "[", "(a+)+$"],
excludeRegexes: ["valid", 123, "[", unsafeNestedQuantifierPattern()],
});

expect(result).toEqual({
Expand Down Expand Up @@ -126,7 +131,10 @@ describe("node name filter config", () => {
);

try {
parseNodeNameFilterConfig({ enabled: true, excludeRegexes: ["(", "(a+)+$"] });
parseNodeNameFilterConfig({
enabled: true,
excludeRegexes: ["(", unsafeNestedQuantifierPattern()],
});
throw new Error("Expected parsing to fail");
} catch (error) {
expect(error).toBeInstanceOf(NodeNameFilterConfigError);
Expand Down Expand Up @@ -198,7 +206,7 @@ describe("resolveNodeNameFilter", () => {
expect(() =>
resolveNodeNameFilter([node("Node")], {
enabled: true,
excludeRegexes: ["(a+)+$"],
excludeRegexes: [unsafeNestedQuantifierPattern()],
})
).toThrow(NodeNameFilterConfigError);
});
Expand Down