statusCodeRiskGuard.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. const NON_REDIRECTABLE_STATUS_CODES = new Set([504, 524]);
  2. export const STATUS_CODE_RISK_I18N_KEYS = {
  3. title: '高危操作确认',
  4. detailTitle: '检测到以下高危状态码重定向规则',
  5. inputPrompt: '操作确认',
  6. confirmButton: '我确认开启高危重试',
  7. markdown: '高危状态码重试风险告知与免责声明Markdown',
  8. confirmText: '高危状态码重试风险确认输入文本',
  9. inputPlaceholder: '高危状态码重试风险输入框占位文案',
  10. mismatchText: '高危状态码重试风险输入不匹配提示',
  11. };
  12. export const STATUS_CODE_RISK_CHECKLIST_KEYS = [
  13. '高危状态码重试风险确认项1',
  14. '高危状态码重试风险确认项2',
  15. '高危状态码重试风险确认项3',
  16. '高危状态码重试风险确认项4',
  17. ];
  18. function parseStatusCodeKey(rawKey) {
  19. if (typeof rawKey !== 'string') {
  20. return null;
  21. }
  22. const normalized = rawKey.trim();
  23. if (!/^[1-5]\d{2}$/.test(normalized)) {
  24. return null;
  25. }
  26. return Number.parseInt(normalized, 10);
  27. }
  28. function parseStatusCodeMappingTarget(rawValue) {
  29. if (typeof rawValue === 'number' && Number.isInteger(rawValue)) {
  30. return rawValue >= 100 && rawValue <= 599 ? rawValue : null;
  31. }
  32. if (typeof rawValue === 'string') {
  33. const normalized = rawValue.trim();
  34. if (!/^[1-5]\d{2}$/.test(normalized)) {
  35. return null;
  36. }
  37. const code = Number.parseInt(normalized, 10);
  38. return code >= 100 && code <= 599 ? code : null;
  39. }
  40. return null;
  41. }
  42. export function collectInvalidStatusCodeEntries(statusCodeMappingStr) {
  43. if (
  44. typeof statusCodeMappingStr !== 'string' ||
  45. statusCodeMappingStr.trim() === ''
  46. ) {
  47. return [];
  48. }
  49. let parsed;
  50. try {
  51. parsed = JSON.parse(statusCodeMappingStr);
  52. } catch {
  53. return [];
  54. }
  55. if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
  56. return [];
  57. }
  58. const invalid = [];
  59. for (const [rawKey, rawValue] of Object.entries(parsed)) {
  60. const fromCode = parseStatusCodeKey(rawKey);
  61. const toCode = parseStatusCodeMappingTarget(rawValue);
  62. if (fromCode === null || toCode === null) {
  63. invalid.push(`${rawKey} → ${rawValue}`);
  64. }
  65. }
  66. return invalid;
  67. }
  68. export function collectDisallowedStatusCodeRedirects(statusCodeMappingStr) {
  69. if (
  70. typeof statusCodeMappingStr !== 'string' ||
  71. statusCodeMappingStr.trim() === ''
  72. ) {
  73. return [];
  74. }
  75. let parsed;
  76. try {
  77. parsed = JSON.parse(statusCodeMappingStr);
  78. } catch (error) {
  79. return [];
  80. }
  81. if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
  82. return [];
  83. }
  84. const riskyMappings = [];
  85. Object.entries(parsed).forEach(([rawFrom, rawTo]) => {
  86. const fromCode = parseStatusCodeKey(rawFrom);
  87. const toCode = parseStatusCodeMappingTarget(rawTo);
  88. if (fromCode === null || toCode === null) {
  89. return;
  90. }
  91. if (!NON_REDIRECTABLE_STATUS_CODES.has(fromCode)) {
  92. return;
  93. }
  94. if (fromCode === toCode) {
  95. return;
  96. }
  97. riskyMappings.push(`${fromCode} -> ${toCode}`);
  98. });
  99. return Array.from(new Set(riskyMappings)).sort();
  100. }
  101. export function collectNewDisallowedStatusCodeRedirects(
  102. originalStatusCodeMappingStr,
  103. currentStatusCodeMappingStr,
  104. ) {
  105. const currentRisky = collectDisallowedStatusCodeRedirects(
  106. currentStatusCodeMappingStr,
  107. );
  108. if (currentRisky.length === 0) {
  109. return [];
  110. }
  111. const originalRiskySet = new Set(
  112. collectDisallowedStatusCodeRedirects(originalStatusCodeMappingStr),
  113. );
  114. return currentRisky.filter((mapping) => !originalRiskySet.has(mapping));
  115. }