KpiSqlSecurityValidatorTests.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. using Admin.NET.Plugin.AiDOP.SmartOps;
  2. using Xunit;
  3. namespace Admin.NET.Plugin.AiDOP.Tests.SmartOps;
  4. /// <summary>KPI 配置 SQL 安全校验(多层非正则)单元测试。覆盖合法查询 + 各类写/危险/绕过。</summary>
  5. public class KpiSqlSecurityValidatorTests
  6. {
  7. private const string LegalSelect =
  8. "SELECT AVG(cycle_days) AS metric_value FROM dwd_t8_material_online_cycle WHERE tenant_id=@tenant_id AND cycle_days IS NOT NULL";
  9. [Fact]
  10. public void LegalSelect_Valid()
  11. {
  12. var r = KpiSqlSecurityValidator.Validate(LegalSelect);
  13. Assert.True(r.Ok);
  14. Assert.Contains("dwd_t8_material_online_cycle", r.ReferencedTables);
  15. }
  16. [Fact]
  17. public void LegalWithSelect_Valid()
  18. {
  19. var sql = "WITH t AS (SELECT cycle_days FROM dwd_t8_material_online_cycle WHERE tenant_id=@tenant_id) " +
  20. "SELECT AVG(cycle_days) AS metric_value FROM mdp_std_t8_kc_tz_head";
  21. var r = KpiSqlSecurityValidator.Validate(sql);
  22. Assert.True(r.Ok);
  23. }
  24. [Theory]
  25. [InlineData("INSERT INTO dwd_t8_x(a) VALUES(1)")]
  26. [InlineData("UPDATE dwd_t8_x SET a=1")]
  27. [InlineData("DELETE FROM dwd_t8_x")]
  28. [InlineData("DROP TABLE dwd_t8_x")]
  29. [InlineData("ALTER TABLE dwd_t8_x ADD c INT")]
  30. [InlineData("CREATE TABLE t(a INT)")]
  31. [InlineData("TRUNCATE TABLE dwd_t8_x")]
  32. [InlineData("REPLACE INTO dwd_t8_x VALUES(1)")]
  33. [InlineData("MERGE INTO dwd_t8_x")]
  34. public void WriteStatements_Invalid(string sql)
  35. {
  36. var r = KpiSqlSecurityValidator.Validate(sql);
  37. Assert.False(r.Ok);
  38. }
  39. [Theory]
  40. [InlineData("EXEC sp_x")]
  41. [InlineData("EXECUTE sp_x")]
  42. [InlineData("CALL my_proc()")]
  43. public void ProcCalls_Invalid(string sql)
  44. {
  45. Assert.False(KpiSqlSecurityValidator.Validate(sql).Ok);
  46. }
  47. [Fact]
  48. public void MultiStatement_Invalid()
  49. {
  50. var r = KpiSqlSecurityValidator.Validate("SELECT 1 AS metric_value FROM dwd_t8_x; SELECT 2");
  51. Assert.False(r.Ok);
  52. Assert.Equal("MULTI_STATEMENT", r.ErrorCode);
  53. }
  54. [Fact]
  55. public void CommentHiddenSecondStatement_StrippedThenSingleStatement_Valid()
  56. {
  57. // 注释里的第二语句被去掉后应为单条合法 SELECT
  58. var sql = "SELECT AVG(cycle_days) AS metric_value FROM dwd_t8_material_online_cycle -- ; DROP TABLE x";
  59. Assert.True(KpiSqlSecurityValidator.Validate(sql).Ok);
  60. }
  61. [Fact]
  62. public void CommentHiddenKeyword_NotFalsePositive()
  63. {
  64. var sql = "SELECT AVG(cycle_days) AS metric_value /* delete update drop */ FROM dwd_t8_material_online_cycle";
  65. Assert.True(KpiSqlSecurityValidator.Validate(sql).Ok);
  66. }
  67. [Fact]
  68. public void KeywordInsideStringLiteral_NotFalsePositive()
  69. {
  70. var sql = "SELECT 'DROP TABLE users' AS result_message, 1 AS metric_value FROM dwd_t8_material_online_cycle";
  71. Assert.True(KpiSqlSecurityValidator.Validate(sql).Ok);
  72. }
  73. [Fact]
  74. public void IllegalTable_Invalid()
  75. {
  76. var r = KpiSqlSecurityValidator.Validate("SELECT metric_value FROM sys_user WHERE tenant_id=@tenant_id");
  77. Assert.False(r.Ok);
  78. Assert.Equal("TABLE_NOT_ALLOWED", r.ErrorCode);
  79. }
  80. [Theory]
  81. [InlineData("SHOW TABLES")]
  82. [InlineData("SET @a=1")]
  83. [InlineData("USE mysql")]
  84. public void NonSelectStart_Invalid(string sql)
  85. {
  86. Assert.False(KpiSqlSecurityValidator.Validate(sql).Ok);
  87. }
  88. [Fact]
  89. public void IntoOutfile_Invalid()
  90. {
  91. var r = KpiSqlSecurityValidator.Validate(
  92. "SELECT metric_value INTO OUTFILE '/tmp/x' FROM dwd_t8_material_online_cycle");
  93. Assert.False(r.Ok);
  94. }
  95. [Fact]
  96. public void Empty_Invalid()
  97. {
  98. Assert.False(KpiSqlSecurityValidator.Validate(" ").Ok);
  99. Assert.False(KpiSqlSecurityValidator.Validate("-- only comment").Ok);
  100. }
  101. }