@ohos-graphics/stability-code-review 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +169 -0
- package/SKILL.md +518 -0
- package/bin/install.js +165 -0
- package/config/rules.yaml +445 -0
- package/config/whitelist.yaml +52 -0
- package/package.json +40 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_001.md +275 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_002.md +273 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_003.md +305 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_004.md +350 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_005.md +301 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_006.md +320 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_007.md +432 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_008.md +394 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_009.md +425 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_010.md +472 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_011.md +204 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_012.md +210 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_013.md +226 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_014.md +222 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_015.md +256 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_016.md +269 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_017.md +222 -0
- package/references/BoundaryCondition/StabilityCodeReview_BoundaryCondition_018.md +336 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_001.md +414 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_002.md +335 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_003.md +284 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_004.md +313 -0
- package/references/ConcurrencyStability/StabilityCodeReview_ConcurrencyStability_005.md +364 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_001.md +142 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_002.md +222 -0
- package/references/ExceptionHandling/StabilityCodeReview_ExceptionHandling_003.md +383 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_001.md +258 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_002.md +131 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_003.md +220 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_004.md +224 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_005.md +250 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_006.md +153 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_007.md +169 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_008.md +153 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_009.md +144 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_010.md +152 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_011.md +221 -0
- package/references/GraphicsStability/StabilityCodeReview_GraphicsStability_012.md +318 -0
- package/references/InitializationOrder/StabilityCodeReview_InitializationOrder_001.md +411 -0
- package/references/Lifecycle/StabilityCodeReview_Lifecycle_001.md +255 -0
- package/references/Lifecycle/StabilityCodeReview_Lifecycle_002.md +177 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_001.md +332 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_002.md +261 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_003.md +428 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_004.md +400 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_005.md +364 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_006.md +359 -0
- package/references/MemoryStability/StabilityCodeReview_MemoryStability_007.md +279 -0
- package/references/PROBLEM_TEMPLATE.md +65 -0
- package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_001.md +380 -0
- package/references/PerformanceStability/StabilityCodeReview_PerformanceStability_002.md +437 -0
- package/references/REPORT_TEMPLATE.csv +5 -0
- package/references/REPORT_TEMPLATE.md +132 -0
- package/references/RULE_DEVELOPMENT_GUIDE.md +711 -0
- package/references/RULE_INDEX.md +101 -0
- package/references/RULE_TEMPLATE.md +192 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_001.md +334 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_002.md +425 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_003.md +420 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_004.md +409 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_005.md +445 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_006.md +384 -0
- package/references/ResourceManagement/StabilityCodeReview_ResourceManagement_007.md +395 -0
- package/scripts/add-rule.py +423 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_BoundaryCondition_017"
|
|
3
|
+
name: "返回值类型不匹配风险"
|
|
4
|
+
category: "边界条件"
|
|
5
|
+
severity: "MEDIUM"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 返回值类型不匹配风险
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
函数返回值类型与接收变量类型不匹配可能导致隐式类型转换、数值截断、符号扩展错误等问题。这类问题可能导致数据错误、逻辑异常,甚至引发安全漏洞。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 场景1:size_t返回值赋给int
|
|
22
|
+
int GetBufferSize()
|
|
23
|
+
{
|
|
24
|
+
size_t size = CalculateSize(); // size_t可能是unsigned long
|
|
25
|
+
return size; // 错误:size_t赋给int,可能截断
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 场景2:int64返回值赋给int32
|
|
29
|
+
int32_t GetId()
|
|
30
|
+
{
|
|
31
|
+
int64_t id = GenerateUniqueId(); // int64_t
|
|
32
|
+
return id; // 错误:int64_t赋给int32_t,可能截断
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 场景3:unsigned返回值赋给signed
|
|
36
|
+
int GetCount()
|
|
37
|
+
{
|
|
38
|
+
unsigned int count = GetElementCount(); // unsigned
|
|
39
|
+
return count; // 错误:unsigned赋给int,可能产生负数错误
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 场景4:指针与整数混用
|
|
43
|
+
int GetPointerValue()
|
|
44
|
+
{
|
|
45
|
+
void* ptr = GetAddress();
|
|
46
|
+
return (int)ptr; // 错误:指针强制转换为int,64位平台指针截断
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 场景5:枚举与整数不匹配
|
|
50
|
+
int GetStatus()
|
|
51
|
+
{
|
|
52
|
+
Status status = GetObjectStatus(); // 枚举类型
|
|
53
|
+
return status; // 错误:枚举返回值类型不明确
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 场景6:函数返回bool但接收为int
|
|
57
|
+
int ProcessData()
|
|
58
|
+
{
|
|
59
|
+
bool result = ValidateData(); // bool返回值
|
|
60
|
+
return result; // 错误:bool赋给int,语义不明确
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### ✅ 修复方案
|
|
65
|
+
|
|
66
|
+
```cpp
|
|
67
|
+
// 修复场景1:使用匹配的类型
|
|
68
|
+
size_t GetBufferSize()
|
|
69
|
+
{
|
|
70
|
+
size_t size = CalculateSize();
|
|
71
|
+
return size; // 正确:返回类型匹配
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 或者如果需要int,添加范围检查
|
|
75
|
+
int GetBufferSize()
|
|
76
|
+
{
|
|
77
|
+
size_t size = CalculateSize();
|
|
78
|
+
if (size > INT_MAX) { // 正确:校验范围
|
|
79
|
+
return -1; // 错误码
|
|
80
|
+
}
|
|
81
|
+
return static_cast<int>(size);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 修复场景2:使用匹配的类型
|
|
85
|
+
int64_t GetId()
|
|
86
|
+
{
|
|
87
|
+
int64_t id = GenerateUniqueId();
|
|
88
|
+
return id; // 正确:返回类型匹配
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 修复场景3:保持类型一致性
|
|
92
|
+
unsigned int GetCount()
|
|
93
|
+
{
|
|
94
|
+
unsigned int count = GetElementCount();
|
|
95
|
+
return count; // 正确:返回类型匹配
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// 或者如果需要int,添加范围检查
|
|
99
|
+
int GetCount()
|
|
100
|
+
{
|
|
101
|
+
unsigned int count = GetElementCount();
|
|
102
|
+
if (count > INT_MAX) { // 正确:校验范围
|
|
103
|
+
LOGE("count overflow");
|
|
104
|
+
return -1;
|
|
105
|
+
}
|
|
106
|
+
return static_cast<int>(count);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 修复场景4:使用intptr_t处理指针
|
|
110
|
+
intptr_t GetPointerValue()
|
|
111
|
+
{
|
|
112
|
+
void* ptr = GetAddress();
|
|
113
|
+
return reinterpret_cast<intptr_t>(ptr); // 正确:intptr_t保证能容纳指针
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 修复场景5:明确枚举返回类型
|
|
117
|
+
Status GetStatus()
|
|
118
|
+
{
|
|
119
|
+
Status status = GetObjectStatus();
|
|
120
|
+
return status; // 正确:返回枚举类型
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 修复场景6:保持bool语义
|
|
124
|
+
bool ProcessData()
|
|
125
|
+
{
|
|
126
|
+
bool result = ValidateData();
|
|
127
|
+
return result; // 正确:返回bool类型
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 检测范围
|
|
132
|
+
|
|
133
|
+
检查以下模式:
|
|
134
|
+
|
|
135
|
+
- `size_t` / `ssize_t` 返回值赋给 `int`
|
|
136
|
+
- `int64_t` / `uint64_t` 返回值赋给 `int32_t`
|
|
137
|
+
- `unsigned` 返回值赋给 `signed` 类型
|
|
138
|
+
- 指针类型强制转换为整数类型
|
|
139
|
+
- 枚举类型返回值赋给整数
|
|
140
|
+
- `bool` 返回值赋给 `int`
|
|
141
|
+
|
|
142
|
+
## 检测要点
|
|
143
|
+
|
|
144
|
+
1. 识别函数返回值类型
|
|
145
|
+
2. 检查接收变量类型是否匹配
|
|
146
|
+
3. 判断是否存在隐式类型转换风险
|
|
147
|
+
4. 检查是否有范围校验(如 INT_MAX 检查)
|
|
148
|
+
5. 排除明确标记为安全的强制转换
|
|
149
|
+
|
|
150
|
+
## 风险流分析(RiskFlow)
|
|
151
|
+
|
|
152
|
+
- **RISK_SOURCE**:类型不匹配的返回值赋值
|
|
153
|
+
- **RISK_TYPE**:隐式类型转换错误
|
|
154
|
+
- **RISK_PATH**:返回值类型不匹配 → 隐式转换 → 数值截断或符号错误 → 数据错误
|
|
155
|
+
- **IMPACT_POINT**:逻辑错误、数据损坏、潜在安全漏洞
|
|
156
|
+
|
|
157
|
+
## 影响分析(ImpactAnalysis)
|
|
158
|
+
|
|
159
|
+
- **Trigger**:返回值超出目标类型范围
|
|
160
|
+
- **Propagation**:数值截断、符号位改变、精度丢失
|
|
161
|
+
- **Consequence**:数据错误、逻辑异常、安全漏洞
|
|
162
|
+
- **Mitigation**:使用匹配类型或添加范围校验
|
|
163
|
+
|
|
164
|
+
## 误报排除
|
|
165
|
+
|
|
166
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
167
|
+
|------|----------|----------|
|
|
168
|
+
| 有范围校验 | 有INT_MAX等范围检查 | 不报 |
|
|
169
|
+
| 明确强制转换 | 有static_cast且类型明确 | 需判断安全性 |
|
|
170
|
+
| 同类型 | 返回类型和接收类型相同 | 不报 |
|
|
171
|
+
| NOPROTECT标记 | // NOPROTECT 注释 | 不报 |
|
|
172
|
+
## 测试用例
|
|
173
|
+
|
|
174
|
+
### 触发用例(应该报)
|
|
175
|
+
|
|
176
|
+
```cpp
|
|
177
|
+
// test_BoundaryCondition_017_trigger.cpp
|
|
178
|
+
int trigger_bad_1()
|
|
179
|
+
{
|
|
180
|
+
size_t size = CalculateSize();
|
|
181
|
+
return size; // 应该报:size_t赋给int
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
int32_t trigger_bad_2()
|
|
185
|
+
{
|
|
186
|
+
int64_t id = GenerateUniqueId();
|
|
187
|
+
return id; // 应该报:int64_t赋给int32_t
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
int trigger_bad_3()
|
|
191
|
+
{
|
|
192
|
+
unsigned count = GetElementCount();
|
|
193
|
+
return count; // 应该报:unsigned赋给int
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 安全用例(不应该报)
|
|
198
|
+
|
|
199
|
+
```cpp
|
|
200
|
+
// test_BoundaryCondition_017_safe.cpp
|
|
201
|
+
size_t safe_good_1()
|
|
202
|
+
{
|
|
203
|
+
size_t size = CalculateSize();
|
|
204
|
+
return size; // 安全:类型匹配
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
int safe_good_2()
|
|
208
|
+
{
|
|
209
|
+
size_t size = CalculateSize();
|
|
210
|
+
if (size <= INT_MAX) { // 安全:有范围校验
|
|
211
|
+
return static_cast<int>(size);
|
|
212
|
+
}
|
|
213
|
+
return -1;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// NOPROTECT: 特殊场景
|
|
217
|
+
int noprotect_case()
|
|
218
|
+
{
|
|
219
|
+
unsigned count = GetElementCount();
|
|
220
|
+
return count; // NOPROTECT标记,不报
|
|
221
|
+
}
|
|
222
|
+
```
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
---
|
|
2
|
+
rule_id: "StabilityCodeReview_BoundaryCondition_018"
|
|
3
|
+
name: "JSON解析安全风险"
|
|
4
|
+
category: "边界条件"
|
|
5
|
+
severity: "HIGH"
|
|
6
|
+
language: ["cpp", "c++"]
|
|
7
|
+
author: "OH-Department7 Stability Team"
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# JSON解析安全风险
|
|
11
|
+
|
|
12
|
+
## 问题描述
|
|
13
|
+
|
|
14
|
+
JSON解析存在多种安全风险:解析深度过大导致栈溢出、超大JSON导致内存耗尽、未捕获解析异常、键不存在导致的空指针访问、类型不匹配导致的类型混淆、以及数值溢出问题。
|
|
15
|
+
|
|
16
|
+
## 检测示例
|
|
17
|
+
|
|
18
|
+
### ❌ 问题代码
|
|
19
|
+
|
|
20
|
+
```cpp
|
|
21
|
+
// 场景1:无深度限制导致栈溢出
|
|
22
|
+
void ParseNestedJson(const std::string& jsonStr)
|
|
23
|
+
{
|
|
24
|
+
Json::Value root;
|
|
25
|
+
Json::Reader reader;
|
|
26
|
+
reader.parse(jsonStr, root); // 错误:未限制解析深度,恶意嵌套JSON可致栈溢出
|
|
27
|
+
|
|
28
|
+
ProcessJson(root);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 场景2:超大JSON未限制导致内存耗尽
|
|
32
|
+
void LoadLargeJson(const char* filePath)
|
|
33
|
+
{
|
|
34
|
+
std::ifstream file(filePath);
|
|
35
|
+
Json::Value root;
|
|
36
|
+
Json::Reader reader;
|
|
37
|
+
reader.parse(file, root); // 错误:未限制JSON大小,超大文件可耗尽内存
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 场景3:未捕获解析异常
|
|
41
|
+
void ParseJsonData(const std::string& jsonStr)
|
|
42
|
+
{
|
|
43
|
+
Json::Value root;
|
|
44
|
+
Json::Reader reader;
|
|
45
|
+
if (!reader.parse(jsonStr, root)) {
|
|
46
|
+
// 错误:parse失败但未记录错误信息,继续使用root可能访问无效数据
|
|
47
|
+
ProcessData(root);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 场景4:键不存在导致空指针访问
|
|
52
|
+
void GetValueFromJson(const Json::Value& root)
|
|
53
|
+
{
|
|
54
|
+
int id = root["id"].asInt(); // 错误:键可能不存在,访问无效Json::Value
|
|
55
|
+
std::string name = root["name"].asString(); // 错误:键可能不存在
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// 场景5:类型不匹配导致错误
|
|
59
|
+
void ParseTypedData(const Json::Value& root)
|
|
60
|
+
{
|
|
61
|
+
int count = root["count"].asInt(); // 错误:count可能是字符串,类型混淆
|
|
62
|
+
double value = root["value"].asDouble(); // 错误:value可能是数组
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 场景6:数值溢出
|
|
66
|
+
void ParseLargeNumber(const Json::Value& root)
|
|
67
|
+
{
|
|
68
|
+
int32_t id = root["id"].asInt(); // 错误:id可能超过int32_t范围,溢出
|
|
69
|
+
size_t size = root["size"].asUInt(); // 错误:size可能是负数或超大值
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 场景7:数组越界访问
|
|
73
|
+
void ParseArrayData(const Json::Value& root)
|
|
74
|
+
{
|
|
75
|
+
const Json::Value& arr = root["items"];
|
|
76
|
+
for (int i = 0; i < 10; i++) { // 错误:固定循环次数,arr可能少于10个元素
|
|
77
|
+
ProcessItem(arr[i]); // 错误:可能越界访问
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 场景8:未检查解析成功状态
|
|
82
|
+
void UseJsonResult(const std::string& jsonStr)
|
|
83
|
+
{
|
|
84
|
+
Json::Value root;
|
|
85
|
+
Json::Reader reader;
|
|
86
|
+
reader.parse(jsonStr, root); // 错误:未检查parse返回值
|
|
87
|
+
|
|
88
|
+
int value = root["value"].asInt(); // root可能无效
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### ✅ 修复方案
|
|
93
|
+
|
|
94
|
+
```cpp
|
|
95
|
+
// 修复场景1:限制解析深度
|
|
96
|
+
void ParseNestedJson(const std::string& jsonStr)
|
|
97
|
+
{
|
|
98
|
+
Json::Value root;
|
|
99
|
+
Json::Reader reader;
|
|
100
|
+
|
|
101
|
+
Json::Features features;
|
|
102
|
+
features.allowNestedDepth_ = 100; // 正确:限制嵌套深度
|
|
103
|
+
reader.parse(jsonStr, root, features);
|
|
104
|
+
|
|
105
|
+
ProcessJson(root);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 修复场景2:限制JSON大小
|
|
109
|
+
void LoadLargeJson(const char* filePath)
|
|
110
|
+
{
|
|
111
|
+
std::ifstream file(filePath);
|
|
112
|
+
|
|
113
|
+
// 正确:限制文件大小
|
|
114
|
+
file.seekg(0, std::ios::end);
|
|
115
|
+
std::streamsize size = file.tellg();
|
|
116
|
+
file.seekg(0, std::ios::beg);
|
|
117
|
+
|
|
118
|
+
const size_t MAX_JSON_SIZE = 10 * 1024 * 1024; // 10MB限制
|
|
119
|
+
if (size > MAX_JSON_SIZE) {
|
|
120
|
+
LOGE("JSON file too large: %lld", size);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
Json::Value root;
|
|
125
|
+
Json::Reader reader;
|
|
126
|
+
reader.parse(file, root);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 修复场景3:捕获并处理解析错误
|
|
130
|
+
void ParseJsonData(const std::string& jsonStr)
|
|
131
|
+
{
|
|
132
|
+
Json::Value root;
|
|
133
|
+
Json::Reader reader;
|
|
134
|
+
|
|
135
|
+
if (!reader.parse(jsonStr, root)) {
|
|
136
|
+
LOGE("JSON parse failed: %s", reader.getFormattedErrorMessages().c_str()); // 正确:记录错误信息
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ProcessData(root);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 修复场景4:检查键存在性
|
|
144
|
+
void GetValueFromJson(const Json::Value& root)
|
|
145
|
+
{
|
|
146
|
+
if (!root.isMember("id") || !root["id"].isInt()) { // 正确:检查键存在和类型
|
|
147
|
+
LOGE("Missing or invalid 'id' field");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
int id = root["id"].asInt();
|
|
151
|
+
|
|
152
|
+
if (!root.isMember("name") || !root["name"].isString()) { // 正确:检查键存在和类型
|
|
153
|
+
LOGE("Missing or invalid 'name' field");
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
std::string name = root["name"].asString();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// 修复场景5:检查类型匹配
|
|
160
|
+
void ParseTypedData(const Json::Value& root)
|
|
161
|
+
{
|
|
162
|
+
if (root.isMember("count") && root["count"].isInt()) { // 正确:检查类型
|
|
163
|
+
int count = root["count"].asInt();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (root.isMember("value") && root["value"].isDouble()) { // 正确:检查类型
|
|
167
|
+
double value = root["value"].asDouble();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// 修复场景6:检查数值范围
|
|
172
|
+
void ParseLargeNumber(const Json::Value& root)
|
|
173
|
+
{
|
|
174
|
+
if (root.isMember("id") && root["id"].isInt()) {
|
|
175
|
+
Json::Int64 id64 = root["id"].asInt64(); // 正确:使用Int64避免溢出
|
|
176
|
+
|
|
177
|
+
if (id64 >= INT32_MIN && id64 <= INT32_MAX) { // 正确:检查范围
|
|
178
|
+
int32_t id = static_cast<int32_t>(id64);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (root.isMember("size") && root["size"].isUInt()) {
|
|
183
|
+
Json::UInt64 size64 = root["size"].asUInt64(); // 正确:使用UInt64
|
|
184
|
+
|
|
185
|
+
if (size64 <= SIZE_MAX) { // 正确:检查范围
|
|
186
|
+
size_t size = static_cast<size_t>(size64);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 修复场景7:检查数组大小
|
|
192
|
+
void ParseArrayData(const Json::Value& root)
|
|
193
|
+
{
|
|
194
|
+
if (!root.isMember("items") || !root["items"].isArray()) { // 正确:检查键和类型
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const Json::Value& arr = root["items"];
|
|
199
|
+
for (Json::ArrayIndex i = 0; i < arr.size(); i++) { // 正确:使用实际大小
|
|
200
|
+
ProcessItem(arr[i]);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 修复场景8:检查解析成功状态
|
|
205
|
+
void UseJsonResult(const std::string& jsonStr)
|
|
206
|
+
{
|
|
207
|
+
Json::Value root;
|
|
208
|
+
Json::Reader reader;
|
|
209
|
+
|
|
210
|
+
bool success = reader.parse(jsonStr, root); // 正确:检查返回值
|
|
211
|
+
if (!success) {
|
|
212
|
+
LOGE("JSON parse failed");
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (root.isMember("value") && root["value"].isInt()) { // 正确:检查字段
|
|
217
|
+
int value = root["value"].asInt();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## 检测范围
|
|
223
|
+
|
|
224
|
+
检查以下模式:
|
|
225
|
+
|
|
226
|
+
- JSON解析未限制嵌套深度
|
|
227
|
+
- JSON解析未限制文件/字符串大小
|
|
228
|
+
- JSON解析失败后继续使用root对象
|
|
229
|
+
- 访问JSON键前未检查键是否存在(isMember)
|
|
230
|
+
- 转换JSON值前未检查类型匹配(isXxx)
|
|
231
|
+
- 数值转换可能导致溢出(asInt转int32_t)
|
|
232
|
+
- JSON数组访问前未检查数组大小
|
|
233
|
+
|
|
234
|
+
## 检测要点
|
|
235
|
+
|
|
236
|
+
1. 识别JSON解析函数调用(parse、parseFromStream)
|
|
237
|
+
2. 检查是否有深度限制设置(allowNestedDepth)
|
|
238
|
+
3. 检查是否有大小限制检查
|
|
239
|
+
4. 检查是否检查parse返回值
|
|
240
|
+
5. 检查是否在访问键前调用isMember
|
|
241
|
+
6. 检查是否在类型转换前调用isXxx
|
|
242
|
+
7. 检查数组访问是否使用size()判断边界
|
|
243
|
+
8. 排除NOPROTECT标记
|
|
244
|
+
|
|
245
|
+
## 风险流分析(RiskFlow)
|
|
246
|
+
|
|
247
|
+
- **RISK_SOURCE**:JSON解析操作
|
|
248
|
+
- **RISK_TYPE**:栈溢出/内存耗尽/空指针/类型混淆/数值溢出
|
|
249
|
+
- **RISK_PATH**:恶意构造JSON → 解析未限制 → 资源耗尽或数据错误 → 程序崩溃
|
|
250
|
+
- **IMPACT_POINT**:栈溢出崩溃、OOM崩溃、空指针崩溃、数据损坏
|
|
251
|
+
|
|
252
|
+
## 影响分析(ImpactAnalysis)
|
|
253
|
+
|
|
254
|
+
- **Trigger**:恶意构造的JSON输入触发各种安全问题
|
|
255
|
+
- **Propagation**:栈溢出、内存耗尽、无效数据访问、数值溢出
|
|
256
|
+
- **Consequence**:程序崩溃、拒绝服务、数据损坏、安全漏洞
|
|
257
|
+
- **Mitigation**:限制深度和大小、检查返回值、验证键和类型、使用安全的数值类型
|
|
258
|
+
|
|
259
|
+
## 误报排除
|
|
260
|
+
|
|
261
|
+
| 场景 | 识别特征 | 处理方式 |
|
|
262
|
+
|------|----------|----------|
|
|
263
|
+
| 有深度限制 | 设置allowNestedDepth | 不报 |
|
|
264
|
+
| 有大小检查 | 有文件大小判断 | 不报 |
|
|
265
|
+
| 有parse返回值检查 | 有 if (!parse()) | 不报 |
|
|
266
|
+
| 有键检查 | 有 isMember() | 不报 |
|
|
267
|
+
| 有类型检查 | 有 isXxx() | 不报 |
|
|
268
|
+
| 有数组边界检查 | 使用 arr.size() | 不报 |
|
|
269
|
+
| NOPROTECT标记 | // NOPROTECT 注释 | 不报 |
|
|
270
|
+
## 测试用例
|
|
271
|
+
|
|
272
|
+
### 触发用例(应该报)
|
|
273
|
+
|
|
274
|
+
```cpp
|
|
275
|
+
// test_BoundaryCondition_018_trigger.cpp
|
|
276
|
+
void trigger_bad_1(const std::string& jsonStr)
|
|
277
|
+
{
|
|
278
|
+
Json::Value root;
|
|
279
|
+
Json::Reader reader;
|
|
280
|
+
reader.parse(jsonStr, root); // 应该报:未限制深度
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
void trigger_bad_2(const Json::Value& root)
|
|
284
|
+
{
|
|
285
|
+
int id = root["id"].asInt(); // 应该报:键未检查存在性
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
void trigger_bad_3(const Json::Value& root)
|
|
289
|
+
{
|
|
290
|
+
int count = root["count"].asInt(); // 应该报:类型未检查
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
void trigger_bad_4(const Json::Value& root)
|
|
294
|
+
{
|
|
295
|
+
const Json::Value& arr = root["items"];
|
|
296
|
+
for (int i = 0; i < 10; i++) {
|
|
297
|
+
ProcessItem(arr[i]); // 应该报:数组越界访问
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### 安全用例(不应该报)
|
|
303
|
+
|
|
304
|
+
```cpp
|
|
305
|
+
// test_BoundaryCondition_018_safe.cpp
|
|
306
|
+
void safe_good_1(const std::string& jsonStr)
|
|
307
|
+
{
|
|
308
|
+
Json::Value root;
|
|
309
|
+
Json::Reader reader;
|
|
310
|
+
|
|
311
|
+
Json::Features features;
|
|
312
|
+
features.allowNestedDepth_ = 100; // 安全:有深度限制
|
|
313
|
+
reader.parse(jsonStr, root, features);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
void safe_good_2(const Json::Value& root)
|
|
317
|
+
{
|
|
318
|
+
if (root.isMember("id") && root["id"].isInt()) { // 安全:有键和类型检查
|
|
319
|
+
int id = root["id"].asInt();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
void safe_good_3(const Json::Value& root)
|
|
324
|
+
{
|
|
325
|
+
const Json::Value& arr = root["items"];
|
|
326
|
+
for (Json::ArrayIndex i = 0; i < arr.size(); i++) { // 安全:使用size()判断边界
|
|
327
|
+
ProcessItem(arr[i]);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// NOPROTECT: 特殊场景
|
|
332
|
+
void noprotect_case(const Json::Value& root)
|
|
333
|
+
{
|
|
334
|
+
int id = root["id"].asInt(); // NOPROTECT标记,不报
|
|
335
|
+
}
|
|
336
|
+
```
|