mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge branch 'main' into oldpaths1 (some generated files are left unmerged).
This commit is contained in:
21
cpp/ql/lib/ext/ComPtr.model.yml
Normal file
21
cpp/ql/lib/ext/ComPtr.model.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/cpp-all
|
||||
extensible: summaryModel
|
||||
data: # namespace, type, subtypes, name, signature, ext, input, output, kind, provenance
|
||||
- ["Microsoft::WRL", "ComPtr", True, "ComPtr<T>", "(T *)", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "ComPtr", "(const ComPtr &)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "ComPtr", "(ComPtr &&)", "", "Argument[*0].Element[@]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "As", "", "", "Argument[-1]", "Argument[*0]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "AsIID", "", "", "Argument[-1]", "Argument[*1]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "AsWeak", "", "", "Argument[-1]", "Argument[*0]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "Attach", "", "", "Argument[*@0]", "Argument[-1].Element[@]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr<T>", True, "CopyTo", "(T **)", "", "Argument[-1].Element[@]", "Argument[**@0]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "CopyTo<T>", "(T **)", "", "Argument[-1].Element[@]", "Argument[**@0]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "CopyTo", "(REFIID,void **)", "", "Argument[-1].Element[@]", "Argument[**@1]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "Detach", "", "", "Argument[-1].Element[@]", "ReturnValue[*]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "Get", "", "", "Argument[-1].Element[@]", "ReturnValue[*]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "GetAddressOf", "", "", "Argument[-1].Element[@]", "ReturnValue[**]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "ReleaseAndGetAddressOf", "", "", "Argument[-1].Element[@]", "ReturnValue[**]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "Swap", "", "", "Argument[-1]", "Argument[*0]", "value", "manual"]
|
||||
- ["Microsoft::WRL", "ComPtr", True, "Swap", "", "", "Argument[*0]", "Argument[-1]", "value", "manual"]
|
||||
@@ -147,8 +147,8 @@ module ExecTaintConfig implements DataFlow::StateConfigSig {
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) { isBarrierImpl(node) }
|
||||
|
||||
predicate isBarrierOut(DataFlow::Node node) {
|
||||
isSink(node, _) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
|
||||
predicate isBarrierOut(DataFlow::Node node, FlowState state) {
|
||||
isSink(node, state) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
|
||||
}
|
||||
|
||||
predicate observeDiffInformedIncrementalMode() { any() }
|
||||
|
||||
@@ -503,6 +503,7 @@
|
||||
| Dubious signature "(CURLU *,CURLUPart,const char *,unsigned int)" in summary model. |
|
||||
| Dubious signature "(CURLU *,const char *)" in summary model. |
|
||||
| Dubious signature "(CURLU *,const char *,char **,OperationConfig *)" in summary model. |
|
||||
| Dubious signature "(ComPtr &&)" in summary model. |
|
||||
| Dubious signature "(CompoundDictionary *,const PreparedDictionary *)" in summary model. |
|
||||
| Dubious signature "(Curl_cfilter *)" in summary model. |
|
||||
| Dubious signature "(Curl_cfilter **,Curl_easy *)" in summary model. |
|
||||
@@ -2130,6 +2131,7 @@
|
||||
| Dubious signature "(RAND_POOL *,unsigned char *)" in summary model. |
|
||||
| Dubious signature "(RAND_POOL *,unsigned int)" in summary model. |
|
||||
| Dubious signature "(RECORD_LAYER *,SSL_CONNECTION *)" in summary model. |
|
||||
| Dubious signature "(REFIID,void **)" in summary model. |
|
||||
| Dubious signature "(RIO_NOTIFIER *)" in summary model. |
|
||||
| Dubious signature "(RIPEMD160_CTX *,const unsigned char *)" in summary model. |
|
||||
| Dubious signature "(RIPEMD160_CTX *,const void *,size_t)" in summary model. |
|
||||
@@ -2431,6 +2433,8 @@
|
||||
| Dubious signature "(Strent *)" in summary model. |
|
||||
| Dubious signature "(Strtab *,const char *,size_t)" in summary model. |
|
||||
| Dubious signature "(Strtab *,size_t *)" in summary model. |
|
||||
| Dubious signature "(T *)" in summary model. |
|
||||
| Dubious signature "(T **)" in summary model. |
|
||||
| Dubious signature "(TLS_FEATURE *)" in summary model. |
|
||||
| Dubious signature "(TLS_RL_RECORD *,const unsigned char *)" in summary model. |
|
||||
| Dubious signature "(TS_ACCURACY *)" in summary model. |
|
||||
@@ -3155,6 +3159,7 @@
|
||||
| Dubious signature "(const CT_POLICY_EVAL_CTX *)" in summary model. |
|
||||
| Dubious signature "(const CURLU *)" in summary model. |
|
||||
| Dubious signature "(const CURLU *,CURLUPart,char **,unsigned int)" in summary model. |
|
||||
| Dubious signature "(const ComPtr &)" in summary model. |
|
||||
| Dubious signature "(const Command *,const size_t,const BlockSplit *,const BlockSplit *,const BlockSplit *,const uint8_t *,size_t,size_t,uint8_t,uint8_t,const ContextType *,HistogramLiteral *,HistogramCommand *,HistogramDistance *)" in summary model. |
|
||||
| Dubious signature "(const Curl_easy *,const connectdata *,int)" in summary model. |
|
||||
| Dubious signature "(const DH *)" in summary model. |
|
||||
|
||||
@@ -1241,4 +1241,140 @@ namespace ATL {
|
||||
sink(static_cast<CStrBufT<char>::PCXSTR>(b)); // $ ir
|
||||
sink(static_cast<CStrBufT<char>::PXSTR>(b)); // $ ir
|
||||
}
|
||||
}
|
||||
|
||||
namespace Microsoft {
|
||||
namespace WRL {
|
||||
template <typename T>
|
||||
class ComPtr;
|
||||
|
||||
struct GUID;
|
||||
|
||||
typedef GUID IID;
|
||||
|
||||
typedef IID *REFIID;
|
||||
|
||||
class IUnknown;
|
||||
|
||||
class WeakRef;
|
||||
|
||||
template <typename T>
|
||||
class ComPtr
|
||||
{
|
||||
public:
|
||||
using InterfaceType = T;
|
||||
|
||||
ComPtr();
|
||||
ComPtr(const ComPtr &);
|
||||
ComPtr(ComPtr &&);
|
||||
|
||||
template <typename U>
|
||||
ComPtr(U *);
|
||||
|
||||
~ComPtr();
|
||||
|
||||
template <typename U>
|
||||
HRESULT As(ComPtr<U> *p) const;
|
||||
|
||||
HRESULT AsWeak(WeakRef *);
|
||||
|
||||
void Attach(InterfaceType *);
|
||||
|
||||
HRESULT CopyTo(InterfaceType **);
|
||||
|
||||
HRESULT CopyTo(REFIID, void **) const;
|
||||
|
||||
template <typename U>
|
||||
HRESULT CopyTo(U **) const;
|
||||
|
||||
T *Detach();
|
||||
|
||||
T *Get() const;
|
||||
|
||||
T *const *GetAddressOf() const;
|
||||
T **GetAddressOf();
|
||||
|
||||
T **ReleaseAndGetAddressOf();
|
||||
|
||||
unsigned long Reset();
|
||||
|
||||
void Swap(ComPtr &&r);
|
||||
|
||||
void Swap(ComPtr &r);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<class T> T&& move(T& t) noexcept; // simplified signature
|
||||
}
|
||||
|
||||
void test_constructor()
|
||||
{
|
||||
Microsoft::WRL::ComPtr<int> p0;
|
||||
sink(*p0.Get()); // clean
|
||||
|
||||
int x = source<int>();
|
||||
Microsoft::WRL::ComPtr<int> p1(new int(x));
|
||||
sink(*p1.Get()); // $ ir MISSING: ast
|
||||
sink(*p1.Detach()); // $ ir MISSING: ast
|
||||
|
||||
Microsoft::WRL::ComPtr<int> p2(p1);
|
||||
sink(*p2.Get()); // $ ir MISSING: ast
|
||||
|
||||
Microsoft::WRL::ComPtr<int> p3(std::move(p1));
|
||||
sink(*p3.Get()); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_As()
|
||||
{
|
||||
int x = source<int>();
|
||||
Microsoft::WRL::ComPtr<int> p1(new int(x));
|
||||
Microsoft::WRL::ComPtr<int> p2;
|
||||
p1.As(&p2);
|
||||
sink(*p2.Get()); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_CopyTo()
|
||||
{
|
||||
int x = source<int>();
|
||||
Microsoft::WRL::ComPtr<int> p1(new int(x));
|
||||
int *raw = nullptr;
|
||||
p1.CopyTo(&raw);
|
||||
sink(*raw); // $ ir MISSING: ast
|
||||
|
||||
Microsoft::WRL::ComPtr<int> p2;
|
||||
p1.CopyTo(nullptr, (void**)&raw);
|
||||
sink(*raw); // $ ir MISSING: ast
|
||||
|
||||
Microsoft::WRL::ComPtr<int> p3(new int(x));
|
||||
|
||||
int* raw2 = nullptr;
|
||||
p3.CopyTo<int>(&raw2);
|
||||
sink(*raw2); // $ ir MISSING: ast
|
||||
}
|
||||
|
||||
void test_Swap()
|
||||
{
|
||||
int x = source<int>();
|
||||
Microsoft::WRL::ComPtr<int> p1(new int(x));
|
||||
Microsoft::WRL::ComPtr<int> p2;
|
||||
p1.Swap(p2);
|
||||
sink(*p2.Get()); // $ ir MISSING: ast
|
||||
sink(*p1.Get()); // $ SPURIOUS: ir
|
||||
}
|
||||
|
||||
void test_GetAddressOf()
|
||||
{
|
||||
int x = source<int>();
|
||||
Microsoft::WRL::ComPtr<int> p1(new int(x));
|
||||
sink(**p1.GetAddressOf()); // $ ir MISSING: ast
|
||||
|
||||
const Microsoft::WRL::ComPtr<int> p2(new int(x));
|
||||
sink(**p2.GetAddressOf()); // $ ir MISSING: ast
|
||||
|
||||
Microsoft::WRL::ComPtr<int> p3(new int(x));
|
||||
int **pp = p3.ReleaseAndGetAddressOf();
|
||||
sink(**pp); // $ ir MISSING: ast
|
||||
}
|
||||
@@ -1278,6 +1278,131 @@ WARNING: module 'TaintTracking' has been deprecated and may be removed in future
|
||||
| atl.cpp:1240:22:1240:30 | call to CStrBufT | atl.cpp:1241:46:1241:46 | b | |
|
||||
| atl.cpp:1240:22:1240:30 | call to CStrBufT | atl.cpp:1242:45:1242:45 | b | |
|
||||
| atl.cpp:1241:46:1241:46 | ref arg b | atl.cpp:1242:45:1242:45 | b | |
|
||||
| atl.cpp:1315:31:1315:32 | call to ComPtr | atl.cpp:1316:9:1316:10 | p0 | |
|
||||
| atl.cpp:1315:31:1315:32 | call to ComPtr | atl.cpp:1328:1:1328:1 | p0 | |
|
||||
| atl.cpp:1316:9:1316:10 | ref arg p0 | atl.cpp:1328:1:1328:1 | p0 | |
|
||||
| atl.cpp:1316:12:1316:14 | call to Get | atl.cpp:1316:8:1316:16 | * ... | TAINT |
|
||||
| atl.cpp:1318:11:1318:21 | call to source | atl.cpp:1319:42:1319:42 | x | |
|
||||
| atl.cpp:1319:34:1319:43 | new | atl.cpp:1319:34:1319:44 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1319:34:1319:44 | call to ComPtr | atl.cpp:1320:9:1320:10 | p1 | |
|
||||
| atl.cpp:1319:34:1319:44 | call to ComPtr | atl.cpp:1321:9:1321:10 | p1 | |
|
||||
| atl.cpp:1319:34:1319:44 | call to ComPtr | atl.cpp:1323:34:1323:35 | p1 | |
|
||||
| atl.cpp:1319:34:1319:44 | call to ComPtr | atl.cpp:1326:44:1326:45 | p1 | |
|
||||
| atl.cpp:1319:34:1319:44 | call to ComPtr | atl.cpp:1328:1:1328:1 | p1 | |
|
||||
| atl.cpp:1319:42:1319:42 | x | atl.cpp:1319:34:1319:43 | new | |
|
||||
| atl.cpp:1320:9:1320:10 | ref arg p1 | atl.cpp:1321:9:1321:10 | p1 | |
|
||||
| atl.cpp:1320:9:1320:10 | ref arg p1 | atl.cpp:1323:34:1323:35 | p1 | |
|
||||
| atl.cpp:1320:9:1320:10 | ref arg p1 | atl.cpp:1326:44:1326:45 | p1 | |
|
||||
| atl.cpp:1320:9:1320:10 | ref arg p1 | atl.cpp:1328:1:1328:1 | p1 | |
|
||||
| atl.cpp:1320:12:1320:14 | call to Get | atl.cpp:1320:8:1320:16 | * ... | TAINT |
|
||||
| atl.cpp:1321:9:1321:10 | ref arg p1 | atl.cpp:1323:34:1323:35 | p1 | |
|
||||
| atl.cpp:1321:9:1321:10 | ref arg p1 | atl.cpp:1326:44:1326:45 | p1 | |
|
||||
| atl.cpp:1321:9:1321:10 | ref arg p1 | atl.cpp:1328:1:1328:1 | p1 | |
|
||||
| atl.cpp:1321:12:1321:17 | call to Detach | atl.cpp:1321:8:1321:19 | * ... | TAINT |
|
||||
| atl.cpp:1323:34:1323:35 | p1 | atl.cpp:1323:34:1323:36 | call to ComPtr | |
|
||||
| atl.cpp:1323:34:1323:36 | call to ComPtr | atl.cpp:1324:9:1324:10 | p2 | |
|
||||
| atl.cpp:1323:34:1323:36 | call to ComPtr | atl.cpp:1328:1:1328:1 | p2 | |
|
||||
| atl.cpp:1324:9:1324:10 | ref arg p2 | atl.cpp:1328:1:1328:1 | p2 | |
|
||||
| atl.cpp:1324:12:1324:14 | call to Get | atl.cpp:1324:8:1324:16 | * ... | TAINT |
|
||||
| atl.cpp:1326:34:1326:42 | call to move | atl.cpp:1326:34:1326:47 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1326:34:1326:42 | ref arg call to move | atl.cpp:1326:44:1326:45 | p1 [inner post update] | |
|
||||
| atl.cpp:1326:34:1326:42 | ref arg call to move | atl.cpp:1328:1:1328:1 | p1 | |
|
||||
| atl.cpp:1326:34:1326:47 | call to ComPtr | atl.cpp:1327:9:1327:10 | p3 | |
|
||||
| atl.cpp:1326:34:1326:47 | call to ComPtr | atl.cpp:1328:1:1328:1 | p3 | |
|
||||
| atl.cpp:1326:44:1326:45 | p1 | atl.cpp:1326:34:1326:42 | call to move | TAINT |
|
||||
| atl.cpp:1326:44:1326:45 | p1 | atl.cpp:1326:34:1326:47 | call to ComPtr | |
|
||||
| atl.cpp:1327:9:1327:10 | ref arg p3 | atl.cpp:1328:1:1328:1 | p3 | |
|
||||
| atl.cpp:1327:12:1327:14 | call to Get | atl.cpp:1327:8:1327:16 | * ... | TAINT |
|
||||
| atl.cpp:1332:11:1332:21 | call to source | atl.cpp:1333:42:1333:42 | x | |
|
||||
| atl.cpp:1333:34:1333:43 | new | atl.cpp:1333:34:1333:44 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1333:34:1333:44 | call to ComPtr | atl.cpp:1335:3:1335:4 | p1 | |
|
||||
| atl.cpp:1333:34:1333:44 | call to ComPtr | atl.cpp:1337:1:1337:1 | p1 | |
|
||||
| atl.cpp:1333:42:1333:42 | x | atl.cpp:1333:34:1333:43 | new | |
|
||||
| atl.cpp:1334:31:1334:32 | call to ComPtr | atl.cpp:1335:10:1335:11 | p2 | |
|
||||
| atl.cpp:1334:31:1334:32 | call to ComPtr | atl.cpp:1336:9:1336:10 | p2 | |
|
||||
| atl.cpp:1334:31:1334:32 | call to ComPtr | atl.cpp:1337:1:1337:1 | p2 | |
|
||||
| atl.cpp:1335:9:1335:11 | ref arg & ... | atl.cpp:1335:10:1335:11 | p2 [inner post update] | |
|
||||
| atl.cpp:1335:9:1335:11 | ref arg & ... | atl.cpp:1336:9:1336:10 | p2 | |
|
||||
| atl.cpp:1335:9:1335:11 | ref arg & ... | atl.cpp:1337:1:1337:1 | p2 | |
|
||||
| atl.cpp:1335:10:1335:11 | p2 | atl.cpp:1335:9:1335:11 | & ... | |
|
||||
| atl.cpp:1336:9:1336:10 | ref arg p2 | atl.cpp:1337:1:1337:1 | p2 | |
|
||||
| atl.cpp:1336:12:1336:14 | call to Get | atl.cpp:1336:8:1336:16 | * ... | TAINT |
|
||||
| atl.cpp:1341:11:1341:21 | call to source | atl.cpp:1342:42:1342:42 | x | |
|
||||
| atl.cpp:1341:11:1341:21 | call to source | atl.cpp:1351:42:1351:42 | x | |
|
||||
| atl.cpp:1342:34:1342:43 | new | atl.cpp:1342:34:1342:44 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1342:34:1342:44 | call to ComPtr | atl.cpp:1344:3:1344:4 | p1 | |
|
||||
| atl.cpp:1342:34:1342:44 | call to ComPtr | atl.cpp:1348:3:1348:4 | p1 | |
|
||||
| atl.cpp:1342:34:1342:44 | call to ComPtr | atl.cpp:1356:1:1356:1 | p1 | |
|
||||
| atl.cpp:1342:42:1342:42 | x | atl.cpp:1342:34:1342:43 | new | |
|
||||
| atl.cpp:1343:14:1343:20 | 0 | atl.cpp:1344:14:1344:16 | raw | |
|
||||
| atl.cpp:1343:14:1343:20 | 0 | atl.cpp:1345:9:1345:11 | raw | |
|
||||
| atl.cpp:1343:14:1343:20 | 0 | atl.cpp:1348:31:1348:33 | raw | |
|
||||
| atl.cpp:1343:14:1343:20 | 0 | atl.cpp:1349:9:1349:11 | raw | |
|
||||
| atl.cpp:1344:3:1344:4 | ref arg p1 | atl.cpp:1348:3:1348:4 | p1 | |
|
||||
| atl.cpp:1344:3:1344:4 | ref arg p1 | atl.cpp:1356:1:1356:1 | p1 | |
|
||||
| atl.cpp:1344:13:1344:16 | ref arg & ... | atl.cpp:1344:14:1344:16 | raw [inner post update] | |
|
||||
| atl.cpp:1344:13:1344:16 | ref arg & ... | atl.cpp:1345:9:1345:11 | raw | |
|
||||
| atl.cpp:1344:13:1344:16 | ref arg & ... | atl.cpp:1348:31:1348:33 | raw | |
|
||||
| atl.cpp:1344:13:1344:16 | ref arg & ... | atl.cpp:1349:9:1349:11 | raw | |
|
||||
| atl.cpp:1344:14:1344:16 | raw | atl.cpp:1344:13:1344:16 | & ... | |
|
||||
| atl.cpp:1345:9:1345:11 | raw | atl.cpp:1345:8:1345:11 | * ... | TAINT |
|
||||
| atl.cpp:1347:31:1347:32 | call to ComPtr | atl.cpp:1356:1:1356:1 | p2 | |
|
||||
| atl.cpp:1348:30:1348:33 | ref arg & ... | atl.cpp:1348:31:1348:33 | raw [inner post update] | |
|
||||
| atl.cpp:1348:30:1348:33 | ref arg & ... | atl.cpp:1349:9:1349:11 | raw | |
|
||||
| atl.cpp:1348:31:1348:33 | raw | atl.cpp:1348:30:1348:33 | & ... | |
|
||||
| atl.cpp:1349:9:1349:11 | raw | atl.cpp:1349:8:1349:11 | * ... | TAINT |
|
||||
| atl.cpp:1351:34:1351:43 | new | atl.cpp:1351:34:1351:44 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1351:34:1351:44 | call to ComPtr | atl.cpp:1354:3:1354:4 | p3 | |
|
||||
| atl.cpp:1351:34:1351:44 | call to ComPtr | atl.cpp:1356:1:1356:1 | p3 | |
|
||||
| atl.cpp:1351:42:1351:42 | x | atl.cpp:1351:34:1351:43 | new | |
|
||||
| atl.cpp:1353:15:1353:21 | 0 | atl.cpp:1354:19:1354:22 | raw2 | |
|
||||
| atl.cpp:1353:15:1353:21 | 0 | atl.cpp:1355:9:1355:12 | raw2 | |
|
||||
| atl.cpp:1354:18:1354:22 | ref arg & ... | atl.cpp:1354:19:1354:22 | raw2 [inner post update] | |
|
||||
| atl.cpp:1354:18:1354:22 | ref arg & ... | atl.cpp:1355:9:1355:12 | raw2 | |
|
||||
| atl.cpp:1354:19:1354:22 | raw2 | atl.cpp:1354:18:1354:22 | & ... | |
|
||||
| atl.cpp:1355:9:1355:12 | raw2 | atl.cpp:1355:8:1355:12 | * ... | TAINT |
|
||||
| atl.cpp:1360:11:1360:21 | call to source | atl.cpp:1361:42:1361:42 | x | |
|
||||
| atl.cpp:1361:34:1361:43 | new | atl.cpp:1361:34:1361:44 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1361:34:1361:44 | call to ComPtr | atl.cpp:1363:3:1363:4 | p1 | |
|
||||
| atl.cpp:1361:34:1361:44 | call to ComPtr | atl.cpp:1365:9:1365:10 | p1 | |
|
||||
| atl.cpp:1361:34:1361:44 | call to ComPtr | atl.cpp:1366:1:1366:1 | p1 | |
|
||||
| atl.cpp:1361:42:1361:42 | x | atl.cpp:1361:34:1361:43 | new | |
|
||||
| atl.cpp:1362:31:1362:32 | call to ComPtr | atl.cpp:1363:11:1363:12 | p2 | |
|
||||
| atl.cpp:1362:31:1362:32 | call to ComPtr | atl.cpp:1364:9:1364:10 | p2 | |
|
||||
| atl.cpp:1362:31:1362:32 | call to ComPtr | atl.cpp:1366:1:1366:1 | p2 | |
|
||||
| atl.cpp:1363:3:1363:4 | ref arg p1 | atl.cpp:1365:9:1365:10 | p1 | |
|
||||
| atl.cpp:1363:3:1363:4 | ref arg p1 | atl.cpp:1366:1:1366:1 | p1 | |
|
||||
| atl.cpp:1363:11:1363:12 | ref arg p2 | atl.cpp:1364:9:1364:10 | p2 | |
|
||||
| atl.cpp:1363:11:1363:12 | ref arg p2 | atl.cpp:1366:1:1366:1 | p2 | |
|
||||
| atl.cpp:1364:9:1364:10 | ref arg p2 | atl.cpp:1366:1:1366:1 | p2 | |
|
||||
| atl.cpp:1364:12:1364:14 | call to Get | atl.cpp:1364:8:1364:16 | * ... | TAINT |
|
||||
| atl.cpp:1365:9:1365:10 | ref arg p1 | atl.cpp:1366:1:1366:1 | p1 | |
|
||||
| atl.cpp:1365:12:1365:14 | call to Get | atl.cpp:1365:8:1365:16 | * ... | TAINT |
|
||||
| atl.cpp:1370:11:1370:21 | call to source | atl.cpp:1371:42:1371:42 | x | |
|
||||
| atl.cpp:1370:11:1370:21 | call to source | atl.cpp:1374:48:1374:48 | x | |
|
||||
| atl.cpp:1370:11:1370:21 | call to source | atl.cpp:1377:42:1377:42 | x | |
|
||||
| atl.cpp:1371:34:1371:43 | new | atl.cpp:1371:34:1371:44 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1371:34:1371:44 | call to ComPtr | atl.cpp:1372:10:1372:11 | p1 | |
|
||||
| atl.cpp:1371:34:1371:44 | call to ComPtr | atl.cpp:1380:1:1380:1 | p1 | |
|
||||
| atl.cpp:1371:42:1371:42 | x | atl.cpp:1371:34:1371:43 | new | |
|
||||
| atl.cpp:1372:9:1372:26 | * ... | atl.cpp:1372:8:1372:26 | * ... | TAINT |
|
||||
| atl.cpp:1372:10:1372:11 | ref arg p1 | atl.cpp:1380:1:1380:1 | p1 | |
|
||||
| atl.cpp:1372:13:1372:24 | call to GetAddressOf | atl.cpp:1372:9:1372:26 | * ... | TAINT |
|
||||
| atl.cpp:1374:40:1374:49 | new | atl.cpp:1374:40:1374:50 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1374:40:1374:50 | call to ComPtr | atl.cpp:1375:10:1375:11 | p2 | |
|
||||
| atl.cpp:1374:40:1374:50 | call to ComPtr | atl.cpp:1380:1:1380:1 | p2 | |
|
||||
| atl.cpp:1374:48:1374:48 | x | atl.cpp:1374:40:1374:49 | new | |
|
||||
| atl.cpp:1375:9:1375:26 | * ... | atl.cpp:1375:8:1375:26 | * ... | TAINT |
|
||||
| atl.cpp:1375:10:1375:11 | ref arg p2 | atl.cpp:1380:1:1380:1 | p2 | |
|
||||
| atl.cpp:1375:13:1375:24 | call to GetAddressOf | atl.cpp:1375:9:1375:26 | * ... | TAINT |
|
||||
| atl.cpp:1377:34:1377:43 | new | atl.cpp:1377:34:1377:44 | call to ComPtr | TAINT |
|
||||
| atl.cpp:1377:34:1377:44 | call to ComPtr | atl.cpp:1378:14:1378:15 | p3 | |
|
||||
| atl.cpp:1377:34:1377:44 | call to ComPtr | atl.cpp:1380:1:1380:1 | p3 | |
|
||||
| atl.cpp:1377:42:1377:42 | x | atl.cpp:1377:34:1377:43 | new | |
|
||||
| atl.cpp:1378:14:1378:15 | ref arg p3 | atl.cpp:1380:1:1380:1 | p3 | |
|
||||
| atl.cpp:1378:17:1378:38 | call to ReleaseAndGetAddressOf | atl.cpp:1379:10:1379:11 | pp | |
|
||||
| atl.cpp:1379:9:1379:11 | * ... | atl.cpp:1379:8:1379:11 | * ... | TAINT |
|
||||
| atl.cpp:1379:10:1379:11 | pp | atl.cpp:1379:9:1379:11 | * ... | TAINT |
|
||||
| bsd.cpp:17:11:17:16 | call to source | bsd.cpp:20:18:20:18 | s | |
|
||||
| bsd.cpp:18:12:18:15 | addr | bsd.cpp:20:22:20:25 | addr | |
|
||||
| bsd.cpp:18:12:18:15 | addr | bsd.cpp:23:8:23:11 | addr | |
|
||||
|
||||
@@ -5567,6 +5567,15 @@ signatureMatches
|
||||
| atl.cpp:1231:5:1231:12 | CStrBufT | (const char *,const char *,unsigned long) | | __ngettext | 2 |
|
||||
| atl.cpp:1231:5:1231:12 | CStrBufT | (unsigned char *,int,unsigned long) | | UTF8_putc | 1 |
|
||||
| atl.cpp:1231:5:1231:12 | CStrBufT | (unsigned char *,int,unsigned long) | | UTF8_putc | 2 |
|
||||
| atl.cpp:1268:5:1268:10 | ComPtr | (const ComPtr &) | ComPtr | ComPtr | 0 |
|
||||
| atl.cpp:1269:5:1269:10 | ComPtr | (ComPtr &&) | ComPtr | ComPtr | 0 |
|
||||
| atl.cpp:1272:5:1272:10 | ComPtr | (T *) | ComPtr | ComPtr<T> | 0 |
|
||||
| atl.cpp:1283:13:1283:18 | CopyTo | (T **) | ComPtr<T> | CopyTo | 0 |
|
||||
| atl.cpp:1285:13:1285:18 | CopyTo | (Curl_easy *,void **) | | Curl_resolver_init | 1 |
|
||||
| atl.cpp:1285:13:1285:18 | CopyTo | (REFIID,void **) | ComPtr | CopyTo | 0 |
|
||||
| atl.cpp:1285:13:1285:18 | CopyTo | (REFIID,void **) | ComPtr | CopyTo | 1 |
|
||||
| atl.cpp:1285:13:1285:18 | CopyTo | (size_t,void **) | | __libc_alloc_buffer_allocate | 1 |
|
||||
| atl.cpp:1288:13:1288:18 | CopyTo | (T **) | ComPtr | CopyTo<T> | 0 |
|
||||
| bsd.cpp:12:5:12:10 | accept | (CURLM *,curl_socket_t,int *) | | curl_multi_socket | 2 |
|
||||
| bsd.cpp:12:5:12:10 | accept | (Curl_easy *,ssize_t *,int *) | | Curl_GetFTPResponse | 2 |
|
||||
| bsd.cpp:12:5:12:10 | accept | (EVP_CIPHER_CTX *,unsigned char *,int *) | | EVP_CipherFinal | 2 |
|
||||
@@ -21750,6 +21759,7 @@ getSignatureParameterName
|
||||
| (CURLU *,const char *,char **,OperationConfig *) | | ipfs_url_rewrite | 1 | const char * |
|
||||
| (CURLU *,const char *,char **,OperationConfig *) | | ipfs_url_rewrite | 2 | char ** |
|
||||
| (CURLU *,const char *,char **,OperationConfig *) | | ipfs_url_rewrite | 3 | OperationConfig * |
|
||||
| (ComPtr &&) | ComPtr | ComPtr | 0 | ComPtr && |
|
||||
| (CompoundDictionary *,const PreparedDictionary *) | | AttachPreparedDictionary | 0 | CompoundDictionary * |
|
||||
| (CompoundDictionary *,const PreparedDictionary *) | | AttachPreparedDictionary | 1 | const PreparedDictionary * |
|
||||
| (Curl_cfilter *) | | Curl_conn_cf_is_ssl | 0 | Curl_cfilter * |
|
||||
@@ -28580,6 +28590,8 @@ getSignatureParameterName
|
||||
| (RAND_POOL *,unsigned int) | | ossl_rand_pool_bytes_needed | 1 | unsigned int |
|
||||
| (RECORD_LAYER *,SSL_CONNECTION *) | | RECORD_LAYER_init | 0 | RECORD_LAYER * |
|
||||
| (RECORD_LAYER *,SSL_CONNECTION *) | | RECORD_LAYER_init | 1 | SSL_CONNECTION * |
|
||||
| (REFIID,void **) | ComPtr | CopyTo | 0 | REFIID |
|
||||
| (REFIID,void **) | ComPtr | CopyTo | 1 | void ** |
|
||||
| (RIO_NOTIFIER *) | | ossl_rio_notifier_cleanup | 0 | RIO_NOTIFIER * |
|
||||
| (RIPEMD160_CTX *,const unsigned char *) | | RIPEMD160_Transform | 0 | RIPEMD160_CTX * |
|
||||
| (RIPEMD160_CTX *,const unsigned char *) | | RIPEMD160_Transform | 1 | const unsigned char * |
|
||||
@@ -30163,6 +30175,9 @@ getSignatureParameterName
|
||||
| (Strtab *,const char *,size_t) | | strtabadd | 2 | size_t |
|
||||
| (Strtab *,size_t *) | | strtabfinalize | 0 | Strtab * |
|
||||
| (Strtab *,size_t *) | | strtabfinalize | 1 | size_t * |
|
||||
| (T *) | ComPtr | ComPtr<T> | 0 | func:0 * |
|
||||
| (T **) | ComPtr | CopyTo<T> | 0 | func:0 ** |
|
||||
| (T **) | ComPtr<T> | CopyTo | 0 | class:0 ** |
|
||||
| (TLS_FEATURE *) | | TLS_FEATURE_free | 0 | TLS_FEATURE * |
|
||||
| (TLS_RL_RECORD *,const unsigned char *) | | ossl_tls_rl_record_set_seq_num | 0 | TLS_RL_RECORD * |
|
||||
| (TLS_RL_RECORD *,const unsigned char *) | | ossl_tls_rl_record_set_seq_num | 1 | const unsigned char * |
|
||||
@@ -33305,6 +33320,7 @@ getSignatureParameterName
|
||||
| (const CURLU *,CURLUPart,char **,unsigned int) | | curl_url_get | 1 | CURLUPart |
|
||||
| (const CURLU *,CURLUPart,char **,unsigned int) | | curl_url_get | 2 | char ** |
|
||||
| (const CURLU *,CURLUPart,char **,unsigned int) | | curl_url_get | 3 | unsigned int |
|
||||
| (const ComPtr &) | ComPtr | ComPtr | 0 | const ComPtr & |
|
||||
| (const Command *,const size_t,const BlockSplit *,const BlockSplit *,const BlockSplit *,const uint8_t *,size_t,size_t,uint8_t,uint8_t,const ContextType *,HistogramLiteral *,HistogramCommand *,HistogramDistance *) | | BrotliBuildHistogramsWithContext | 0 | const Command * |
|
||||
| (const Command *,const size_t,const BlockSplit *,const BlockSplit *,const BlockSplit *,const uint8_t *,size_t,size_t,uint8_t,uint8_t,const ContextType *,HistogramLiteral *,HistogramCommand *,HistogramDistance *) | | BrotliBuildHistogramsWithContext | 1 | const size_t |
|
||||
| (const Command *,const size_t,const BlockSplit *,const BlockSplit *,const BlockSplit *,const uint8_t *,size_t,size_t,uint8_t,uint8_t,const ContextType *,HistogramLiteral *,HistogramCommand *,HistogramDistance *) | | BrotliBuildHistogramsWithContext | 2 | const BlockSplit * |
|
||||
@@ -46404,6 +46420,18 @@ getParameterTypeName
|
||||
| atl.cpp:1231:5:1231:12 | CStrBufT | 1 | int |
|
||||
| atl.cpp:1231:5:1231:12 | CStrBufT | 2 | DWORD |
|
||||
| atl.cpp:1231:5:1231:12 | CStrBufT | 2 | unsigned long |
|
||||
| atl.cpp:1268:5:1268:10 | ComPtr | 0 | const ComPtr & |
|
||||
| atl.cpp:1269:5:1269:10 | ComPtr | 0 | ComPtr && |
|
||||
| atl.cpp:1272:5:1272:10 | ComPtr | 0 | func:0 * |
|
||||
| atl.cpp:1277:13:1277:14 | As | 0 | ComPtr * |
|
||||
| atl.cpp:1283:13:1283:18 | CopyTo | 0 | Interfaceclass:0ype ** |
|
||||
| atl.cpp:1283:13:1283:18 | CopyTo | 0 | class:0 ** |
|
||||
| atl.cpp:1285:13:1285:18 | CopyTo | 0 | GUID * |
|
||||
| atl.cpp:1285:13:1285:18 | CopyTo | 0 | REFIID |
|
||||
| atl.cpp:1285:13:1285:18 | CopyTo | 1 | void ** |
|
||||
| atl.cpp:1288:13:1288:18 | CopyTo | 0 | func:0 ** |
|
||||
| atl.cpp:1303:10:1303:13 | Swap | 0 | ComPtr & |
|
||||
| atl.cpp:1310:25:1310:28 | move | 0 | func:0 & |
|
||||
| bsd.cpp:6:8:6:8 | operator= | 0 | const sockaddr & |
|
||||
| bsd.cpp:6:8:6:8 | operator= | 0 | sockaddr && |
|
||||
| bsd.cpp:12:5:12:10 | accept | 0 | int |
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
.. _codeql-cli-2.22.4:
|
||||
|
||||
==========================
|
||||
CodeQL 2.22.4 (2025-08-21)
|
||||
==========================
|
||||
|
||||
.. contents:: Contents
|
||||
:depth: 2
|
||||
:local:
|
||||
:backlinks: none
|
||||
|
||||
This is an overview of changes in the CodeQL CLI and relevant CodeQL query and library packs. For additional updates on changes to the CodeQL code scanning experience, check out the `code scanning section on the GitHub blog <https://github.blog/tag/code-scanning/>`__, `relevant GitHub Changelog updates <https://github.blog/changelog/label/code-scanning/>`__, `changes in the CodeQL extension for Visual Studio Code <https://marketplace.visualstudio.com/items/GitHub.vscode-codeql/changelog>`__, and the `CodeQL Action changelog <https://github.com/github/codeql-action/blob/main/CHANGELOG.md>`__.
|
||||
|
||||
Security Coverage
|
||||
-----------------
|
||||
|
||||
CodeQL 2.22.4 runs a total of 478 security queries when configured with the Default suite (covering 169 CWE). The Extended suite enables an additional 130 queries (covering 32 more CWE). 2 security queries have been added with this release.
|
||||
|
||||
CodeQL CLI
|
||||
----------
|
||||
|
||||
There are no user-facing CLI changes in this release.
|
||||
|
||||
Query Packs
|
||||
-----------
|
||||
|
||||
Minor Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C/C++
|
||||
"""""
|
||||
|
||||
* The :code:`cpp/short-global-name` query will no longer give alerts for instantiations of template variables, only for the template itself.
|
||||
* Fixed a false positive in :code:`cpp/overflow-buffer` when the type of the destination buffer is a reference to a class/struct type.
|
||||
|
||||
Golang
|
||||
""""""
|
||||
|
||||
* Go 1.25 is now supported.
|
||||
|
||||
JavaScript/TypeScript
|
||||
"""""""""""""""""""""
|
||||
|
||||
* The :code:`js/regex-injection` query no longer considers environment variables as sources by default. Environment variables can be re-enabled as sources by setting the threat model to include the "environment" category.
|
||||
|
||||
New Queries
|
||||
~~~~~~~~~~~
|
||||
|
||||
Rust
|
||||
""""
|
||||
|
||||
* Added a new query, :code:`rust/cleartext-storage-database`, for detecting cases where sensitive information is stored non-encrypted in a database.
|
||||
|
||||
Language Libraries
|
||||
------------------
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~
|
||||
|
||||
Ruby
|
||||
""""
|
||||
|
||||
* Made the following changes to :code:`NetHttpRequest`
|
||||
|
||||
* Adds :code:`connectionNode`, like other Ruby HTTP clients
|
||||
* Makes :code:`requestNode` and :code:`connectionNode` public so subclasses can use them
|
||||
* Adds detection of :code:`Net::HTTP.start`, a common way to make HTTP requests in Ruby
|
||||
|
||||
Major Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Java/Kotlin
|
||||
"""""""""""
|
||||
|
||||
* Added library models for the relevant method calls under :code:`jakarta.servlet.ServletRequest` and :code:`jakarta.servlet.http.HttpServletRequest` as remote flow sources.
|
||||
|
||||
Minor Analysis Improvements
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
C/C++
|
||||
"""""
|
||||
|
||||
* The guards libraries (:code:`semmle.code.cpp.controlflow.Guards` and :code:`semmle.code.cpp.controlflow.IRGuards`) have been improved to recognize more guards.
|
||||
* Improved dataflow through global variables in the new dataflow library (:code:`semmle.code.cpp.dataflow.new.DataFlow` and :code:`semmle.code.cpp.dataflow.new.TaintTracking`). Queries based on these libraries will produce more results on codebases with many global variables.
|
||||
* The global value numbering library (:code:`semmle.code.cpp.valuenumbering.GlobalValueNumbering` and :code:`semmle.code.cpp.ir.ValueNumbering`) has been improved so more expressions are assigned the same value number.
|
||||
|
||||
Java/Kotlin
|
||||
"""""""""""
|
||||
|
||||
* Guard implication logic involving wrapper methods has been improved. In particular, this means fewer false positives for :code:`java/dereferenced-value-may-be-null`.
|
||||
|
||||
JavaScript/TypeScript
|
||||
"""""""""""""""""""""
|
||||
|
||||
* Improved modeling of command-line argument parsing libraries `arg <https://www.npmjs.com/package/arg>`__, `args <https://www.npmjs.com/package/args>`__, `command-line-args <https://www.npmjs.com/package/command-line-args>`__ and `commander <https://www.npmjs.com/package/commander>`__
|
||||
|
||||
Rust
|
||||
""""
|
||||
|
||||
* |link-code-let-chains-in-code-if-and-code-while-1|_ are now supported, as well as |link-code-if-let-guards-in-code-match-expressions-2|_.
|
||||
* Added more detail to models of :code:`postgres`, :code:`rusqlite`, :code:`sqlx` and :code:`tokio-postgres`. This may improve query results, particularly for :code:`rust/sql-injection` and :code:`rust/cleartext-storage-database`.
|
||||
|
||||
.. |link-code-let-chains-in-code-if-and-code-while-1| replace:: :code:`let` chains in :code:`if` and :code:`while`\
|
||||
.. _link-code-let-chains-in-code-if-and-code-while-1: https://doc.rust-lang.org/edition-guide/rust-2024/let-chains.html
|
||||
|
||||
.. |link-code-if-let-guards-in-code-match-expressions-2| replace:: :code:`if let` guards in :code:`match` expressions
|
||||
.. _link-code-if-let-guards-in-code-match-expressions-2: https://rust-lang.github.io/rfcs/2294-if-let-guard.html
|
||||
|
||||
@@ -11,6 +11,7 @@ A list of queries for each suite and language `is available here <https://docs.g
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
codeql-cli-2.22.4
|
||||
codeql-cli-2.22.3
|
||||
codeql-cli-2.22.2
|
||||
codeql-cli-2.22.1
|
||||
|
||||
@@ -82,6 +82,7 @@ ql/java/ql/src/Violations of Best Practice/Records/IgnoredSerializationMembersOf
|
||||
ql/java/ql/src/Violations of Best Practice/SpecialCharactersInLiterals/NonExplicitControlAndWhitespaceCharsInLiterals.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToRunFinalizersOnExit.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToStringToString.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToSystemExit.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DefaultToString.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/DoNotCallFinalize.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/PrintLnArray.ql
|
||||
|
||||
@@ -27,6 +27,7 @@ ql/java/ql/src/Security/CWE/CWE-113/ResponseSplitting.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-1204/StaticInitializationVector.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-134/ExternallyControlledFormatString.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuatorsConfig/SpringBootActuatorsConfig.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/SensitiveDataExposureThroughErrorMessage.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-266/IntentUriPermissionManipulation.ql
|
||||
|
||||
@@ -143,6 +143,7 @@ ql/java/ql/src/Security/CWE/CWE-200/AndroidSensitiveTextField.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuatorsConfig/SpringBootActuatorsConfig.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/TempDirLocalInformationDisclosure.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/SensitiveDataExposureThroughErrorMessage.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
|
||||
|
||||
@@ -46,6 +46,7 @@ ql/java/ql/src/Security/CWE/CWE-200/AndroidSensitiveTextField.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsAllowsContentAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/AndroidWebViewSettingsFileAccess.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuators.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/SpringBootActuatorsConfig/SpringBootActuatorsConfig.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-200/TempDirLocalInformationDisclosure.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/SensitiveDataExposureThroughErrorMessage.ql
|
||||
ql/java/ql/src/Security/CWE/CWE-209/StackTraceExposure.ql
|
||||
|
||||
@@ -66,7 +66,6 @@ ql/java/ql/src/Frameworks/Spring/XML Configuration Errors/MissingSetters.ql
|
||||
ql/java/ql/src/Language Abuse/CastThisToTypeParameter.ql
|
||||
ql/java/ql/src/Language Abuse/DubiousDowncastOfThis.ql
|
||||
ql/java/ql/src/Language Abuse/DubiousTypeTestOfThis.ql
|
||||
ql/java/ql/src/Language Abuse/EmptyStatement.ql
|
||||
ql/java/ql/src/Language Abuse/EnumIdentifier.ql
|
||||
ql/java/ql/src/Language Abuse/ImplementsAnnotation.ql
|
||||
ql/java/ql/src/Language Abuse/MissedTernaryOpportunity.ql
|
||||
@@ -187,7 +186,6 @@ ql/java/ql/src/Violations of Best Practice/Magic Constants/MagicNumbersUseConsta
|
||||
ql/java/ql/src/Violations of Best Practice/Magic Constants/MagicStringsUseConstant.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/ConfusingOverridesNames.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Naming Conventions/LocalShadowsField.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/CallsToSystemExit.ql
|
||||
ql/java/ql/src/Violations of Best Practice/Undesirable Calls/GarbageCollection.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/AutoBoxing.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/FinallyMayNotComplete.ql
|
||||
@@ -196,7 +194,6 @@ ql/java/ql/src/Violations of Best Practice/legacy/ParameterAssignment.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/UnnecessaryCast.ql
|
||||
ql/java/ql/src/Violations of Best Practice/legacy/UnnecessaryImport.ql
|
||||
ql/java/ql/src/definitions.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-020/Log4jJndiInjection.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-036/OpenStream.ql
|
||||
ql/java/ql/src/experimental/Security/CWE/CWE-073/FilePathInjection.ql
|
||||
|
||||
@@ -70,7 +70,12 @@ class ConfigValue extends @configValue, ConfigLocatable {
|
||||
override string toString() { result = this.getValue() }
|
||||
}
|
||||
|
||||
/** A `.properties` file. */
|
||||
class PropertiesFile extends File {
|
||||
PropertiesFile() { this.getExtension() = "properties" }
|
||||
}
|
||||
|
||||
/** A Java property is a name-value pair in a `.properties` file. */
|
||||
class JavaProperty extends ConfigPair {
|
||||
JavaProperty() { this.getFile().getExtension() = "properties" }
|
||||
JavaProperty() { this.getFile() instanceof PropertiesFile }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/** Provides classes and predicates to reason about Spring Boot actuators exposed in configuration files. */
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
private import semmle.code.configfiles.ConfigFiles
|
||||
private import semmle.code.xml.MavenPom
|
||||
|
||||
/** The parent node of the `org.springframework.boot` group. */
|
||||
private class SpringBootParent extends Parent {
|
||||
SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" }
|
||||
}
|
||||
|
||||
/** A `Pom` with a Spring Boot parent node. */
|
||||
private class SpringBootPom extends Pom {
|
||||
SpringBootPom() { this.getParentElement() instanceof SpringBootParent }
|
||||
|
||||
/** Holds if the Spring Boot Security module is used in the project. */
|
||||
predicate isSpringBootSecurityUsed() {
|
||||
this.getADependency().getArtifact().getValue() = "spring-boot-starter-security"
|
||||
}
|
||||
}
|
||||
|
||||
/** A dependency with artifactId `spring-boot-starter-actuator`. */
|
||||
class SpringBootStarterActuatorDependency extends Dependency {
|
||||
SpringBootStarterActuatorDependency() {
|
||||
this.getArtifact().getValue() = "spring-boot-starter-actuator"
|
||||
}
|
||||
}
|
||||
|
||||
/** The Spring Boot configuration property `management.security.enabled`. */
|
||||
private class ManagementSecurityEnabledProperty extends JavaProperty {
|
||||
ManagementSecurityEnabledProperty() {
|
||||
this.getNameElement().getName() = "management.security.enabled"
|
||||
}
|
||||
|
||||
/** Gets the whitespace-trimmed value of this property. */
|
||||
string getValue() { result = this.getValueElement().getValue().trim() }
|
||||
|
||||
/** Holds if `management.security.enabled` is set to `false`. */
|
||||
predicate hasSecurityDisabled() { this.getValue() = "false" }
|
||||
}
|
||||
|
||||
/**
|
||||
* The Spring Boot configuration property `management.endpoints.web.exposure.include`
|
||||
* or `management.endpoints.web.expose`.
|
||||
*/
|
||||
private class ManagementEndpointsExposeProperty extends JavaProperty {
|
||||
ManagementEndpointsExposeProperty() {
|
||||
this.getNameElement().getName() = "management.endpoints.web." + ["exposure.include", "expose"]
|
||||
}
|
||||
|
||||
/** Gets the whitespace-trimmed value of this property. */
|
||||
string getValue() { result = this.getValueElement().getValue().trim() }
|
||||
}
|
||||
|
||||
private newtype TOption =
|
||||
TNone() or
|
||||
TSome(JavaProperty jp)
|
||||
|
||||
/**
|
||||
* An option type that is either a singleton `None` or a `Some` wrapping
|
||||
* the `JavaProperty` type.
|
||||
*/
|
||||
class JavaPropertyOption extends TOption {
|
||||
/** Gets a textual representation of this element. */
|
||||
string toString() {
|
||||
this = TNone() and result = "(none)"
|
||||
or
|
||||
result = this.asSome().toString()
|
||||
}
|
||||
|
||||
/** Gets the location of this element. */
|
||||
Location getLocation() { result = this.asSome().getLocation() }
|
||||
|
||||
/** Gets the wrapped element, if any. */
|
||||
JavaProperty asSome() { this = TSome(result) }
|
||||
|
||||
/** Holds if this option is the singleton `None`. */
|
||||
predicate isNone() { this = TNone() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `JavaPropertyOption` jpOption of a repository using `SpringBootStarterActuatorDependency`
|
||||
* d exposes sensitive Spring Boot Actuator endpoints.
|
||||
*/
|
||||
predicate exposesSensitiveEndpoint(
|
||||
SpringBootStarterActuatorDependency d, JavaPropertyOption jpOption
|
||||
) {
|
||||
exists(PropertiesFile propFile, SpringBootPom pom |
|
||||
d = pom.getADependency() and
|
||||
not pom.isSpringBootSecurityUsed() and
|
||||
propFile
|
||||
.getParentContainer()
|
||||
.getAbsolutePath()
|
||||
.matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory
|
||||
exists(string springBootVersion |
|
||||
springBootVersion = pom.getParentElement().getVersionString()
|
||||
|
|
||||
springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4
|
||||
not exists(ManagementSecurityEnabledProperty ep | ep.getFile() = propFile) and
|
||||
jpOption.isNone()
|
||||
or
|
||||
springBootVersion.regexpMatch("1\\.[0-5].*") and // version 1.0, 1.1, ..., 1.5
|
||||
exists(ManagementSecurityEnabledProperty ep |
|
||||
ep.hasSecurityDisabled() and ep.getFile() = propFile and ep = jpOption.asSome()
|
||||
)
|
||||
or
|
||||
springBootVersion.matches(["2.%", "3.%"]) and //version 2.x and 3.x
|
||||
exists(ManagementEndpointsExposeProperty ep |
|
||||
ep.getFile() = propFile and
|
||||
ep = jpOption.asSome() and
|
||||
(
|
||||
// all endpoints are exposed
|
||||
ep.getValue() = "*"
|
||||
or
|
||||
// version 2.x: exposes health and info only by default
|
||||
springBootVersion.matches("2.%") and
|
||||
not ep.getValue() = ["health", "info"]
|
||||
or
|
||||
// version 3.x: exposes health only by default
|
||||
springBootVersion.matches("3.%") and
|
||||
not ep.getValue() = "health"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Spring Boot includes features called actuators that let you monitor and interact with your web
|
||||
application. Exposing unprotected actuator endpoints through configuration files can lead to
|
||||
information disclosure or even to remote code execution.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Since actuator endpoints may contain sensitive information, carefully consider when to expose them,
|
||||
and secure them as you would any sensitive URL. If you need to expose actuator endpoints, use Spring
|
||||
Security, which secures actuators by default, or define a custom security configuration.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following examples show <code>application.properties</code> configurations that expose sensitive
|
||||
actuator endpoints.</p>
|
||||
<sample src="application_bad.properties" />
|
||||
|
||||
<p>The below configurations ensure that sensitive actuator endpoints are not exposed.</p>
|
||||
<sample src="application_good.properties" />
|
||||
|
||||
<p>To use Spring Security, which secures actuators by default, add the <code>spring-boot-starter-security</code>
|
||||
dependency in your Maven <code>pom.xml</code> file.</p>
|
||||
<sample src="pom_good.xml" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Spring Boot Reference Documentation:
|
||||
<a href="https://docs.spring.io/spring-boot/reference/actuator/endpoints.html">Endpoints</a>.
|
||||
</li>
|
||||
<li>
|
||||
HackerOne Report:
|
||||
<a href="https://hackerone.com/reports/862589">Spring Actuator endpoints publicly available, leading to account takeover</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @name Exposed Spring Boot actuators in configuration file
|
||||
* @description Exposing Spring Boot actuators through configuration files may lead to information leak from
|
||||
* the internal application, or even to remote code execution.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 6.5
|
||||
* @precision high
|
||||
* @id java/spring-boot-exposed-actuators-config
|
||||
* @tags security
|
||||
* external/cwe/cwe-200
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.xml.MavenPom
|
||||
import semmle.code.java.security.SpringBootActuatorsConfigQuery
|
||||
|
||||
from SpringBootStarterActuatorDependency d, JavaPropertyOption jpOption
|
||||
where exposesSensitiveEndpoint(d, jpOption)
|
||||
select d, "Insecure Spring Boot actuator $@ exposes sensitive endpoints.", jpOption, "configuration"
|
||||
@@ -0,0 +1,10 @@
|
||||
# vulnerable configuration (Spring Boot 1.0 - 1.4): exposes endpoints by default
|
||||
|
||||
# vulnerable configuration (Spring Boot 1.5): false value exposes endpoints
|
||||
management.security.enabled=false
|
||||
|
||||
# vulnerable configuration (Spring Boot 2.x): exposes all endpoints
|
||||
management.endpoints.web.exposure.include=*
|
||||
|
||||
# vulnerable configuration (Spring Boot 3.x): exposes all endpoints
|
||||
management.endpoints.web.exposure.include=*
|
||||
@@ -0,0 +1,11 @@
|
||||
# safe configuration (Spring Boot 1.0 - 1.4)
|
||||
management.security.enabled=true
|
||||
|
||||
# safe configuration (Spring Boot 1.5+)
|
||||
management.security.enabled=true
|
||||
|
||||
# safe configuration (Spring Boot 2.x): exposes health and info only by default
|
||||
management.endpoints.web.exposure.include=health,info
|
||||
|
||||
# safe configuration (Spring Boot 3.x): exposes health only by default
|
||||
management.endpoints.web.exposure.include=health
|
||||
@@ -0,0 +1,12 @@
|
||||
...
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- GOOD: Enable Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
...
|
||||
@@ -4,7 +4,7 @@ class FileOutput {
|
||||
try {
|
||||
output.write(s.getBytes());
|
||||
} catch (IOException e) {
|
||||
System.exit(1);
|
||||
System.exit(1); // BAD: Should handle or propagate error instead of exiting
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -16,9 +16,30 @@ class Action {
|
||||
// ...
|
||||
// Perform tasks ...
|
||||
// ...
|
||||
System.exit(0);
|
||||
System.exit(0); // BAD: Should return status or throw exception
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
new Action(args).run();
|
||||
new Action().run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Good example: Proper error handling
|
||||
class BetterAction {
|
||||
public int run() throws Exception {
|
||||
// ...
|
||||
// Perform tasks ...
|
||||
// ...
|
||||
return 0; // Return status instead of calling System.exit
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
BetterAction action = new BetterAction();
|
||||
int exitCode = action.run();
|
||||
System.exit(exitCode); // GOOD: Exit from main method
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
System.exit(1); // GOOD: Exit from main method on error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,17 +13,20 @@ program state from being written to disk consistently.</p>
|
||||
|
||||
<p>It is sometimes considered acceptable to call <code>System.exit</code>
|
||||
from a program's <code>main</code> method in order to indicate the overall exit status
|
||||
of the program. Such calls are an exception to this rule.</p>
|
||||
of the program. The <code>main</code> method should be the primary place
|
||||
where exit conditions are handled, as it represents the natural termination point
|
||||
of the application. Such calls are an exception to this rule.</p>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
|
||||
<p>It is usually preferable to use a different mechanism for reporting
|
||||
failure conditions. Consider returning a special value (perhaps
|
||||
<code>null</code>) that users of the current method check for and
|
||||
recover from appropriately. Alternatively, throw a suitable exception, which
|
||||
unwinds the stack and allows properly written code to clean up after itself,
|
||||
while leaving other threads undisturbed.</p>
|
||||
<p>Instead of calling <code>System.exit</code> from non-main methods, prefer to propagate
|
||||
errors upward to the <code>main</code> method where they can be handled appropriately.
|
||||
Consider returning a special value (perhaps <code>null</code>) that users of the current
|
||||
method check for and recover from appropriately. Alternatively, throw a suitable exception,
|
||||
which unwinds the stack and allows properly written code to clean up after itself,
|
||||
while leaving other threads undisturbed. The <code>main</code> method can then catch
|
||||
these exceptions and decide whether to exit the program and with what exit code.</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
@@ -38,12 +41,14 @@ upwards and be handled by a method that knows how to recover.</p>
|
||||
<p>Problem 2 is more subtle. In this example, there is just one entry point to
|
||||
the program (the <code>main</code> method), which constructs an
|
||||
<code>Action</code> and performs it. <code>Action.run</code> calls
|
||||
<code>System.exit</code> to indicate successful completion. Consider,
|
||||
however, how this code might be integrated in an application server that
|
||||
constructs <code>Action</code> instances and calls
|
||||
<code>System.exit</code> to indicate successful completion. Instead, the
|
||||
<code>run</code> method should return a status code or throw an exception
|
||||
on failure, allowing the <code>main</code> method to decide whether to exit
|
||||
and with what exit code. Consider how this code might be integrated in an
|
||||
application server that constructs <code>Action</code> instances and calls
|
||||
<code>run</code> on them without going through <code>main</code>.
|
||||
The fact that <code>run</code> terminates the JVM instead of returning its
|
||||
exit code as an integer makes that use-case impossible.</p>
|
||||
exit code makes that use-case impossible.</p>
|
||||
|
||||
<sample src="CallsToSystemExit.java" />
|
||||
|
||||
|
||||
@@ -4,26 +4,80 @@
|
||||
* reuse and prevent important cleanup steps from running.
|
||||
* @kind problem
|
||||
* @problem.severity warning
|
||||
* @precision low
|
||||
* @precision medium
|
||||
* @id java/jvm-exit
|
||||
* @tags reliability
|
||||
* maintainability
|
||||
* @previous-id java/jvm-exit-prevents-cleanup-and-reuse
|
||||
* @tags quality
|
||||
* reliability
|
||||
* correctness
|
||||
* external/cwe/cwe-382
|
||||
*/
|
||||
|
||||
import java
|
||||
|
||||
from Method m, MethodCall sysexitCall, Method sysexit, Class system
|
||||
where
|
||||
sysexitCall = m.getACallSite(sysexit) and
|
||||
(sysexit.hasName("exit") or sysexit.hasName("halt")) and
|
||||
sysexit.getDeclaringType() = system and
|
||||
(
|
||||
system.hasQualifiedName("java.lang", "System") or
|
||||
system.hasQualifiedName("java.lang", "Runtime")
|
||||
) and
|
||||
m.fromSource() and
|
||||
not m instanceof MainMethod
|
||||
select sysexitCall,
|
||||
"Avoid calls to " + sysexit.getDeclaringType().getName() + "." + sysexit.getName() +
|
||||
"() as this makes code harder to reuse."
|
||||
/**
|
||||
* A `Method` which, when called, causes the JVM to exit or halt.
|
||||
*
|
||||
* Explicitly includes these methods from the java standard library:
|
||||
* - `java.lang.System.exit`
|
||||
* - `java.lang.Runtime.halt`
|
||||
* - `java.lang.Runtime.exit`
|
||||
*/
|
||||
class ExitOrHaltMethod extends Method {
|
||||
ExitOrHaltMethod() {
|
||||
exists(Class system | this.getDeclaringType() = system |
|
||||
this.hasName("exit") and
|
||||
system.hasQualifiedName("java.lang", ["System", "Runtime"])
|
||||
or
|
||||
this.hasName("halt") and
|
||||
system.hasQualifiedName("java.lang", "Runtime")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A `MethodCall` to an `ExitOrHaltMethod`, which causes the JVM to exit abruptly. */
|
||||
class ExitOrHaltMethodCall extends MethodCall {
|
||||
ExitOrHaltMethodCall() {
|
||||
exists(ExitOrHaltMethod exitMethod | this.getMethod() = exitMethod |
|
||||
exists(SourceMethodNotMainOrTest srcMethod | this = srcMethod.getACallSite(exitMethod))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An intentional `MethodCall` to a system or runtime "exit" method, such as for
|
||||
* functions which exist for the purpose of exiting the program. Assumes that an exit method
|
||||
* call within a method is intentional if the exit code is passed from a parameter of the
|
||||
* enclosing method.
|
||||
*/
|
||||
class IntentionalExitMethodCall extends ExitOrHaltMethodCall {
|
||||
IntentionalExitMethodCall() {
|
||||
this.getMethod().hasName("exit") and
|
||||
this.getAnArgument() = this.getEnclosingCallable().getAParameter().getAnAccess()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A `Method` that is defined in source code and is not a `MainMethod` or a `LikelyTestMethod`.
|
||||
*/
|
||||
class SourceMethodNotMainOrTest extends Method {
|
||||
SourceMethodNotMainOrTest() {
|
||||
this.fromSource() and
|
||||
not this instanceof MainMethod and
|
||||
not (
|
||||
this.getEnclosingCallable*() instanceof LikelyTestMethod
|
||||
or
|
||||
this.getDeclaringType()
|
||||
.getEnclosingType*()
|
||||
.(LocalClassOrInterface)
|
||||
.getLocalTypeDeclStmt()
|
||||
.getEnclosingCallable() instanceof LikelyTestMethod
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
from ExitOrHaltMethodCall mc
|
||||
where not mc instanceof IntentionalExitMethodCall
|
||||
select mc,
|
||||
"Avoid calls to " + mc.getMethod().getDeclaringType().getName() + "." + mc.getMethod().getName() +
|
||||
"() as this prevents runtime cleanup and makes code harder to reuse."
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: newQuery
|
||||
---
|
||||
* The query `java/insecure-spring-actuator-config` has been promoted from experimental to the main query pack as `java/spring-boot-exposed-actuators-config`. Its results will now appear by default. This query detects exposure of Spring Boot actuators through configuration files. It was originally submitted as an experimental query [by @luchua-bc](https://github.com/github/codeql/pull/5384).
|
||||
@@ -1,47 +0,0 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<overview>
|
||||
<p>Spring Boot is a popular framework that facilitates the development of stand-alone applications
|
||||
and micro services. Spring Boot Actuator helps to expose production-ready support features against
|
||||
Spring Boot applications.</p>
|
||||
|
||||
<p>Endpoints of Spring Boot Actuator allow to monitor and interact with a Spring Boot application.
|
||||
Exposing unprotected actuator endpoints through configuration files can lead to information disclosure
|
||||
or even remote code execution vulnerability.</p>
|
||||
|
||||
<p>Rather than programmatically permitting endpoint requests or enforcing access control, frequently
|
||||
developers simply leave management endpoints publicly accessible in the application configuration file
|
||||
<code>application.properties</code> without enforcing access control through Spring Security.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Declare the Spring Boot Starter Security module in XML configuration or programmatically enforce
|
||||
security checks on management endpoints using Spring Security. Otherwise accessing management endpoints
|
||||
on a different HTTP port other than the port that the web application is listening on also helps to
|
||||
improve the security.</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following examples show both 'BAD' and 'GOOD' configurations. In the 'BAD' configuration,
|
||||
no security module is declared and sensitive management endpoints are exposed. In the 'GOOD' configuration,
|
||||
security is enforced and only endpoints requiring exposure are exposed.</p>
|
||||
<sample src="pom_good.xml" />
|
||||
<sample src="pom_bad.xml" />
|
||||
<sample src="application.properties" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>
|
||||
Spring Boot documentation:
|
||||
<a href="https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html">Spring Boot Actuator: Production-ready Features</a>
|
||||
</li>
|
||||
<li>
|
||||
VERACODE Blog:
|
||||
<a href="https://www.veracode.com/blog/research/exploiting-spring-boot-actuators">Exploiting Spring Boot Actuators</a>
|
||||
</li>
|
||||
<li>
|
||||
HackerOne Report:
|
||||
<a href="https://hackerone.com/reports/862589">Spring Actuator endpoints publicly available, leading to account takeover</a>
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -1,121 +0,0 @@
|
||||
/**
|
||||
* @name Insecure Spring Boot Actuator Configuration
|
||||
* @description Exposed Spring Boot Actuator through configuration files without declarative or procedural
|
||||
* security enforcement leads to information leak or even remote code execution.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/insecure-spring-actuator-config
|
||||
* @tags security
|
||||
* experimental
|
||||
* external/cwe/cwe-016
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note this query requires properties files to be indexed before it can produce results.
|
||||
* If creating your own database with the CodeQL CLI, you should run
|
||||
* `codeql database index-files --language=properties ...`
|
||||
* If using lgtm.com, you should add `properties_files: true` to the index block of your
|
||||
* lgtm.yml file (see https://lgtm.com/help/lgtm/java-extraction)
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.configfiles.ConfigFiles
|
||||
import semmle.code.xml.MavenPom
|
||||
|
||||
/** The parent node of the `org.springframework.boot` group. */
|
||||
class SpringBootParent extends Parent {
|
||||
SpringBootParent() { this.getGroup().getValue() = "org.springframework.boot" }
|
||||
}
|
||||
|
||||
/** Class of Spring Boot dependencies. */
|
||||
class SpringBootPom extends Pom {
|
||||
SpringBootPom() { this.getParentElement() instanceof SpringBootParent }
|
||||
|
||||
/** Holds if the Spring Boot Actuator module `spring-boot-starter-actuator` is used in the project. */
|
||||
predicate isSpringBootActuatorUsed() {
|
||||
this.getADependency().getArtifact().getValue() = "spring-boot-starter-actuator"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the Spring Boot Security module is used in the project, which brings in other security
|
||||
* related libraries.
|
||||
*/
|
||||
predicate isSpringBootSecurityUsed() {
|
||||
this.getADependency().getArtifact().getValue() = "spring-boot-starter-security"
|
||||
}
|
||||
}
|
||||
|
||||
/** The properties file `application.properties`. */
|
||||
class ApplicationProperties extends ConfigPair {
|
||||
ApplicationProperties() { this.getFile().getBaseName() = "application.properties" }
|
||||
}
|
||||
|
||||
/** The configuration property `management.security.enabled`. */
|
||||
class ManagementSecurityConfig extends ApplicationProperties {
|
||||
ManagementSecurityConfig() { this.getNameElement().getName() = "management.security.enabled" }
|
||||
|
||||
/** Gets the whitespace-trimmed value of this property. */
|
||||
string getValue() { result = this.getValueElement().getValue().trim() }
|
||||
|
||||
/** Holds if `management.security.enabled` is set to `false`. */
|
||||
predicate hasSecurityDisabled() { this.getValue() = "false" }
|
||||
|
||||
/** Holds if `management.security.enabled` is set to `true`. */
|
||||
predicate hasSecurityEnabled() { this.getValue() = "true" }
|
||||
}
|
||||
|
||||
/** The configuration property `management.endpoints.web.exposure.include`. */
|
||||
class ManagementEndPointInclude extends ApplicationProperties {
|
||||
ManagementEndPointInclude() {
|
||||
this.getNameElement().getName() = "management.endpoints.web.exposure.include"
|
||||
}
|
||||
|
||||
/** Gets the whitespace-trimmed value of this property. */
|
||||
string getValue() { result = this.getValueElement().getValue().trim() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ApplicationProperties` ap of a repository managed by `SpringBootPom` pom
|
||||
* has a vulnerable configuration of Spring Boot Actuator management endpoints.
|
||||
*/
|
||||
predicate hasConfidentialEndPointExposed(SpringBootPom pom, ApplicationProperties ap) {
|
||||
pom.isSpringBootActuatorUsed() and
|
||||
not pom.isSpringBootSecurityUsed() and
|
||||
ap.getFile()
|
||||
.getParentContainer()
|
||||
.getAbsolutePath()
|
||||
.matches(pom.getFile().getParentContainer().getAbsolutePath() + "%") and // in the same sub-directory
|
||||
exists(string springBootVersion | springBootVersion = pom.getParentElement().getVersionString() |
|
||||
springBootVersion.regexpMatch("1\\.[0-4].*") and // version 1.0, 1.1, ..., 1.4
|
||||
not exists(ManagementSecurityConfig me |
|
||||
me.hasSecurityEnabled() and me.getFile() = ap.getFile()
|
||||
)
|
||||
or
|
||||
springBootVersion.matches("1.5%") and // version 1.5
|
||||
exists(ManagementSecurityConfig me | me.hasSecurityDisabled() and me.getFile() = ap.getFile())
|
||||
or
|
||||
springBootVersion.matches("2.%") and //version 2.x
|
||||
exists(ManagementEndPointInclude mi |
|
||||
mi.getFile() = ap.getFile() and
|
||||
(
|
||||
mi.getValue() = "*" // all endpoints are enabled
|
||||
or
|
||||
mi.getValue()
|
||||
.matches([
|
||||
"%dump%", "%trace%", "%logfile%", "%shutdown%", "%startup%", "%mappings%", "%env%",
|
||||
"%beans%", "%sessions%"
|
||||
]) // confidential endpoints to check although all endpoints apart from '/health' and '/info' are considered sensitive by Spring
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
deprecated query predicate problems(Dependency d, string message) {
|
||||
exists(SpringBootPom pom |
|
||||
hasConfidentialEndPointExposed(pom, _) and
|
||||
d = pom.getADependency() and
|
||||
d.getArtifact().getValue() = "spring-boot-starter-actuator"
|
||||
) and
|
||||
message = "Insecure configuration of Spring Boot Actuator exposes sensitive endpoints."
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
#management.endpoints.web.base-path=/admin
|
||||
|
||||
|
||||
#### BAD: All management endpoints are accessible ####
|
||||
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
|
||||
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=false
|
||||
|
||||
# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything
|
||||
management.endpoints.web.exposure.include=*
|
||||
|
||||
|
||||
#### GOOD: All management endpoints have access control ####
|
||||
# safe configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
management.security.enabled=true
|
||||
|
||||
# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=true
|
||||
|
||||
# safe configuration (spring boot 2+): exposes health and info only by default, here overridden to expose one additional endpoint which we assume is intentional and safe.
|
||||
management.endpoints.web.exposure.include=beans,info,health
|
||||
@@ -1 +0,0 @@
|
||||
| pom.xml:29:9:32:22 | dependency | Insecure configuration of Spring Boot Actuator exposes sensitive endpoints. |
|
||||
@@ -1 +0,0 @@
|
||||
experimental/Security/CWE/CWE-016/InsecureSpringActuatorConfig.ql
|
||||
@@ -1,13 +0,0 @@
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@Controller
|
||||
public class SensitiveInfo {
|
||||
@RequestMapping
|
||||
public void handleLogin(@RequestParam String username, @RequestParam String password) throws Exception {
|
||||
if (!username.equals("") && password.equals("")) {
|
||||
//Blank processing
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
#management.endpoints.web.base-path=/admin
|
||||
|
||||
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
|
||||
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=false
|
||||
|
||||
# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything
|
||||
management.endpoints.web.exposure.include=*
|
||||
management.endpoints.web.exposure.exclude=beans
|
||||
|
||||
management.endpoint.shutdown.enabled=true
|
||||
|
||||
management.endpoint.health.show-details=when_authorized
|
||||
@@ -0,0 +1,6 @@
|
||||
| ExampleRuntimeExit.java:22:17:22:44 | exit(...) | Avoid calls to Runtime.exit() as this prevents runtime cleanup and makes code harder to reuse. |
|
||||
| ExampleRuntimeExit.java:25:17:25:44 | exit(...) | Avoid calls to Runtime.exit() as this prevents runtime cleanup and makes code harder to reuse. |
|
||||
| ExampleRuntimeHalt.java:18:17:18:44 | halt(...) | Avoid calls to Runtime.halt() as this prevents runtime cleanup and makes code harder to reuse. |
|
||||
| ExampleRuntimeHalt.java:21:17:21:44 | halt(...) | Avoid calls to Runtime.halt() as this prevents runtime cleanup and makes code harder to reuse. |
|
||||
| ExampleSystemExit.java:22:17:22:30 | exit(...) | Avoid calls to System.exit() as this prevents runtime cleanup and makes code harder to reuse. |
|
||||
| ExampleSystemExit.java:25:17:25:30 | exit(...) | Avoid calls to System.exit() as this prevents runtime cleanup and makes code harder to reuse. |
|
||||
@@ -0,0 +1,2 @@
|
||||
query: Violations of Best Practice/Undesirable Calls/CallsToSystemExit.ql
|
||||
postprocess: utils/test/InlineExpectationsTestQuery.ql
|
||||
@@ -0,0 +1,37 @@
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExampleRuntimeExit {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Action action = new Action();
|
||||
try {
|
||||
action.run();
|
||||
} catch (Exception e) {
|
||||
printUsageAndExit(e.getMessage(), 1);
|
||||
}
|
||||
Runtime.getRuntime().exit(0); // COMPLIANT
|
||||
}
|
||||
|
||||
static class Action {
|
||||
public void run() {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream("output.txt");
|
||||
fos.write("Hello, World!".getBytes());
|
||||
fos.close();
|
||||
Runtime.getRuntime().exit(0); // $ Alert
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Runtime.getRuntime().exit(1); // $ Alert
|
||||
} catch (Exception e) {
|
||||
// re-throw the exception
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void printUsageAndExit(final String message, final int exitCode) {
|
||||
System.err.println("Usage: <example_cmd> <example_args> : " + message);
|
||||
Runtime.getRuntime().exit(exitCode); // COMPLIANT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExampleRuntimeHalt {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Action action = new Action();
|
||||
action.run();
|
||||
Runtime.getRuntime().halt(0); // COMPLIANT
|
||||
}
|
||||
|
||||
static class Action {
|
||||
public void run() {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream("output.txt");
|
||||
fos.write("Hello, World!".getBytes());
|
||||
fos.close();
|
||||
Runtime.getRuntime().halt(0); // $ Alert
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Runtime.getRuntime().halt(1); // $ Alert
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ExampleSystemExit {
|
||||
|
||||
public static void main(String[] args) {
|
||||
Action action = new Action();
|
||||
try {
|
||||
action.run();
|
||||
} catch (Exception e) {
|
||||
printUsageAndExit(e.getMessage(), 1);
|
||||
}
|
||||
System.exit(0); // COMPLIANT
|
||||
}
|
||||
|
||||
static class Action {
|
||||
public void run() {
|
||||
try {
|
||||
FileOutputStream fos = new FileOutputStream("output.txt");
|
||||
fos.write("Hello, World!".getBytes());
|
||||
fos.close();
|
||||
System.exit(0); // $ Alert
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1); // $ Alert
|
||||
} catch (Exception e) {
|
||||
// re-throw the exception
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void printUsageAndExit(final String message, final int exitCode) {
|
||||
System.err.println("Usage: <example_cmd> <example_args> : " + message);
|
||||
System.exit(exitCode); // COMPLIANT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
public class LocalClassInTestMethod {
|
||||
public void testNestedCase() {
|
||||
class OuterLocalClass {
|
||||
void func() {
|
||||
class NestedLocalClass {
|
||||
void nestedMethod() {
|
||||
System.exit(4);
|
||||
Runtime.getRuntime().halt(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OuterLocalClass outer = new OuterLocalClass();
|
||||
outer.func();
|
||||
}
|
||||
public void testNestedCase2() {
|
||||
class OuterLocalClass {
|
||||
class NestedLocalClass {
|
||||
void nestedMethod() {
|
||||
System.exit(4);
|
||||
Runtime.getRuntime().halt(5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
| Version1.0.x-1.4.x/bad/default/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | file://:0:0:0:0 | (none) | configuration |
|
||||
| Version1.0.x-1.4.x/bad/false/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | Version1.0.x-1.4.x/bad/false/application.properties:2:1:2:33 | management.security.enabled=false | configuration |
|
||||
| Version1.5.x/bad/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | Version1.5.x/bad/application.properties:2:1:2:33 | management.security.enabled=false | configuration |
|
||||
| Version2.x/bad/expose/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | Version2.x/bad/expose/application.properties:2:1:2:33 | management.endpoints.web.expose=* | configuration |
|
||||
| Version2.x/bad/exposure-include/all-exposed/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | Version2.x/bad/exposure-include/all-exposed/application.properties:2:1:2:43 | management.endpoints.web.exposure.include=* | configuration |
|
||||
| Version2.x/bad/exposure-include/some-exposed/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | Version2.x/bad/exposure-include/some-exposed/application.properties:2:1:2:59 | management.endpoints.web.exposure.include=health,info,beans | configuration |
|
||||
| Version3.x/bad/all-exposed/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | Version3.x/bad/all-exposed/application.properties:2:1:2:43 | management.endpoints.web.exposure.include=* | configuration |
|
||||
| Version3.x/bad/some-exposed/pom.xml:29:9:32:22 | dependency | Insecure Spring Boot actuator $@ exposes sensitive endpoints. | Version3.x/bad/some-exposed/application.properties:2:1:2:59 | management.endpoints.web.exposure.include=health,info,beans | configuration |
|
||||
@@ -0,0 +1,2 @@
|
||||
query: Security/CWE/CWE-200/SpringBootActuatorsConfig/SpringBootActuatorsConfig.ql
|
||||
postprocess: utils/test/InlineExpectationsTestQuery.ql
|
||||
@@ -0,0 +1 @@
|
||||
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
@@ -17,7 +17,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.8.RELEASE</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -29,18 +29,15 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- BAD: No Spring Security enabled -->
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
@@ -0,0 +1,2 @@
|
||||
# vulnerable configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
management.security.enabled=false
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# safe configuration (spring boot 1.0 - 1.4): exposes actuators by default
|
||||
management.security.enabled=true
|
||||
@@ -17,7 +17,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.8.RELEASE</version>
|
||||
<version>1.2.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -34,13 +34,10 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- GOOD: Enable Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
@@ -0,0 +1,2 @@
|
||||
# safe configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=false
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.5.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# vulnerable configuration (spring boot 1.5+): requires value false to expose sensitive actuators
|
||||
management.security.enabled=true
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.5.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# vulnerable configuration (spring boot 2.0.0.RC1): exposes health and info only by default, here overridden to expose everything
|
||||
management.endpoints.web.expose=*
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to expose everything
|
||||
management.endpoints.web.exposure.include=*
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# vulnerable configuration (spring boot 2+): exposes health and info only by default, here overridden to also expose beans
|
||||
management.endpoints.web.exposure.include=health,info,beans
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# safe configuration (spring boot 2+): exposes health and info only by default
|
||||
management.endpoints.web.exposure.include=info,health
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# vulnerable configuration (spring boot 3+): exposes health only by default, here overridden to expose everything
|
||||
management.endpoints.web.exposure.include=*
|
||||
@@ -17,7 +17,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.3.8.RELEASE</version>
|
||||
<version>3.3.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
@@ -0,0 +1,2 @@
|
||||
# vulnerable configuration (spring boot 3+): exposes health only by default, here overridden to also expose info and beans
|
||||
management.endpoints.web.exposure.include=health,info,beans
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency> <!-- $ Alert -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<!-- dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,2 @@
|
||||
# safe configuration (spring boot 3+): exposes health only by default.
|
||||
management.endpoints.web.exposure.include=health
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>spring-boot-actuator-app</groupId>
|
||||
<artifactId>spring-boot-actuator-app</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.3.5</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/springframework-5.8.x
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../../stubs/springframework-5.8.x
|
||||
@@ -1,16 +0,0 @@
|
||||
#Incorrect unhashable class
|
||||
class MyMutableThing(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __hash__(self):
|
||||
raise NotImplementedError("%r is unhashable" % self)
|
||||
|
||||
#Make class unhashable in the standard way
|
||||
class MyCorrectMutableThing(object):
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
__hash__ = None
|
||||
@@ -5,11 +5,11 @@
|
||||
<overview>
|
||||
<p>User-defined classes interact with the Python virtual machine via special methods (also called "magic methods").
|
||||
For example, for a class to support addition it must implement the <code>__add__</code> and <code>__radd__</code> special methods.
|
||||
When the expression <code>a + b</code> is evaluated the Python virtual machine will call <code>type(a).__add__(a, b)</code> and if that
|
||||
When the expression <code>a + b</code> is evaluated, the Python virtual machine will call <code>type(a).__add__(a, b)</code>, and if that
|
||||
is not implemented it will call <code>type(b).__radd__(b, a)</code>.</p>
|
||||
<p>
|
||||
Since the virtual machine calls these special methods for common expressions, users of the class will expect these operations to raise standard exceptions.
|
||||
For example, users would expect that the expression <code>a.b</code> might raise an <code>AttributeError</code>
|
||||
For example, users would expect that the expression <code>a.b</code> may raise an <code>AttributeError</code>
|
||||
if the object <code>a</code> does not have an attribute <code>b</code>.
|
||||
If a <code>KeyError</code> were raised instead,
|
||||
then this would be unexpected and may break code that expected an <code>AttributeError</code>, but not a <code>KeyError</code>.
|
||||
@@ -20,50 +20,48 @@ Therefore, if a method is unable to perform the expected operation then its resp
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Attribute access, <code>a.b</code>: Raise <code>AttributeError</code></li>
|
||||
<li>Arithmetic operations, <code>a + b</code>: Do not raise an exception, return <code>NotImplemented</code> instead.</li>
|
||||
<li>Indexing, <code>a[b]</code>: Raise <code>KeyError</code>.</li>
|
||||
<li>Hashing, <code>hash(a)</code>: Use <code>__hash__ = None</code> to indicate that an object is unhashable.</li>
|
||||
<li>Equality methods, <code>a != b</code>: Never raise an exception, always return <code>True</code> or <code>False</code>.</li>
|
||||
<li>Ordering comparison methods, <code>a < b</code>: Raise a <code>TypeError</code> if the objects cannot be ordered.</li>
|
||||
<li>Most others: Ideally, do not implement the method at all, otherwise raise <code>TypeError</code> to indicate that the operation is unsupported.</li>
|
||||
<li>Attribute access, <code>a.b</code> (<code>__getattr__</code>): Raise <code>AttributeError</code>.</li>
|
||||
<li>Arithmetic operations, <code>a + b</code> (<code>__add__</code>): Do not raise an exception, return <code>NotImplemented</code> instead.</li>
|
||||
<li>Indexing, <code>a[b]</code> (<code>__getitem__</code>): Raise <code>KeyError</code> or <code>IndexError</code>.</li>
|
||||
<li>Hashing, <code>hash(a)</code> (<code>__hash__</code>): Should not raise an exception. Use <code>__hash__ = None</code> to indicate that an object is unhashable rather than raising an exception.</li>
|
||||
<li>Equality methods, <code>a == b</code> (<code>__eq__</code>): Never raise an exception, always return <code>True</code> or <code>False</code>.</li>
|
||||
<li>Ordering comparison methods, <code>a < b</code> (<code>__lt__</code>): Raise a <code>TypeError</code> if the objects cannot be ordered.</li>
|
||||
<li>Most others: If the operation is never supported, the method often does not need to be implemented at all; otherwise a <code>TypeError</code> should be raised.</li>
|
||||
</ul>
|
||||
|
||||
</overview>
|
||||
<recommendation>
|
||||
<p>If the method is meant to be abstract, then declare it so using the <code>@abstractmethod</code> decorator.
|
||||
Otherwise, either remove the method or ensure that the method raises an exception of the correct type.
|
||||
<p>If the method always raises as exception, then if it is intended to be an abstract method, the <code>@abstractmethod</code> decorator should be used.
|
||||
Otherwise, ensure that the method raises an exception of the correct type, or remove the method if the operation does not need to be supported.
|
||||
</p>
|
||||
|
||||
</recommendation>
|
||||
<example>
|
||||
|
||||
<p>
|
||||
This example shows two unhashable classes. The first class is unhashable in a non-standard way which may cause maintenance problems.
|
||||
The second, corrected, class uses the standard idiom for unhashable classes.
|
||||
In the following example, the <code>__add__</code> method of <code>A</code> raises a <code>TypeError</code> if <code>other</code> is of the wrong type.
|
||||
However, it should return <code>NotImplemented</code> instead of rising an exception, to allow other classes to support adding to <code>A</code>.
|
||||
This is demonstrated in the class <code>B</code>.
|
||||
</p>
|
||||
<sample src="IncorrectRaiseInSpecialMethod.py" />
|
||||
<sample src="examples/IncorrectRaiseInSpecialMethod.py" />
|
||||
<p>
|
||||
In this example, the first class is implicitly abstract; the <code>__add__</code> method is unimplemented,
|
||||
presumably with the expectation that it will be implemented by sub-classes.
|
||||
The second class makes this explicit with an <code>@abstractmethod</code> decoration on the unimplemented <code>__add__</code> method.
|
||||
In the following example, the <code>__getitem__</code> method of <code>C</code> raises a <code>ValueError</code>, rather than a <code>KeyError</code> or <code>IndexError</code> as expected.
|
||||
</p>
|
||||
<sample src="IncorrectRaiseInSpecialMethod2.py" />
|
||||
<sample src="examples/IncorrectRaiseInSpecialMethod2.py" />
|
||||
<p>
|
||||
In this last example, the first class implements a collection backed by the file store.
|
||||
However, should an <code>IOError</code> be raised in the <code>__getitem__</code> it will propagate to the caller.
|
||||
The second class handles any <code>IOError</code> by reraising a <code>KeyError</code> which is the standard exception for
|
||||
the <code>__getitem__</code> method.
|
||||
In the following example, the class <code>__hash__</code> method of <code>D</code> raises <code>NotImplementedError</code>.
|
||||
This causes <code>D</code> to be incorrectly identified as hashable by <code>isinstance(obj, collections.abc.Hashable)</code>; so the correct
|
||||
way to make a class unhashable is to set <code>__hash__ = None</code>.
|
||||
</p>
|
||||
|
||||
<sample src="IncorrectRaiseInSpecialMethod3.py" />
|
||||
<sample src="examples/IncorrectRaiseInSpecialMethod3.py" />
|
||||
|
||||
|
||||
</example>
|
||||
<references>
|
||||
|
||||
<li>Python Language Reference: <a href="http://docs.python.org/dev/reference/datamodel.html#special-method-names">Special Method Names</a>.</li>
|
||||
<li>Python Library Reference: <a href="https://docs.python.org/2/library/exceptions.html">Exceptions</a>.</li>
|
||||
<li>Python Library Reference: <a href="https://docs.python.org/3/library/exceptions.html">Exceptions</a>.</li>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,114 +7,188 @@
|
||||
* error-handling
|
||||
* @problem.severity recommendation
|
||||
* @sub-severity high
|
||||
* @precision very-high
|
||||
* @precision high
|
||||
* @id py/unexpected-raise-in-special-method
|
||||
*/
|
||||
|
||||
import python
|
||||
import semmle.python.ApiGraphs
|
||||
import semmle.python.dataflow.new.internal.DataFlowDispatch
|
||||
|
||||
private predicate attribute_method(string name) {
|
||||
name = "__getattribute__" or name = "__getattr__" or name = "__setattr__"
|
||||
/** Holds if `name` is the name of a special method for attribute access such as `a.b`, that should raise an `AttributeError`. */
|
||||
private predicate attributeMethod(string name) {
|
||||
name = ["__getattribute__", "__getattr__", "__delattr__"] // __setattr__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
|
||||
}
|
||||
|
||||
private predicate indexing_method(string name) {
|
||||
name = "__getitem__" or name = "__setitem__" or name = "__delitem__"
|
||||
/** Holds if `name` is the name of a special method for indexing operations such as `a[b]`, that should raise a `LookupError`. */
|
||||
private predicate indexingMethod(string name) {
|
||||
name = ["__getitem__", "__delitem__"] // __setitem__ excluded as it makes sense to raise different kinds of errors based on the `value` parameter
|
||||
}
|
||||
|
||||
private predicate arithmetic_method(string name) {
|
||||
name in [
|
||||
"__add__", "__sub__", "__or__", "__xor__", "__rshift__", "__pow__", "__mul__", "__neg__",
|
||||
"__radd__", "__rsub__", "__rdiv__", "__rfloordiv__", "__div__", "__rdiv__", "__rlshift__",
|
||||
"__rand__", "__ror__", "__rxor__", "__rrshift__", "__rpow__", "__rmul__", "__truediv__",
|
||||
"__rtruediv__", "__pos__", "__iadd__", "__isub__", "__idiv__", "__ifloordiv__", "__idiv__",
|
||||
"__ilshift__", "__iand__", "__ior__", "__ixor__", "__irshift__", "__abs__", "__ipow__",
|
||||
"__imul__", "__itruediv__", "__floordiv__", "__div__", "__divmod__", "__lshift__", "__and__"
|
||||
/** Holds if `name` is the name of a special method for arithmetic operations. */
|
||||
private predicate arithmeticMethod(string name) {
|
||||
name =
|
||||
[
|
||||
"__add__", "__sub__", "__and__", "__or__", "__xor__", "__lshift__", "__rshift__", "__pow__",
|
||||
"__mul__", "__div__", "__divmod__", "__truediv__", "__floordiv__", "__matmul__", "__radd__",
|
||||
"__rsub__", "__rand__", "__ror__", "__rxor__", "__rlshift__", "__rrshift__", "__rpow__",
|
||||
"__rmul__", "__rdiv__", "__rdivmod__", "__rtruediv__", "__rfloordiv__", "__rmatmul__",
|
||||
"__iadd__", "__isub__", "__iand__", "__ior__", "__ixor__", "__ilshift__", "__irshift__",
|
||||
"__ipow__", "__imul__", "__idiv__", "__idivmod__", "__itruediv__", "__ifloordiv__",
|
||||
"__imatmul__", "__pos__", "__neg__", "__abs__", "__invert__",
|
||||
]
|
||||
}
|
||||
|
||||
private predicate ordering_method(string name) {
|
||||
name = "__lt__"
|
||||
or
|
||||
name = "__le__"
|
||||
or
|
||||
name = "__gt__"
|
||||
or
|
||||
name = "__ge__"
|
||||
or
|
||||
name = "__cmp__" and major_version() = 2
|
||||
/** Holds if `name is the name of a special method for ordering operations such as `a < b`. */
|
||||
private predicate orderingMethod(string name) {
|
||||
name =
|
||||
[
|
||||
"__lt__",
|
||||
"__le__",
|
||||
"__gt__",
|
||||
"__ge__",
|
||||
]
|
||||
}
|
||||
|
||||
private predicate cast_method(string name) {
|
||||
name = "__nonzero__" and major_version() = 2
|
||||
or
|
||||
name = "__int__"
|
||||
or
|
||||
name = "__float__"
|
||||
or
|
||||
name = "__long__"
|
||||
or
|
||||
name = "__trunc__"
|
||||
or
|
||||
name = "__complex__"
|
||||
/** Holds if `name` is the name of a special method for casting an object to a numeric type, such as `int(x)` */
|
||||
private predicate castMethod(string name) {
|
||||
name =
|
||||
[
|
||||
"__int__",
|
||||
"__float__",
|
||||
"__index__",
|
||||
"__trunc__",
|
||||
"__complex__"
|
||||
] // __bool__ excluded as it makes sense to allow it to always raise
|
||||
}
|
||||
|
||||
predicate correct_raise(string name, ClassObject ex) {
|
||||
ex.getAnImproperSuperType() = theTypeErrorType() and
|
||||
/** Holds if we allow a special method named `name` to raise `exec` as an exception. */
|
||||
predicate correctRaise(string name, Expr exec) {
|
||||
execIsOfType(exec, "TypeError") and
|
||||
(
|
||||
name = "__copy__" or
|
||||
name = "__deepcopy__" or
|
||||
name = "__call__" or
|
||||
indexing_method(name) or
|
||||
attribute_method(name)
|
||||
indexingMethod(name) or
|
||||
attributeMethod(name) or
|
||||
// Allow add methods to raise a TypeError, as they can be used for sequence concatenation as well as arithmetic
|
||||
name = ["__add__", "__iadd__", "__radd__"]
|
||||
)
|
||||
or
|
||||
preferred_raise(name, ex)
|
||||
or
|
||||
preferred_raise(name, ex.getASuperType())
|
||||
exists(string execName |
|
||||
preferredRaise(name, execName, _) and
|
||||
execIsOfType(exec, execName)
|
||||
)
|
||||
}
|
||||
|
||||
predicate preferred_raise(string name, ClassObject ex) {
|
||||
attribute_method(name) and ex = theAttributeErrorType()
|
||||
/** Holds if it is preferred for `name` to raise exceptions of type `execName`. `message` is the alert message. */
|
||||
predicate preferredRaise(string name, string execName, string message) {
|
||||
attributeMethod(name) and
|
||||
execName = "AttributeError" and
|
||||
message = "should raise an AttributeError instead."
|
||||
or
|
||||
indexing_method(name) and ex = Object::builtin("LookupError")
|
||||
indexingMethod(name) and
|
||||
execName = "LookupError" and
|
||||
message = "should raise a LookupError (KeyError or IndexError) instead."
|
||||
or
|
||||
ordering_method(name) and ex = theTypeErrorType()
|
||||
orderingMethod(name) and
|
||||
execName = "TypeError" and
|
||||
message = "should raise a TypeError or return NotImplemented instead."
|
||||
or
|
||||
arithmetic_method(name) and ex = Object::builtin("ArithmeticError")
|
||||
arithmeticMethod(name) and
|
||||
execName = "ArithmeticError" and
|
||||
message = "should raise an ArithmeticError or return NotImplemented instead."
|
||||
or
|
||||
name = "__bool__" and ex = theTypeErrorType()
|
||||
name = "__bool__" and
|
||||
execName = "TypeError" and
|
||||
message = "should raise a TypeError instead."
|
||||
}
|
||||
|
||||
predicate no_need_to_raise(string name, string message) {
|
||||
name = "__hash__" and message = "use __hash__ = None instead"
|
||||
or
|
||||
cast_method(name) and message = "there is no need to implement the method at all."
|
||||
}
|
||||
|
||||
predicate is_abstract(FunctionObject func) {
|
||||
func.getFunction().getADecorator().(Name).getId().matches("%abstract%")
|
||||
}
|
||||
|
||||
predicate always_raises(FunctionObject f, ClassObject ex) {
|
||||
ex = f.getARaisedType() and
|
||||
strictcount(f.getARaisedType()) = 1 and
|
||||
not exists(f.getFunction().getANormalExit()) and
|
||||
/* raising StopIteration is equivalent to a return in a generator */
|
||||
not ex = theStopIterationType()
|
||||
}
|
||||
|
||||
from FunctionObject f, ClassObject cls, string message
|
||||
where
|
||||
f.getFunction().isSpecialMethod() and
|
||||
not is_abstract(f) and
|
||||
always_raises(f, cls) and
|
||||
(
|
||||
no_need_to_raise(f.getName(), message) and not cls.getName() = "NotImplementedError"
|
||||
/** Holds if `exec` is an exception object of the type named `execName`. */
|
||||
predicate execIsOfType(Expr exec, string execName) {
|
||||
// Might make sense to have execName be an IPA type here. Or part of a more general API modeling builtin/stdlib subclass relations.
|
||||
exists(string subclass |
|
||||
execName = "TypeError" and
|
||||
subclass = "TypeError"
|
||||
or
|
||||
not correct_raise(f.getName(), cls) and
|
||||
not cls.getName() = "NotImplementedError" and
|
||||
exists(ClassObject preferred | preferred_raise(f.getName(), preferred) |
|
||||
message = "raise " + preferred.getName() + " instead"
|
||||
execName = "LookupError" and
|
||||
subclass = ["LookupError", "KeyError", "IndexError"]
|
||||
or
|
||||
execName = "ArithmeticError" and
|
||||
subclass = ["ArithmeticError", "FloatingPointError", "OverflowError", "ZeroDivisionError"]
|
||||
or
|
||||
execName = "AttributeError" and
|
||||
subclass = "AttributeError"
|
||||
|
|
||||
exec = API::builtin(subclass).getACall().asExpr()
|
||||
or
|
||||
exec = API::builtin(subclass).getASubclass().getACall().asExpr()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `meth` need not be implemented if it always raises. `message` is the alert message, and `allowNotImplemented` is true
|
||||
* if we still allow the method to always raise `NotImplementedError`.
|
||||
*/
|
||||
predicate noNeedToAlwaysRaise(Function meth, string message, boolean allowNotImplemented) {
|
||||
meth.getName() = "__hash__" and
|
||||
message = "use __hash__ = None instead." and
|
||||
allowNotImplemented = false
|
||||
or
|
||||
castMethod(meth.getName()) and
|
||||
message = "this method does not need to be implemented." and
|
||||
allowNotImplemented = true and
|
||||
// Allow an always raising cast method if it's overriding other behavior
|
||||
not exists(Function overridden |
|
||||
overridden.getName() = meth.getName() and
|
||||
overridden.getScope() = getADirectSuperclass+(meth.getScope()) and
|
||||
not alwaysRaises(overridden, _)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `func` has a decorator likely marking it as an abstract method. */
|
||||
predicate isAbstract(Function func) { func.getADecorator().(Name).getId().matches("%abstract%") }
|
||||
|
||||
/** Holds if `f` always raises the exception `exec`. */
|
||||
predicate alwaysRaises(Function f, Expr exec) {
|
||||
directlyRaises(f, exec) and
|
||||
strictcount(Expr e | directlyRaises(f, e)) = 1 and
|
||||
not exists(f.getANormalExit())
|
||||
}
|
||||
|
||||
/** Holds if `f` directly raises `exec` using a `raise` statement. */
|
||||
predicate directlyRaises(Function f, Expr exec) {
|
||||
exists(Raise r |
|
||||
r.getScope() = f and
|
||||
exec = r.getException() and
|
||||
exec instanceof Call
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if `exec` is a `NotImplementedError`. */
|
||||
predicate isNotImplementedError(Expr exec) {
|
||||
exec = API::builtin("NotImplementedError").getACall().asExpr()
|
||||
}
|
||||
|
||||
/** Gets the name of the builtin exception type `exec` constructs, if it can be determined. */
|
||||
string getExecName(Expr exec) { result = exec.(Call).getFunc().(Name).getId() }
|
||||
|
||||
from Function f, Expr exec, string message
|
||||
where
|
||||
f.isSpecialMethod() and
|
||||
not isAbstract(f) and
|
||||
directlyRaises(f, exec) and
|
||||
(
|
||||
exists(boolean allowNotImplemented, string subMessage |
|
||||
alwaysRaises(f, exec) and
|
||||
noNeedToAlwaysRaise(f, subMessage, allowNotImplemented) and
|
||||
(allowNotImplemented = true implies not isNotImplementedError(exec)) and // don't alert if it's a NotImplementedError and that's ok
|
||||
message = "This method always raises $@ - " + subMessage
|
||||
)
|
||||
or
|
||||
not isNotImplementedError(exec) and
|
||||
not correctRaise(f.getName(), exec) and
|
||||
exists(string subMessage | preferredRaise(f.getName(), _, subMessage) |
|
||||
if alwaysRaises(f, exec)
|
||||
then message = "This method always raises $@ - " + subMessage
|
||||
else message = "This method raises $@ - " + subMessage
|
||||
)
|
||||
)
|
||||
select f, "Function always raises $@; " + message, cls, cls.toString()
|
||||
select f, message, exec, getExecName(exec)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
|
||||
#Abstract base class, but don't declare it.
|
||||
class ImplicitAbstractClass(object):
|
||||
|
||||
def __add__(self, other):
|
||||
raise NotImplementedError()
|
||||
|
||||
#Make abstractness explicit.
|
||||
class ExplicitAbstractClass:
|
||||
__metaclass__ = ABCMeta
|
||||
|
||||
@abstractmethod
|
||||
def __add__(self, other):
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
#Incorrect file-backed table
|
||||
class FileBackedTable(object):
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key not in self.index:
|
||||
raise IOError("Key '%s' not in table" % key)
|
||||
else:
|
||||
#May raise an IOError
|
||||
return self.backing.get_row(key)
|
||||
|
||||
#Correct by transforming exception
|
||||
class ObjectLikeFileBackedTable(object):
|
||||
|
||||
def get_from_key(self, key):
|
||||
if key not in self.index:
|
||||
raise IOError("Key '%s' not in table" % key)
|
||||
else:
|
||||
#May raise an IOError
|
||||
return self.backing.get_row(key)
|
||||
|
||||
def __getitem__(self, key):
|
||||
try:
|
||||
return self.get_from_key(key)
|
||||
except IOError:
|
||||
raise KeyError(key)
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
class A:
|
||||
def __init__(self, a):
|
||||
self.a = a
|
||||
|
||||
def __add__(self, other):
|
||||
# BAD: Should return NotImplemented instead of raising
|
||||
if not isinstance(other,A):
|
||||
raise TypeError(f"Cannot add A to {other.__class__}")
|
||||
return A(self.a + other.a)
|
||||
|
||||
class B:
|
||||
def __init__(self, a):
|
||||
self.a = a
|
||||
|
||||
def __add__(self, other):
|
||||
# GOOD: Returning NotImplemented allows for the operation to fallback to other implementations to allow other classes to support adding to B.
|
||||
if not isinstance(other,B):
|
||||
return NotImplemented
|
||||
return B(self.a + other.a)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class C:
|
||||
def __getitem__(self, idx):
|
||||
if self.idx < 0:
|
||||
# BAD: Should raise a KeyError or IndexError instead.
|
||||
raise ValueError("Invalid index")
|
||||
return self.lookup(idx)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
class D:
|
||||
def __hash__(self):
|
||||
# BAD: Use `__hash__ = None` instead.
|
||||
raise NotImplementedError(f"{self.__class__} is unhashable.")
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* The `py/unexpected-raise-in-special-method` query has been modernized. It produces additional results in cases where the exception is
|
||||
only raised conditionally. Its precision has been changed from `very-high` to `high`.
|
||||
@@ -0,0 +1,6 @@
|
||||
| test.py:6:5:6:33 | Function __getitem__ | This method always raises $@ - should raise a LookupError (KeyError or IndexError) instead. | test.py:7:15:7:33 | ZeroDivisionError() | ZeroDivisionError |
|
||||
| test.py:9:5:9:32 | Function __getattr__ | This method always raises $@ - should raise an AttributeError instead. | test.py:10:15:10:33 | ZeroDivisionError() | ZeroDivisionError |
|
||||
| test.py:12:5:12:23 | Function __bool__ | This method always raises $@ - should raise a TypeError instead. | test.py:13:15:13:26 | ValueError() | ValueError |
|
||||
| test.py:15:5:15:22 | Function __int__ | This method always raises $@ - this method does not need to be implemented. | test.py:16:15:16:26 | ValueError() | ValueError |
|
||||
| test.py:24:5:24:23 | Function __hash__ | This method always raises $@ - use __hash__ = None instead. | test.py:25:15:25:35 | NotImplementedError() | NotImplementedError |
|
||||
| test.py:28:5:28:29 | Function __sub__ | This method raises $@ - should raise an ArithmeticError or return NotImplemented instead. | test.py:30:19:30:29 | TypeError() | TypeError |
|
||||
@@ -0,0 +1,2 @@
|
||||
query: Functions/IncorrectRaiseInSpecialMethod.ql
|
||||
postprocess: utils/test/InlineExpectationsTestQuery.ql
|
||||
@@ -0,0 +1,66 @@
|
||||
class A:
|
||||
|
||||
def __add__(self, other): # No alert - Always allow NotImplementedError
|
||||
raise NotImplementedError()
|
||||
|
||||
def __getitem__(self, index): # $ Alert
|
||||
raise ZeroDivisionError()
|
||||
|
||||
def __getattr__(self, name): # $ Alert
|
||||
raise ZeroDivisionError()
|
||||
|
||||
def __bool__(self): # $ Alert
|
||||
raise ValueError()
|
||||
|
||||
def __int__(self): # $ Alert # Cast method need not be defined to always raise
|
||||
raise ValueError()
|
||||
|
||||
def __float__(self): # No alert - OK to raise conditionally
|
||||
if self.z:
|
||||
return 0
|
||||
else:
|
||||
raise ValueError()
|
||||
|
||||
def __hash__(self): # $ Alert # should use __hash__=None rather than stub implementation to make class unhashable
|
||||
raise NotImplementedError()
|
||||
|
||||
class B:
|
||||
def __sub__(self, other): # $ Alert # should return NotImplemented instead
|
||||
if not isinstance(other,B):
|
||||
raise TypeError()
|
||||
return self
|
||||
|
||||
def __add__(self, other): # No alert - allow add to raise a TypeError, as it is sometimes used for sequence concatenation as well as arithmetic
|
||||
if not isinstance(other,B):
|
||||
raise TypeError()
|
||||
return self
|
||||
|
||||
def __setitem__(self, key, val): # No alert - allow setitem to raise arbitrary exceptions as they could be due to the value, rather than a LookupError relating to the key
|
||||
if val < 0:
|
||||
raise ValueError()
|
||||
|
||||
def __getitem__(self, key): # No alert - indexing method allowed to raise TypeError or subclasses of LookupError.
|
||||
if not isinstance(key, int):
|
||||
raise TypeError()
|
||||
if key < 0:
|
||||
raise KeyError()
|
||||
return 3
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name != "a":
|
||||
raise AttributeError()
|
||||
return 2
|
||||
|
||||
def __div__(self, other):
|
||||
if other == 0:
|
||||
raise ZeroDivisionError()
|
||||
return self
|
||||
|
||||
|
||||
class D:
|
||||
def __int__(self):
|
||||
return 2
|
||||
|
||||
class E(D):
|
||||
def __int__(self): # No alert - cast method may override to raise exception
|
||||
raise TypeError()
|
||||
@@ -1,3 +0,0 @@
|
||||
| protocols.py:98:5:98:33 | Function __getitem__ | Function always raises $@; raise LookupError instead | file://:Compiled Code:0:0:0:0 | builtin-class ZeroDivisionError | builtin-class ZeroDivisionError |
|
||||
| protocols.py:101:5:101:26 | Function __getattr__ | Function always raises $@; raise AttributeError instead | file://:Compiled Code:0:0:0:0 | builtin-class ZeroDivisionError | builtin-class ZeroDivisionError |
|
||||
| protocols.py:104:5:104:23 | Function __bool__ | Function always raises $@; raise TypeError instead | file://:Compiled Code:0:0:0:0 | builtin-class ZeroDivisionError | builtin-class ZeroDivisionError |
|
||||
@@ -1 +0,0 @@
|
||||
Functions/IncorrectRaiseInSpecialMethod.ql
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::diagnostics::{ExtractionStep, emit_extraction_diagnostics};
|
||||
use crate::rust_analyzer::path_to_file_id;
|
||||
use crate::rust_analyzer::{RustAnalyzerNoSemantics, path_to_file_id};
|
||||
use crate::translate::SourceKind;
|
||||
use crate::trap::TrapId;
|
||||
use anyhow::Context;
|
||||
@@ -85,14 +85,12 @@ impl<'a> Extractor<'a> {
|
||||
translator.emit_parse_error(&ast, &err);
|
||||
}
|
||||
let no_location = (LineCol { line: 0, col: 0 }, LineCol { line: 0, col: 0 });
|
||||
if let Err(reason) = semantics_info {
|
||||
if let Err(RustAnalyzerNoSemantics { severity, reason }) = semantics_info {
|
||||
if !reason.is_empty() {
|
||||
let message = format!("semantic analyzer unavailable ({reason})");
|
||||
let full_message = format!(
|
||||
"{message}: macro expansion, call graph, and type inference will be skipped."
|
||||
);
|
||||
let full_message = format!("{message}: macro expansion will be skipped.");
|
||||
translator.emit_diagnostic(
|
||||
trap::DiagnosticSeverity::Warning,
|
||||
severity,
|
||||
"semantics".to_owned(),
|
||||
message,
|
||||
full_message,
|
||||
@@ -131,10 +129,10 @@ impl<'a> Extractor<'a> {
|
||||
&mut self,
|
||||
file: &Path,
|
||||
source_kind: SourceKind,
|
||||
reason: &str,
|
||||
err: RustAnalyzerNoSemantics,
|
||||
) {
|
||||
self.extract(
|
||||
&RustAnalyzer::WithoutSemantics { reason },
|
||||
&RustAnalyzer::from(err),
|
||||
file,
|
||||
source_kind,
|
||||
);
|
||||
@@ -158,21 +156,25 @@ impl<'a> Extractor<'a> {
|
||||
file: &Path,
|
||||
semantics: &Semantics<'_, RootDatabase>,
|
||||
vfs: &Vfs,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), RustAnalyzerNoSemantics> {
|
||||
let before = Instant::now();
|
||||
let Some(id) = path_to_file_id(file, vfs) else {
|
||||
return Err("not included in files loaded from manifest".to_string());
|
||||
return Err(RustAnalyzerNoSemantics::warning(
|
||||
"not included in files loaded from manifest",
|
||||
));
|
||||
};
|
||||
match semantics.file_to_module_def(id) {
|
||||
None => return Err("not included as a module".to_string()),
|
||||
None => {
|
||||
return Err(RustAnalyzerNoSemantics::info("not included as a module"));
|
||||
}
|
||||
Some(module)
|
||||
if module
|
||||
.as_source_file_id(semantics.db)
|
||||
.is_none_or(|mod_file_id| mod_file_id.file_id(semantics.db) != id) =>
|
||||
{
|
||||
return Err(
|
||||
"not loaded as its own module, probably included by `!include`".to_string(),
|
||||
);
|
||||
return Err(RustAnalyzerNoSemantics::info(
|
||||
"not loaded as its own module, probably included by `!include`",
|
||||
));
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
@@ -274,7 +276,11 @@ fn main() -> anyhow::Result<()> {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
extractor.extract_without_semantics(file, SourceKind::Source, "no manifest found");
|
||||
extractor.extract_without_semantics(
|
||||
file,
|
||||
SourceKind::Source,
|
||||
RustAnalyzerNoSemantics::warning("no manifest found"),
|
||||
);
|
||||
}
|
||||
let cwd = cwd()?;
|
||||
let (cargo_config, load_cargo_config) = cfg.to_cargo_config(&cwd);
|
||||
@@ -308,7 +314,7 @@ fn main() -> anyhow::Result<()> {
|
||||
vfs,
|
||||
source_mode,
|
||||
),
|
||||
Err(reason) => extractor.extract_without_semantics(file, source_mode, &reason),
|
||||
Err(e) => extractor.extract_without_semantics(file, source_mode, e),
|
||||
};
|
||||
}
|
||||
for (file_id, file) in vfs.iter() {
|
||||
@@ -335,7 +341,7 @@ fn main() -> anyhow::Result<()> {
|
||||
extractor.extract_without_semantics(
|
||||
file,
|
||||
SourceKind::Source,
|
||||
"unable to load manifest",
|
||||
RustAnalyzerNoSemantics::warning("unable to load manifest"),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -347,7 +353,7 @@ fn main() -> anyhow::Result<()> {
|
||||
let entry = entry.context("failed to read builtins directory")?;
|
||||
let path = entry.path();
|
||||
if path.extension().is_some_and(|ext| ext == "rs") {
|
||||
extractor.extract_without_semantics(&path, SourceKind::Library, "");
|
||||
extractor.extract_without_semantics(&path, SourceKind::Library, Default::default());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::trap;
|
||||
use itertools::Itertools;
|
||||
use ra_ap_base_db::{EditionedFileId, FileText, RootQueryDb, SourceDatabase};
|
||||
use ra_ap_hir::Semantics;
|
||||
@@ -23,16 +24,47 @@ use std::rc::Rc;
|
||||
use tracing::{debug, error, info, trace, warn};
|
||||
use triomphe::Arc;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct RustAnalyzerNoSemantics {
|
||||
pub severity: trap::DiagnosticSeverity,
|
||||
pub reason: &'static str,
|
||||
}
|
||||
|
||||
impl RustAnalyzerNoSemantics {
|
||||
pub fn warning(reason: &'static str) -> Self {
|
||||
RustAnalyzerNoSemantics {
|
||||
severity: trap::DiagnosticSeverity::Warning,
|
||||
reason,
|
||||
}
|
||||
}
|
||||
pub fn info(reason: &'static str) -> Self {
|
||||
RustAnalyzerNoSemantics {
|
||||
severity: trap::DiagnosticSeverity::Info,
|
||||
reason,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RustAnalyzer<'a> {
|
||||
WithSemantics {
|
||||
vfs: &'a Vfs,
|
||||
semantics: &'a Semantics<'a, RootDatabase>,
|
||||
},
|
||||
WithoutSemantics {
|
||||
reason: &'a str,
|
||||
severity: trap::DiagnosticSeverity,
|
||||
reason: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<RustAnalyzerNoSemantics> for RustAnalyzer<'static> {
|
||||
fn from(value: RustAnalyzerNoSemantics) -> Self {
|
||||
RustAnalyzer::WithoutSemantics {
|
||||
severity: value.severity,
|
||||
reason: value.reason,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct FileSemanticInformation<'a> {
|
||||
pub file_id: EditionedFileId,
|
||||
pub semantics: &'a Semantics<'a, RootDatabase>,
|
||||
@@ -42,7 +74,7 @@ pub struct ParseResult<'a> {
|
||||
pub ast: SourceFile,
|
||||
pub text: Arc<str>,
|
||||
pub errors: Vec<SyntaxError>,
|
||||
pub semantics_info: Result<FileSemanticInformation<'a>, &'a str>,
|
||||
pub semantics_info: Result<FileSemanticInformation<'a>, RustAnalyzerNoSemantics>,
|
||||
}
|
||||
|
||||
impl<'a> RustAnalyzer<'a> {
|
||||
@@ -52,7 +84,7 @@ impl<'a> RustAnalyzer<'a> {
|
||||
load_config: &LoadCargoConfig,
|
||||
) -> Option<(RootDatabase, Vfs)> {
|
||||
let progress = |t| trace!("progress: {t}");
|
||||
let manifest = project.manifest_path();
|
||||
let manifest: &ManifestPath = project.manifest_path();
|
||||
match load_workspace_at(manifest.as_ref(), config, load_config, &progress) {
|
||||
Ok((db, vfs, _macro_server)) => Some((db, vfs)),
|
||||
Err(err) => {
|
||||
@@ -67,16 +99,25 @@ impl<'a> RustAnalyzer<'a> {
|
||||
fn get_file_data(
|
||||
&self,
|
||||
path: &Path,
|
||||
) -> Result<(&Semantics<'_, RootDatabase>, EditionedFileId, FileText), &str> {
|
||||
) -> Result<(&Semantics<'_, RootDatabase>, EditionedFileId, FileText), RustAnalyzerNoSemantics>
|
||||
{
|
||||
match self {
|
||||
RustAnalyzer::WithoutSemantics { reason } => Err(reason),
|
||||
RustAnalyzer::WithoutSemantics { severity, reason } => Err(RustAnalyzerNoSemantics {
|
||||
severity: *severity,
|
||||
reason,
|
||||
}),
|
||||
RustAnalyzer::WithSemantics { vfs, semantics } => {
|
||||
let file_id = path_to_file_id(path, vfs).ok_or("file not found in project")?;
|
||||
let input = std::panic::catch_unwind(|| semantics.db.file_text(file_id))
|
||||
.or(Err("no text available for the file in the project"))?;
|
||||
let editioned_file_id = semantics
|
||||
.attach_first_edition(file_id)
|
||||
.ok_or("failed to determine rust edition")?;
|
||||
let file_id = path_to_file_id(path, vfs).ok_or(
|
||||
RustAnalyzerNoSemantics::warning("file not found in project"),
|
||||
)?;
|
||||
let input = std::panic::catch_unwind(|| semantics.db.file_text(file_id)).or(
|
||||
Err(RustAnalyzerNoSemantics::warning(
|
||||
"no text available for the file in the project",
|
||||
)),
|
||||
)?;
|
||||
let editioned_file_id = semantics.attach_first_edition(file_id).ok_or(
|
||||
RustAnalyzerNoSemantics::warning("failed to determine rust edition"),
|
||||
)?;
|
||||
Ok((semantics, editioned_file_id, input))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,7 +470,7 @@ impl<'a> Translator<'a> {
|
||||
mcall,
|
||||
&SyntaxError::new(
|
||||
format!(
|
||||
"macro expansion failed: could not resolve macro '{}'",
|
||||
"macro expansion failed for '{}'",
|
||||
mcall.path().map(|p| p.to_string()).unwrap_or_default()
|
||||
),
|
||||
range.unwrap_or_else(|| TextRange::empty(TextSize::from(0))),
|
||||
|
||||
@@ -127,9 +127,10 @@ pub struct TrapFile {
|
||||
compression: Compression,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
|
||||
pub enum DiagnosticSeverity {
|
||||
Debug = 10,
|
||||
#[default]
|
||||
Info = 20,
|
||||
Warning = 30,
|
||||
Error = 40,
|
||||
|
||||
16
rust/ql/.generated.list
generated
16
rust/ql/.generated.list
generated
@@ -1,4 +1,4 @@
|
||||
lib/codeql/rust/controlflow/internal/generated/CfgNodes.qll 06394c1314f09d65b8ade88732f1114202e1896ebeb8d687f8ee230cea01127b 7a9223854ec30cae886b237d7930120ce073ab49af486b0d3bc971df2a039e62
|
||||
lib/codeql/rust/controlflow/internal/generated/CfgNodes.qll 11c7521ec2231a4d0447f30fc3d0bb14aebb659bd8cf75935af1050673a3b1d6 d0a77b572a032e43f1c47622315c0cdfe17e68c2b057534b9322fc528029fb40
|
||||
lib/codeql/rust/elements/Abi.qll 485a2e79f6f7bfd1c02a6e795a71e62dede3c3e150149d5f8f18b761253b7208 6159ba175e7ead0dd2e3f2788f49516c306ee11b1a443bd4bdc00b7017d559bd
|
||||
lib/codeql/rust/elements/Addressable.qll 13011bfd2e1556694c3d440cc34af8527da4df49ad92b62f2939d3699ff2cea5 ddb25935f7553a1a384b1abe2e4b4fa90ab50b952dadec32fd867afcb054f4be
|
||||
lib/codeql/rust/elements/Adt.qll c2afed4ac2e17039ccd98f74ea22111f4d765c4e232c50ccd3128da0d26da837 1380bde2eb667c6ec2ef5f8710aa24e926851c9e321ebc72ba514fa92c369dc3
|
||||
@@ -146,7 +146,7 @@ lib/codeql/rust/elements/Static.qll 9dca6d4fb80fb4ead49a3de89bec2b02bae6f96fbc26
|
||||
lib/codeql/rust/elements/Stmt.qll 532b12973037301246daf7d8c0177f734202f43d9261c7a4ca6f5080eea8ca64 b838643c4f2b4623d2c816cddad0e68ca3e11f2879ab7beaece46f489ec4b1f3
|
||||
lib/codeql/rust/elements/StmtList.qll e874859ce03672d0085e47e0ca5e571b92b539b31bf0d5a8802f9727bef0c6b0 e5fe83237f713cdb57c446a6e1c20f645c2f49d9f5ef2c984032df83acb3c0de
|
||||
lib/codeql/rust/elements/Struct.qll 297d3ea732fc7fbb8b8fb5479c1873ce84705146853ff752c84a6f70af12b923 3df0e5fd50a910a0b5611c3a860a1d7c318f6925c3a0727006d91840caf04812
|
||||
lib/codeql/rust/elements/StructExpr.qll af9059c01a97755e94f1a8b60c66d9c7663ed0705b2845b086b8953f16019fab 2d33d86b035a15c1b31c3e07e0e74c4bbe57a71c5a55d60e720827814e73b7ba
|
||||
lib/codeql/rust/elements/StructExpr.qll 84f384ef74c723796e514186037a91dd9666556f62c717f133ce22e9dda4425f 176497835252cfdfe110e58ebde9fbde553d03e44e07d3e4d8041e835dbf31b9
|
||||
lib/codeql/rust/elements/StructExprField.qll 3eb9f17ecd1ad38679689eb4ecc169d3a0b5b7a3fc597ae5a957a7aea2f74e4f 8fcd26f266f203004899a60447ba16e7eae4e3a654fbec7f54e26857730ede93
|
||||
lib/codeql/rust/elements/StructExprFieldList.qll 6efb2ec4889b38556dc679bb89bbd4bd76ed6a60014c41f8e232288fc23b2d52 dc867a0a4710621e04b36bbec7d317d6f360e0d6ac68b79168c8b714babde31d
|
||||
lib/codeql/rust/elements/StructField.qll c43a552ce22c768c7f4c878501f08ecd4eae3554c5cd885dcd2e8625fe705233 bfd7934835ca41eb70e4064198d9b40ec9812842fb4349e412d1aaf98c3cd625
|
||||
@@ -159,7 +159,7 @@ lib/codeql/rust/elements/TokenTree.qll 23e57fd945ce509df5122aa46f7971360788945cb
|
||||
lib/codeql/rust/elements/Trait.qll f78a917c2f2e5a0dfcd7c36e95ad67b1fa218484ee509610db8ca38453bebd4c 2a12f03870ebf86e104bdc3b61aae8512bfafbbf79a0cff5c3c27a04635926af
|
||||
lib/codeql/rust/elements/TraitAlias.qll 1d82d043f24dbac04baa7aa3882c6884b8ffbc5d9b97669ce8efb7e2c8d3d2c8 505ba5426e87b3c49721f440fbc9ad6b0e7d89d1b1a51ca3fa3a6cc2d36f8b82
|
||||
lib/codeql/rust/elements/TryExpr.qll cb452f53292a1396139f64a35f05bb11501f6b363f8affc9f2d5f1945ad4a647 d60ad731bfe256d0f0b688bdc31708759a3d990c11dee4f1d85ccc0d9e07bec9
|
||||
lib/codeql/rust/elements/TupleExpr.qll 561486554f0c397bc37c87894c56507771174bfb25f19b3bf258a94f67573e56 d523246820853ff0a7c6b5f9dbe73d42513cadd6d6b76ea7e64147140ac93c15
|
||||
lib/codeql/rust/elements/TupleExpr.qll 1b1be270198f9d3db1c28c4caaa4a7fe9b5ae14651f1a10e2891a7d78d6ad18b 4f585aa684dfbff753e342903ddd60ee4d7c374b8bddeb645784d10903c90ae0
|
||||
lib/codeql/rust/elements/TupleField.qll e20a991f7f1322cc7c05b2a8946d5017edb119812efa3e44daa94a5dff2d0c7b 8c25c9577fef8b5b9a4b285ceb7cfffcd8d89448035b1967cd7fda1503adfe13
|
||||
lib/codeql/rust/elements/TupleFieldList.qll b67cd2dec918d09e582467e5db7a38c8fa18350af591b43a1b450cd2026dbb67 22fdd1e77c16e3be4627ee7a45985b94785492d36056eeeff2c94b43450b48c8
|
||||
lib/codeql/rust/elements/TuplePat.qll 028cdea43868b0fdd2fc4c31ff25b6bbb40813e8aaccf72186051a280db7632e 38c56187971671e6a9dd0c6ccccb2ee4470aa82852110c6b89884496eb4abc64
|
||||
@@ -407,7 +407,7 @@ lib/codeql/rust/elements/internal/TraitConstructor.qll 1f790e63c32f1a22ae1b039ca
|
||||
lib/codeql/rust/elements/internal/TryExprConstructor.qll 98e3077ebc4d76f687488b344f532b698512af215b66f0a74b5cea8ed180836c b95603c10c262911eeffdf4ccba14849e8443916b360e287963d5f2582d8e434
|
||||
lib/codeql/rust/elements/internal/TryExprImpl.qll cacf43a49ba518be3f94e4a355f5889861edc41f77601eff27e0ed774eca6651 5f4a6a346ec457d5de89b32419e8b4c2deddc55e2d61dbb59842d7f34aa11c44
|
||||
lib/codeql/rust/elements/internal/TupleExprConstructor.qll 71c38786723225d3d90399b8a085b2b2664c62256654db9e1288fadd56745b9d 639ad70b49ebadc027127fbdc9de14e5180169a4285908233bc38ccac6f14110
|
||||
lib/codeql/rust/elements/internal/TupleExprImpl.qll 23a0e4367fbcfcec3e2cf4a429f329a222b399c6729dd60f7ea42550273a6132 615f3b4897fdcbfddcf5c58e6edd64bf6e395923af89cc4e2a336099168bb6ad
|
||||
lib/codeql/rust/elements/internal/TupleExprImpl.qll daabbc7dd36c615cdd8d3b59e06f4992a302b26554115711f733508836887abe 4c43a26e5f8b68d9d032bb5cd0af88cf9ac9b4b4e40af47dc85dd931ce9db6f8
|
||||
lib/codeql/rust/elements/internal/TupleFieldConstructor.qll 89d3cf2540235044ed5a89706cfbdebc5cdf9180fd5b6d3376c79a1b2c0430c0 16861fe089aac8e42a5a90d81dd48d5015391d0a06c78ca02bd876d65378699f
|
||||
lib/codeql/rust/elements/internal/TupleFieldListConstructor.qll 4335ba2061b6e4968db9ec05c0b4d3e6a564db89a2df69e036f317672a7900b1 0b8dded875dbf696cf588e8c21acc27332a2ff66ced7bfabdfc1ad621991f888
|
||||
lib/codeql/rust/elements/internal/TupleFieldListImpl.qll 74869e92a3cbdd7895adaaa418d29d5e97387daf46c17315f219ad967af15d76 5815e4b37db958663df1f6fedc9667a11b261c9c2133e3f983a3aedc452c01fc
|
||||
@@ -587,7 +587,11 @@ lib/codeql/rust/elements/internal/generated/PtrTypeRepr.qll 8d0ea4f6c7f8203340bf
|
||||
lib/codeql/rust/elements/internal/generated/PureSynthConstructors.qll e5b8e69519012bbaae29dcb82d53f7f7ecce368c0358ec27ef6180b228a0057f e5b8e69519012bbaae29dcb82d53f7f7ecce368c0358ec27ef6180b228a0057f
|
||||
lib/codeql/rust/elements/internal/generated/RangeExpr.qll 23cca03bf43535f33b22a38894f70d669787be4e4f5b8fe5c8f7b964d30e9027 18624cef6c6b679eeace2a98737e472432e0ead354cca02192b4d45330f047c9
|
||||
lib/codeql/rust/elements/internal/generated/RangePat.qll 80826a6a6868a803aa2372e31c52a03e1811a3f1f2abdb469f91ca0bfdd9ecb6 34ee1e208c1690cba505dff2c588837c0cd91e185e2a87d1fe673191962276a9
|
||||
<<<<<<< HEAD
|
||||
lib/codeql/rust/elements/internal/generated/Raw.qll 53fc373ce72ac66e58c68f27e90f627f190590d226602f55b38827422aa1c78a 958aaf7f788fe126de27610203a87cf9fdd8a02f2d2e2dd77e69a7b8fef5db55
|
||||
=======
|
||||
lib/codeql/rust/elements/internal/generated/Raw.qll b32f6737ca918cf003f294cc809546e3e89fa9f91666b20aab8acaa6fb986094 aeecf005da2fcfcc6e143c7970866f476f3b146632ed6bb36ccb5db19570c11b
|
||||
>>>>>>> main
|
||||
lib/codeql/rust/elements/internal/generated/RefExpr.qll 7d995884e3dc1c25fc719f5d7253179344d63650e217e9ff6530285fe7a57f64 f2c3c12551deea4964b66553fb9b6423ee16fec53bd63db4796191aa60dc6c66
|
||||
lib/codeql/rust/elements/internal/generated/RefPat.qll 456ede39837463ee22a630ec7ab6c8630d3664a8ea206fcc6e4f199e92fa564c 5622062765f32930465ba6b170e986706f159f6070f48adee3c20e24e8df4e05
|
||||
lib/codeql/rust/elements/internal/generated/RefTypeRepr.qll 5b0663a6d234572fb3e467e276d019415caa95ef006438cc59b7af4e1783161e 0e27c8a8f0e323c0e4d6db01fca821bf07c0864d293cdf96fa891b10820c1e4b
|
||||
@@ -604,7 +608,7 @@ lib/codeql/rust/elements/internal/generated/Static.qll 1a6c87d3c5602e3d02268ebe2
|
||||
lib/codeql/rust/elements/internal/generated/Stmt.qll 8473ff532dd5cc9d7decaddcd174b94d610f6ca0aec8e473cc051dad9f3db917 6ef7d2b5237c2dbdcacbf7d8b39109d4dc100229f2b28b5c9e3e4fbf673ba72b
|
||||
lib/codeql/rust/elements/internal/generated/StmtList.qll 816aebf8f56e179f5f0ba03e80d257ee85459ea757392356a0af6dbd0cd9ef5e 6aa51cdcdc8d93427555fa93f0e84afdfbbd4ffc8b8d378ae4a22b5b6f94f48b
|
||||
lib/codeql/rust/elements/internal/generated/Struct.qll 999da1b46e40d6e03fd2338fea02429462877c329c5d1338618cbd886a81567e daa7ff7bd32c554462e0a1502d8319cb5e734e056d0564e06596e416e2b88e9d
|
||||
lib/codeql/rust/elements/internal/generated/StructExpr.qll c6d861eaa0123b103fd9ffd2485423419ef9b7e0b4af9ed2a2090d8ec534f65d 50da99ee44771e1239ed8919f711991dd3ec98589fbe49b49b68c88074a07d74
|
||||
lib/codeql/rust/elements/internal/generated/StructExpr.qll e77702890561102af38f52d836729e82569c964f8d4c7e680b27992c1ff0f141 23dc51f68107ab0e5c9dd88a6bcc85bb66e8e0f4064cb4d416f50f2ba5db698c
|
||||
lib/codeql/rust/elements/internal/generated/StructExprField.qll 6bdc52ed325fd014495410c619536079b8c404e2247bd2435aa7685dd56c3833 501a30650cf813176ff325a1553da6030f78d14be3f84fea6d38032f4262c6b0
|
||||
lib/codeql/rust/elements/internal/generated/StructExprFieldList.qll 298d33442d1054922d2f97133a436ee559f1f35b7708523284d1f7eee7ebf443 7febe38a79fadf3dcb53fb8f8caf4c2780f5df55a1f8336269c7b674d53c6272
|
||||
lib/codeql/rust/elements/internal/generated/StructField.qll 0ccd678b64b82fdab7ffe9eb74f0d393b22da4459fe72248828896b5204c009c 0faf5a517eccc43141a48809ed35b864341a35764de2dba7442daa899ff4ff69
|
||||
@@ -619,7 +623,7 @@ lib/codeql/rust/elements/internal/generated/TokenTree.qll 1a3c4f5f30659738641abd
|
||||
lib/codeql/rust/elements/internal/generated/Trait.qll 8fa41b50fa0f68333534f2b66bb4ec8e103ff09ac8fa5c2cc64bc04beafec205 ce1c9aa6d0e2f05d28aab8e1165c3b9fb8e24681ade0cf6a9df2e8617abeae7e
|
||||
lib/codeql/rust/elements/internal/generated/TraitAlias.qll 40a296cf89eceaf02a32db90acb42bdc90df10e717bae3ab95bc09d842360a5b af85cf1f8fa46a8b04b763cdcacc6643b83c074c58c1344e485157d2ceb26306
|
||||
lib/codeql/rust/elements/internal/generated/TryExpr.qll 73052d7d309427a30019ad962ee332d22e7e48b9cc98ee60261ca2df2f433f93 d9dd70bf69eaa22475acd78bea504341e3574742a51ad9118566f39038a02d85
|
||||
lib/codeql/rust/elements/internal/generated/TupleExpr.qll 75186da7c077287b9a86fc9194221ab565d458c08a5f80b763e73be5b646b29f 0250d75c43e2e6f56cdc8a0c00cc42b3d459ea8d48172d236c8cdf0fe96dfed2
|
||||
lib/codeql/rust/elements/internal/generated/TupleExpr.qll 98f10bc72d09f98e3be87f41b1a3cbf037f4a7e3d3560dfa6d5759905a8177a5 6a9eb5568c518876b2912371e2b7b774cf5245097c5a0206eda35b749995f00b
|
||||
lib/codeql/rust/elements/internal/generated/TupleField.qll d546b4e0c1a0b243c2bf88b371377cf9a396ca497cd5e78915e0e552910b6093 c0a754d15e0de590ee15139d8d366e4d7e4d33882c943e6ea8fa5fa8dce790e3
|
||||
lib/codeql/rust/elements/internal/generated/TupleFieldList.qll fb76d1a395326361859177c05e90e5bbb22d37518758752e9d89906006fb683e f31508b120c36f569cc7dcae06c9e55cf875abfb2fbe54a64ec12d8b3d2db108
|
||||
lib/codeql/rust/elements/internal/generated/TuplePat.qll 4e13b509e1c9dd1581a9dc50d38e0a6e36abc1254ea9c732b5b3e6503335afeb 298028df9eb84e106e625ed09d6b20038ad47bfc2faf634a0ffea50b17b5805d
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
| src/directory_module/not_loaded.rs:1:1:1:1 | semantic analyzer unavailable (not included as a module) | Extraction warning in src/directory_module/not_loaded.rs with message semantic analyzer unavailable (not included as a module) | 1 |
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
| Extraction errors | 0 |
|
||||
| Extraction warnings | 1 |
|
||||
| Extraction warnings | 0 |
|
||||
| Files extracted - total | 5 |
|
||||
| Files extracted - with errors | 1 |
|
||||
| Files extracted - without errors | 4 |
|
||||
| Files extracted - without errors % | 80 |
|
||||
| Files extracted - with errors | 0 |
|
||||
| Files extracted - without errors | 5 |
|
||||
| Files extracted - without errors % | 100 |
|
||||
| Inconsistencies - AST | 0 |
|
||||
| Inconsistencies - CFG | 0 |
|
||||
| Inconsistencies - Path resolution | 0 |
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Attribute macros are now taken into account when identifying macro-expanded code. This affects the queries `rust/unused-variable` and `rust/unused-value`, which exclude results in macro-expanded code.
|
||||
@@ -2944,8 +2944,8 @@ module MakeCfgNodes<LocationSig Loc, InputSig<Loc> Input> {
|
||||
* ```rust
|
||||
* let first = Foo { a: 1, b: 2 };
|
||||
* let second = Foo { a: 2, ..first };
|
||||
* Foo { a: 1, b: 2 }[2] = 10;
|
||||
* Foo { .. } = second;
|
||||
* let n = Foo { a: 1, b: 2 }.b;
|
||||
* Foo { a: m, .. } = second;
|
||||
* ```
|
||||
*/
|
||||
final class StructExprCfgNode extends CfgNodeFinal, ExprCfgNode {
|
||||
@@ -3063,8 +3063,9 @@ module MakeCfgNodes<LocationSig Loc, InputSig<Loc> Input> {
|
||||
/**
|
||||
* A tuple expression. For example:
|
||||
* ```rust
|
||||
* (1, "one");
|
||||
* (2, "two")[0] = 3;
|
||||
* let tuple = (1, "one");
|
||||
* let n = (2, "two").0;
|
||||
* let (a, b) = tuple;
|
||||
* ```
|
||||
*/
|
||||
final class TupleExprCfgNode extends CfgNodeFinal, ExprCfgNode {
|
||||
|
||||
4
rust/ql/lib/codeql/rust/elements/StructExpr.qll
generated
4
rust/ql/lib/codeql/rust/elements/StructExpr.qll
generated
@@ -13,8 +13,8 @@ import codeql.rust.elements.StructExprFieldList
|
||||
* ```rust
|
||||
* let first = Foo { a: 1, b: 2 };
|
||||
* let second = Foo { a: 2, ..first };
|
||||
* Foo { a: 1, b: 2 }[2] = 10;
|
||||
* Foo { .. } = second;
|
||||
* let n = Foo { a: 1, b: 2 }.b;
|
||||
* Foo { a: m, .. } = second;
|
||||
* ```
|
||||
*/
|
||||
final class StructExpr = Impl::StructExpr;
|
||||
|
||||
5
rust/ql/lib/codeql/rust/elements/TupleExpr.qll
generated
5
rust/ql/lib/codeql/rust/elements/TupleExpr.qll
generated
@@ -10,8 +10,9 @@ import codeql.rust.elements.Expr
|
||||
/**
|
||||
* A tuple expression. For example:
|
||||
* ```rust
|
||||
* (1, "one");
|
||||
* (2, "two")[0] = 3;
|
||||
* let tuple = (1, "one");
|
||||
* let n = (2, "two").0;
|
||||
* let (a, b) = tuple;
|
||||
* ```
|
||||
*/
|
||||
final class TupleExpr = Impl::TupleExpr;
|
||||
|
||||
@@ -19,6 +19,8 @@ module Impl {
|
||||
or
|
||||
n = root.(Adt).getDeriveMacroExpansion(_)
|
||||
or
|
||||
n = root.(Item).getAttributeMacroExpansion()
|
||||
or
|
||||
isInMacroExpansion(root, n.getParentNode())
|
||||
}
|
||||
|
||||
|
||||
@@ -20,8 +20,8 @@ module Impl {
|
||||
* ```rust
|
||||
* let first = Foo { a: 1, b: 2 };
|
||||
* let second = Foo { a: 2, ..first };
|
||||
* Foo { a: 1, b: 2 }[2] = 10;
|
||||
* Foo { .. } = second;
|
||||
* let n = Foo { a: 1, b: 2 }.b;
|
||||
* Foo { a: m, .. } = second;
|
||||
* ```
|
||||
*/
|
||||
class StructExpr extends Generated::StructExpr {
|
||||
|
||||
@@ -15,8 +15,9 @@ module Impl {
|
||||
/**
|
||||
* A tuple expression. For example:
|
||||
* ```rust
|
||||
* (1, "one");
|
||||
* (2, "two")[0] = 3;
|
||||
* let tuple = (1, "one");
|
||||
* let n = (2, "two").0;
|
||||
* let (a, b) = tuple;
|
||||
* ```
|
||||
*/
|
||||
class TupleExpr extends Generated::TupleExpr { }
|
||||
|
||||
@@ -3346,8 +3346,9 @@ module Raw {
|
||||
* INTERNAL: Do not use.
|
||||
* A tuple expression. For example:
|
||||
* ```rust
|
||||
* (1, "one");
|
||||
* (2, "two")[0] = 3;
|
||||
* let tuple = (1, "one");
|
||||
* let n = (2, "two").0;
|
||||
* let (a, b) = tuple;
|
||||
* ```
|
||||
*/
|
||||
class TupleExpr extends @tuple_expr, Expr {
|
||||
@@ -4054,6 +4055,63 @@ module Raw {
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
* A path pattern. For example:
|
||||
* ```rust
|
||||
* match x {
|
||||
* Foo::Bar => "ok",
|
||||
* _ => "fail",
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class PathPat extends @path_pat, Pat, PathAstNode {
|
||||
override string toString() { result = "PathPat" }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
* A struct expression. For example:
|
||||
* ```rust
|
||||
* let first = Foo { a: 1, b: 2 };
|
||||
* let second = Foo { a: 2, ..first };
|
||||
* let n = Foo { a: 1, b: 2 }.b;
|
||||
* Foo { a: m, .. } = second;
|
||||
* ```
|
||||
*/
|
||||
class StructExpr extends @struct_expr, Expr, PathAstNode {
|
||||
override string toString() { result = "StructExpr" }
|
||||
|
||||
/**
|
||||
* Gets the struct expression field list of this struct expression, if it exists.
|
||||
*/
|
||||
StructExprFieldList getStructExprFieldList() {
|
||||
struct_expr_struct_expr_field_lists(this, result)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
* A struct pattern. For example:
|
||||
* ```rust
|
||||
* match x {
|
||||
* Foo { a: 1, b: 2 } => "ok",
|
||||
* Foo { .. } => "fail",
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
class StructPat extends @struct_pat, Pat, PathAstNode {
|
||||
override string toString() { result = "StructPat" }
|
||||
|
||||
/**
|
||||
* Gets the struct pattern field list of this struct pattern, if it exists.
|
||||
*/
|
||||
StructPatFieldList getStructPatFieldList() { struct_pat_struct_pat_field_lists(this, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL: Do not use.
|
||||
>>>>>>> main
|
||||
* A Trait. For example:
|
||||
* ```
|
||||
* trait Frobinizable {
|
||||
|
||||
@@ -20,8 +20,8 @@ module Generated {
|
||||
* ```rust
|
||||
* let first = Foo { a: 1, b: 2 };
|
||||
* let second = Foo { a: 2, ..first };
|
||||
* Foo { a: 1, b: 2 }[2] = 10;
|
||||
* Foo { .. } = second;
|
||||
* let n = Foo { a: 1, b: 2 }.b;
|
||||
* Foo { a: m, .. } = second;
|
||||
* ```
|
||||
* INTERNAL: Do not reference the `Generated::StructExpr` class directly.
|
||||
* Use the subclass `StructExpr`, where the following predicates are available.
|
||||
|
||||
@@ -18,8 +18,9 @@ module Generated {
|
||||
/**
|
||||
* A tuple expression. For example:
|
||||
* ```rust
|
||||
* (1, "one");
|
||||
* (2, "two")[0] = 3;
|
||||
* let tuple = (1, "one");
|
||||
* let n = (2, "two").0;
|
||||
* let (a, b) = tuple;
|
||||
* ```
|
||||
* INTERNAL: Do not reference the `Generated::TupleExpr` class directly.
|
||||
* Use the subclass `TupleExpr`, where the following predicates are available.
|
||||
|
||||
@@ -82,14 +82,23 @@ abstract class Type extends TType {
|
||||
pragma[nomagic]
|
||||
abstract TupleField getTupleField(int i);
|
||||
|
||||
/** Gets the `i`th type parameter of this type, if any. */
|
||||
abstract TypeParameter getTypeParameter(int i);
|
||||
/**
|
||||
* Gets the `i`th positional type parameter of this type, if any.
|
||||
*
|
||||
* This excludes synthetic type parameters, such as associated types in traits.
|
||||
*/
|
||||
abstract TypeParameter getPositionalTypeParameter(int i);
|
||||
|
||||
/** Gets the default type for the `i`th type parameter, if any. */
|
||||
TypeMention getTypeParameterDefault(int i) { none() }
|
||||
|
||||
/** Gets a type parameter of this type. */
|
||||
final TypeParameter getATypeParameter() { result = this.getTypeParameter(_) }
|
||||
/**
|
||||
* Gets a type parameter of this type.
|
||||
*
|
||||
* This includes both positional type parameters and synthetic type parameters,
|
||||
* such as associated types in traits.
|
||||
*/
|
||||
TypeParameter getATypeParameter() { result = this.getPositionalTypeParameter(_) }
|
||||
|
||||
/** Gets a textual representation of this type. */
|
||||
abstract string toString();
|
||||
@@ -108,7 +117,9 @@ class TupleType extends Type, TTuple {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) { result = TTupleTypeParameter(arity, i) }
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TTupleTypeParameter(arity, i)
|
||||
}
|
||||
|
||||
/** Gets the arity of this tuple type. */
|
||||
int getArity() { result = arity }
|
||||
@@ -141,7 +152,7 @@ class StructType extends StructOrEnumType, TStruct {
|
||||
|
||||
override TupleField getTupleField(int i) { result = struct.getTupleField(i) }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TTypeParamTypeParameter(struct.getGenericParamList().getTypeParam(i))
|
||||
}
|
||||
|
||||
@@ -166,7 +177,7 @@ class EnumType extends StructOrEnumType, TEnum {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TTypeParamTypeParameter(enum.getGenericParamList().getTypeParam(i))
|
||||
}
|
||||
|
||||
@@ -192,10 +203,18 @@ class TraitType extends Type, TTrait {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TTypeParamTypeParameter(trait.getGenericParamList().getTypeParam(i))
|
||||
}
|
||||
|
||||
override TypeParameter getATypeParameter() {
|
||||
result = super.getATypeParameter()
|
||||
or
|
||||
result.(AssociatedTypeTypeParameter).getTrait() = trait
|
||||
or
|
||||
result.(SelfTypeParameter).getTrait() = trait
|
||||
}
|
||||
|
||||
override TypeMention getTypeParameterDefault(int i) {
|
||||
result = trait.getGenericParamList().getTypeParam(i).getDefaultType()
|
||||
}
|
||||
@@ -218,7 +237,7 @@ class ArrayType extends Type, TArrayType {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TArrayTypeParameter() and
|
||||
i = 0
|
||||
}
|
||||
@@ -241,7 +260,7 @@ class RefType extends Type, TRefType {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TRefTypeParameter() and
|
||||
i = 0
|
||||
}
|
||||
@@ -274,7 +293,7 @@ class ImplTraitType extends Type, TImplTraitType {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
exists(TypeParam tp |
|
||||
implTraitTypeParam(impl, i, tp) and
|
||||
result = TImplTraitTypeParameter(impl, tp)
|
||||
@@ -295,10 +314,19 @@ class DynTraitType extends Type, TDynTraitType {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override DynTraitTypeParameter getTypeParameter(int i) {
|
||||
override DynTraitTypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TDynTraitTypeParameter(trait.getGenericParamList().getTypeParam(i))
|
||||
}
|
||||
|
||||
override TypeParameter getATypeParameter() {
|
||||
result = super.getATypeParameter()
|
||||
or
|
||||
exists(AstNode n |
|
||||
dynTraitTypeParameter(trait, n) and
|
||||
result = TDynTraitTypeParameter(n)
|
||||
)
|
||||
}
|
||||
|
||||
Trait getTrait() { result = trait }
|
||||
|
||||
override string toString() { result = "dyn " + trait.getName().toString() }
|
||||
@@ -336,7 +364,7 @@ class SliceType extends Type, TSliceType {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) {
|
||||
override TypeParameter getPositionalTypeParameter(int i) {
|
||||
result = TSliceTypeParameter() and
|
||||
i = 0
|
||||
}
|
||||
@@ -352,7 +380,7 @@ abstract class TypeParameter extends Type {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) { none() }
|
||||
override TypeParameter getPositionalTypeParameter(int i) { none() }
|
||||
}
|
||||
|
||||
private class RawTypeParameter = @type_param or @trait or @type_alias or @impl_trait_type_repr;
|
||||
@@ -548,7 +576,7 @@ class ImplTraitTypeTypeParameter extends ImplTraitType, TypeParameter {
|
||||
|
||||
override TupleField getTupleField(int i) { none() }
|
||||
|
||||
override TypeParameter getTypeParameter(int i) { none() }
|
||||
override TypeParameter getPositionalTypeParameter(int i) { none() }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -374,6 +374,20 @@ private module CertainTypeInference {
|
||||
or
|
||||
result = inferLiteralType(n, path, true)
|
||||
or
|
||||
result = inferRefNodeType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferTupleRootType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferAsyncBlockExprRootType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferArrayExprType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferCastExprType(n, path)
|
||||
or
|
||||
infersCertainTypeAt(n, path, result.getATypeParameter())
|
||||
}
|
||||
|
||||
@@ -405,7 +419,7 @@ private module CertainTypeInference {
|
||||
inferCertainType(n, path) != t
|
||||
or
|
||||
// If we infer that `n` has _some_ type at `T1.T2....Tn`, and we also
|
||||
// know that `n` certainly has type `certainType` at `T1.T2...Ti`, `i <=0 < n`,
|
||||
// know that `n` certainly has type `certainType` at `T1.T2...Ti`, `0 <= i < n`,
|
||||
// then it must be the case that `T(i+1)` is a type parameter of `certainType`,
|
||||
// otherwise there is a conflict.
|
||||
//
|
||||
@@ -2366,30 +2380,18 @@ private module Cached {
|
||||
or
|
||||
result = inferStructExprType(n, path)
|
||||
or
|
||||
result = inferTupleRootType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferPathExprType(n, path)
|
||||
or
|
||||
result = inferCallExprBaseType(n, path)
|
||||
or
|
||||
result = inferFieldExprType(n, path)
|
||||
or
|
||||
result = inferRefNodeType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferTryExprType(n, path)
|
||||
or
|
||||
result = inferLiteralType(n, path, false)
|
||||
or
|
||||
result = inferAsyncBlockExprRootType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferAwaitExprType(n, path)
|
||||
or
|
||||
result = inferArrayExprType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
result = inferRangeExprType(n) and
|
||||
path.isEmpty()
|
||||
or
|
||||
@@ -2401,8 +2403,6 @@ private module Cached {
|
||||
or
|
||||
result = inferClosureExprType(n, path)
|
||||
or
|
||||
result = inferCastExprType(n, path)
|
||||
or
|
||||
result = inferStructPatType(n, path)
|
||||
or
|
||||
result = inferTupleStructPatType(n, path)
|
||||
|
||||
@@ -182,7 +182,7 @@ class NonAliasPathTypeMention extends PathTypeMention {
|
||||
private TypeMention getTypeMentionForTypeParameter(TypeParameter tp) {
|
||||
exists(int i |
|
||||
result = this.getPositionalTypeArgument(pragma[only_bind_into](i)) and
|
||||
tp = this.resolveRootType().getTypeParameter(pragma[only_bind_into](i))
|
||||
tp = this.resolveRootType().getPositionalTypeParameter(pragma[only_bind_into](i))
|
||||
)
|
||||
or
|
||||
exists(TypeAlias alias |
|
||||
|
||||
@@ -116,7 +116,7 @@ SourceFile/gen_source_file.rs c0469cc8f0ecce3dd2e77963216d7e8808046014533359a44c
|
||||
Static/gen_static.rs 21314018ea184c1ddcb594d67bab97ae18ceaf663d9f120f39ff755d389dde7a 21314018ea184c1ddcb594d67bab97ae18ceaf663d9f120f39ff755d389dde7a
|
||||
StmtList/gen_stmt_list.rs adbd82045a50e2051434ce3cdd524c9f2c6ad9f3dd02b4766fb107e2e99212db adbd82045a50e2051434ce3cdd524c9f2c6ad9f3dd02b4766fb107e2e99212db
|
||||
Struct/gen_struct.rs 5e181e90075f716c04c75e4ef0334abe3d5f419cd9ccfadfe595c09fab33566b 5e181e90075f716c04c75e4ef0334abe3d5f419cd9ccfadfe595c09fab33566b
|
||||
StructExpr/gen_struct_expr.rs 8dd9a578625a88623c725b8afdfd8b636e1c3c991fe96c55b24d4b283d2212fb 8dd9a578625a88623c725b8afdfd8b636e1c3c991fe96c55b24d4b283d2212fb
|
||||
StructExpr/gen_struct_expr.rs e7824008b0b73d02f6243fd8a18e0ef93c63bfe775a878fc2679c3870fc342fd e7824008b0b73d02f6243fd8a18e0ef93c63bfe775a878fc2679c3870fc342fd
|
||||
StructExprField/gen_struct_expr_field.rs 4ccca8e8ad462b4873f5604f0afdd1836027b8d39e36fbe7d6624ef3e744a084 4ccca8e8ad462b4873f5604f0afdd1836027b8d39e36fbe7d6624ef3e744a084
|
||||
StructExprFieldList/gen_struct_expr_field_list.rs bc17e356690cfb1cd51f7dbe5ac88c0b9188c9bf94b9e3cc82581120190f33dc bc17e356690cfb1cd51f7dbe5ac88c0b9188c9bf94b9e3cc82581120190f33dc
|
||||
StructField/gen_struct_field.rs 0884e458732ab74b4c7debee4fbef014f847815be5a6ddeba467844d33607c6e 0884e458732ab74b4c7debee4fbef014f847815be5a6ddeba467844d33607c6e
|
||||
@@ -128,7 +128,7 @@ TokenTree/gen_token_tree.rs 3fdc9a36a1870bb2bedf66c8fe37d368f4ac18488e7118b86e39
|
||||
Trait/gen_trait.rs bac694993e224f9c6dd86cfb28c54846ae1b3bae45a1e58d3149c884184487ea bac694993e224f9c6dd86cfb28c54846ae1b3bae45a1e58d3149c884184487ea
|
||||
TraitAlias/gen_trait_alias.rs 425d78a7cb87db7737ceaf713c9a62e0411537374d1bc58c5b1fb80cc25732c9 425d78a7cb87db7737ceaf713c9a62e0411537374d1bc58c5b1fb80cc25732c9
|
||||
TryExpr/gen_try_expr.rs f60198181a423661f4ed1bf6f98d475f40ada190b7b5fc6af97aa5e45ca29a1e f60198181a423661f4ed1bf6f98d475f40ada190b7b5fc6af97aa5e45ca29a1e
|
||||
TupleExpr/gen_tuple_expr.rs 8ecd1b6ecc58a0319eed434a423cc6f41bdf1901b1950e6e79735d7f7b2f8374 8ecd1b6ecc58a0319eed434a423cc6f41bdf1901b1950e6e79735d7f7b2f8374
|
||||
TupleExpr/gen_tuple_expr.rs 27e56846b3f08c37c8a345169c2a532b2023d231d46a5bdf586bbc6d8fb36a01 27e56846b3f08c37c8a345169c2a532b2023d231d46a5bdf586bbc6d8fb36a01
|
||||
TupleField/gen_tuple_field.rs 5d6b4f356af895541f975cc1fd90116fd047fe914c2049d47f61e4a43a8c2af4 5d6b4f356af895541f975cc1fd90116fd047fe914c2049d47f61e4a43a8c2af4
|
||||
TupleFieldList/gen_tuple_field_list.rs 42f0af8c391fb9e33fe09b791e0e719cadf5143b58764f8a5d38f8d9054daca7 42f0af8c391fb9e33fe09b791e0e719cadf5143b58764f8a5d38f8d9054daca7
|
||||
TuplePat/gen_tuple_pat.rs b1b0c9c5ff1b787f380644691c77807655a4f6441fc7431c90ecf78c54c26148 b1b0c9c5ff1b787f380644691c77807655a4f6441fc7431c90ecf78c54c26148
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
instances
|
||||
| gen_struct_expr.rs:5:17:5:34 | Foo {...} |
|
||||
| gen_struct_expr.rs:6:18:6:38 | Foo {...} |
|
||||
| gen_struct_expr.rs:7:5:7:22 | Foo {...} |
|
||||
| gen_struct_expr.rs:8:5:8:14 | Foo {...} |
|
||||
| gen_struct_expr.rs:7:13:7:30 | Foo {...} |
|
||||
| gen_struct_expr.rs:8:5:8:20 | Foo {...} |
|
||||
getPath
|
||||
| gen_struct_expr.rs:5:17:5:34 | Foo {...} | gen_struct_expr.rs:5:17:5:19 | Foo |
|
||||
| gen_struct_expr.rs:6:18:6:38 | Foo {...} | gen_struct_expr.rs:6:18:6:20 | Foo |
|
||||
| gen_struct_expr.rs:7:5:7:22 | Foo {...} | gen_struct_expr.rs:7:5:7:7 | Foo |
|
||||
| gen_struct_expr.rs:8:5:8:14 | Foo {...} | gen_struct_expr.rs:8:5:8:7 | Foo |
|
||||
| gen_struct_expr.rs:7:13:7:30 | Foo {...} | gen_struct_expr.rs:7:13:7:15 | Foo |
|
||||
| gen_struct_expr.rs:8:5:8:20 | Foo {...} | gen_struct_expr.rs:8:5:8:7 | Foo |
|
||||
getStructExprFieldList
|
||||
| gen_struct_expr.rs:5:17:5:34 | Foo {...} | gen_struct_expr.rs:5:21:5:34 | StructExprFieldList |
|
||||
| gen_struct_expr.rs:6:18:6:38 | Foo {...} | gen_struct_expr.rs:6:22:6:38 | StructExprFieldList |
|
||||
| gen_struct_expr.rs:7:5:7:22 | Foo {...} | gen_struct_expr.rs:7:9:7:22 | StructExprFieldList |
|
||||
| gen_struct_expr.rs:8:5:8:14 | Foo {...} | gen_struct_expr.rs:8:9:8:14 | StructExprFieldList |
|
||||
| gen_struct_expr.rs:7:13:7:30 | Foo {...} | gen_struct_expr.rs:7:17:7:30 | StructExprFieldList |
|
||||
| gen_struct_expr.rs:8:5:8:20 | Foo {...} | gen_struct_expr.rs:8:9:8:20 | StructExprFieldList |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user