1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 #include "squid.h"
10 #include "anyp/Uri.h"
11 #include "CacheManager.h"
12 #include "mgr/Action.h"
13 #include "Store.h"
14 #include "testCacheManager.h"
15 #include "unitTestMain.h"
16 
17 #include <cppunit/TestAssert.h>
18 
19 CPPUNIT_TEST_SUITE_REGISTRATION( testCacheManager );
20 
21 /// Provides test code access to CacheManager internal symbols
22 class CacheManagerInternals : public CacheManager
23 {
24 public:
ParseUrl(const AnyP::Uri & u)25     void ParseUrl(const AnyP::Uri &u) { CacheManager::ParseUrl(u); }
26 };
27 
28 /* init memory pools */
29 
setUp()30 void testCacheManager::setUp()
31 {
32     Mem::Init();
33     AnyP::UriScheme::Init();
34 }
35 
36 /*
37  * Test creating a CacheManager
38  */
39 void
testCreate()40 testCacheManager::testCreate()
41 {
42     CacheManager::GetInstance(); //it's a singleton..
43 }
44 
45 /* an action to register */
46 static void
dummy_action(StoreEntry * sentry)47 dummy_action(StoreEntry * sentry)
48 {
49     sentry->flags=1;
50 }
51 
52 /*
53  * registering an action makes it findable.
54  */
55 void
testRegister()56 testCacheManager::testRegister()
57 {
58     CacheManager *manager=CacheManager::GetInstance();
59     CPPUNIT_ASSERT(manager != NULL);
60 
61     manager->registerProfile("sample", "my sample", &dummy_action, false, false);
62     Mgr::Action::Pointer action = manager->createNamedAction("sample");
63     CPPUNIT_ASSERT(action != NULL);
64 
65     const Mgr::ActionProfile::Pointer profile = action->command().profile;
66     CPPUNIT_ASSERT(profile != NULL);
67     CPPUNIT_ASSERT(profile->creator != NULL);
68     CPPUNIT_ASSERT_EQUAL(false, profile->isPwReq);
69     CPPUNIT_ASSERT_EQUAL(false, profile->isAtomic);
70     CPPUNIT_ASSERT_EQUAL(String("sample"), String(action->name()));
71 
72     StoreEntry *sentry=new StoreEntry();
73     sentry->flags=0x25; //arbitrary test value
74     action->run(sentry, false);
75     CPPUNIT_ASSERT_EQUAL(1,(int)sentry->flags);
76 }
77 
78 void
testParseUrl()79 testCacheManager::testParseUrl()
80 {
81     auto *mgr = static_cast<CacheManagerInternals *>(CacheManager::GetInstance());
82     CPPUNIT_ASSERT(mgr != nullptr);
83 
84     std::vector<AnyP::ProtocolType> validSchemes = {
85         AnyP::PROTO_CACHE_OBJECT,
86         AnyP::PROTO_HTTP,
87         AnyP::PROTO_HTTPS,
88         AnyP::PROTO_FTP
89     };
90 
91     AnyP::Uri mgrUrl;
92     mgrUrl.host("localhost");
93     mgrUrl.port(3128);
94 
95     const std::vector<const char *> magicPrefixes = {
96         "/",
97         "/squid-internal-mgr/"
98     };
99 
100     const std::vector<const char *> validActions = {
101         "",
102         "menu"
103     };
104 
105     const std::vector<const char *> invalidActions = {
106         "INVALID" // any unregistered name
107     };
108 
109     const std::vector<const char *> validParams = {
110         "",
111         "?",
112         "?&",
113         "?&&&&&&&&&&&&",
114         "?foo=bar",
115         "?0123456789=bar",
116         "?foo=bar&",
117         "?foo=bar&&&&",
118         "?&foo=bar",
119         "?&&&&foo=bar",
120         "?&foo=bar&",
121         "?&&&&foo=bar&&&&",
122         "?foo=?_weird?~`:[]stuff&bar=okay&&&&&&",
123         "?intlist=1",
124         "?intlist=1,2,3,4,5",
125         "?string=1a",
126         "?string=1,2,3,4,z",
127         "?string=1,2,3,4,[0]",
128         "?intlist=1,2,3,4,5&string=1,2,3,4,y"
129     };
130 
131     const std::vector<const char *> invalidParams = {
132         "?/",
133         "?foo",
134         "?/foo",
135         "?foo/",
136         "?foo=",
137         "?foo=&",
138         "?=foo",
139         "? foo=bar",
140         "? &",
141         "?& ",
142         "?=&",
143         "?&=",
144         "? &&&",
145         "?& &&",
146         "?&& &",
147         "?=&&&",
148         "?&=&&",
149         "?&&=&"
150     };
151 
152     const std::vector<const char *> validFragments = {
153         "",
154         "#",
155         "##",
156         "#?a=b",
157         "#fragment"
158     };
159 
160     for (const auto &scheme : validSchemes) {
161         mgrUrl.setScheme(scheme);
162 
163         for (const auto *magic : magicPrefixes) {
164 
165             // all schemes except cache_object require magic path prefix bytes
166             if (scheme != AnyP::PROTO_CACHE_OBJECT && strlen(magic) <= 2)
167                 continue;
168 
169             /* Check the parser accepts all the valid cases */
170 
171             for (const auto *action : validActions) {
172                 for (const auto *param : validParams) {
173                     for (const auto *frag : validFragments) {
174                         try {
175                             SBuf bits;
176                             bits.append(magic);
177                             bits.append(action);
178                             bits.append(param);
179                             bits.append(frag);
180                             mgrUrl.path(bits);
181 
182                             (void)mgr->ParseUrl(mgrUrl);
183                         } catch (...) {
184                             std::cerr << std::endl
185                                       << "FAIL: " << mgrUrl
186                                       << Debug::Extra << "error: " << CurrentException << std::endl;
187                             CPPUNIT_FAIL("rejected a valid URL");
188                         }
189                     }
190                 }
191             }
192 
193             /* Check that invalid parameters are rejected */
194 
195             for (const auto *action : validActions) {
196                 for (const auto *param : invalidParams) {
197                     for (const auto *frag : validFragments) {
198                         try {
199                             SBuf bits;
200                             bits.append(magic);
201                             bits.append(action);
202                             bits.append(param);
203                             bits.append(frag);
204                             mgrUrl.path(bits);
205 
206                             (void)mgr->ParseUrl(mgrUrl);
207 
208                             std::cerr << std::endl
209                                       << "FAIL: " << mgrUrl
210                                       << Debug::Extra << "error: should be rejected due to '" << param << "'" << std::endl;
211                         } catch (const TextException &e) {
212                             continue; // success. caught bad input
213                         }
214                         CPPUNIT_FAIL("failed to reject an invalid URL");
215                     }
216                 }
217             }
218         }
219     }
220 }
221