Issues (11)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/WebMoneyMerchant.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace ActionM\WebMoneyMerchant;
4
5
use Illuminate\Http\Request;
6
use Illuminate\Support\Facades\Validator;
7
use ActionM\WebMoneyMerchant\Events\WebMoneyMerchantEvent;
8
use ActionM\WebMoneyMerchant\Exceptions\InvalidConfiguration;
9
10
class WebMoneyMerchant
11
{
12
    public function __construct()
13
    {
14
    }
15
16
    /**
17
     * Allow the access, if the ip address is in the whitelist.
18
     * @param $ip
19
     * @return bool
20
     */
21
    public function allowIP($ip)
22
    {
23
        // Allow the local ip or any other ip address
24
        if ($ip == '127.0.0.1' || in_array('*', config('webmoney-merchant.allowed_ips'))) {
25
            return true;
26
        }
27
28
        return in_array($ip, config('webmoney-merchant.allowed_ips'));
29
    }
30
31
    /**
32
     * Generates the '403' error code.
33
     * @param $message
34
     * @return mixed
35
     */
36
    public function responseError($message)
37
    {
38
        return abort(403, $message);
39
    }
40
41
    /**
42
     * Returns the 'YES' success message.
43
     * @return string
44
     */
45
    public function responseOK()
46
    {
47
        return 'YES';
48
    }
49
50
    /**
51
     * Fills in the event details to pass the title and request params as array.
52
     * @param $event_type
53
     * @param $event_title
54
     * @param Request $request
55
     */
56
    public function eventFillAndSend($event_type, $event_title, Request $request)
57
    {
58
        $event_details = [
59
            'title' => 'WebMoneyMerchant: '.$event_title,
60
            'ip' => $request->ip(),
61
            'request' => $request->all(),
62
        ];
63
64
        event(
65
            new WebMoneyMerchantEvent($event_type, $event_details)
66
        );
67
    }
68
69
    /**
70
     * Calculates the signature for the order form.
71
     * @param $LMI_PAYMENT_AMOUNT
72
     * @param $LMI_PAYMENT_NO
73
     * @return string
74
     */
75
    public function getFormSignature($LMI_PAYMENT_AMOUNT, $LMI_PAYMENT_NO)
76
    {
77
        $hashStr = config('webmoney-merchant.WM_LMI_PAYEE_PURSE').';'.$LMI_PAYMENT_AMOUNT.';'.$LMI_PAYMENT_NO.';'.config('webmoney-merchant.WM_LMI_SECRET_X20').';';
78
79
        return hash('sha256', $hashStr);
80
    }
81
82
    /**
83
     * Returns the hash for the params from WebMoneyMerchant.
84
     * @param Request $request
85
     * @return string
86
     */
87
    public function getSignature(Request $request)
88
    {
89
        $hashStr =
90
            $request->get('LMI_PAYEE_PURSE').
91
            $request->get('LMI_PAYMENT_AMOUNT').
92
            $request->get('LMI_PAYMENT_NO').
93
            $request->get('LMI_MODE').
94
            $request->get('LMI_SYS_INVS_NO').
95
            $request->get('LMI_SYS_TRANS_NO').
96
            $request->get('LMI_SYS_TRANS_DATE').
97
            config('webmoney-merchant.WM_LMI_SECRET_KEY').
98
            $request->get('LMI_PAYER_PURSE').
99
            $request->get('LMI_PAYER_WM');
100
101
        return hash('sha256', $hashStr);
102
    }
103
104
    /**
105
     * Generates the order array with required fields for the order form.
106
     * @param $payment_amount
107
     * @param $payment_no
108
     * @param $item_name
109
     * @return array
110
     */
111
    public function generateWebMoneyMerchantOrderWithRequiredFields($payment_amount, $payment_no, $item_name)
112
    {
113
        $order = [
114
            'PAYMENT_AMOUNT' => $payment_amount,
115
            'PAYMENT_NO' => $payment_no,
116
            'ITEM_NAME' => base64_encode($item_name),
117
        ];
118
119
        $this->requiredOrderParamsCheck($order);
120
121
        return $order;
122
    }
123
124
    /**
125
     * Checks required order params for the order form and raise an exception if it fails.
126
     * @param $order
127
     * @throws InvalidConfiguration
128
     */
129
    public function requiredOrderParamsCheck($order)
130
    {
131
        $required_fields = [
132
            'PAYMENT_AMOUNT',
133
            'PAYMENT_NO',
134
            'ITEM_NAME',
135
        ];
136
137
        foreach ($required_fields as $key => $value) {
138
            if (! array_key_exists($value, $order) || empty($order[$value])) {
139
                throw InvalidConfiguration::generatePaymentFormOrderParamsNotSet($value);
140
            }
141
        }
142
143
        // Checks if PAYMENT_NO is numeric.
144
        if (! is_numeric($order['PAYMENT_NO'])) {
145
            throw InvalidConfiguration::generatePaymentFormOrderInvalidPaymentNo('PAYMENT_NO');
146
        }
147
148
        // Checks if PAYMENT_NO > 0 and < 2147483647
149
        if (intval($order['PAYMENT_NO']) < 1 || intval($order['PAYMENT_NO']) > 2147483647) {
150
            throw InvalidConfiguration::generatePaymentFormOrderInvalidPaymentNo($order['PAYMENT_NO']);
151
        }
152
    }
153
154
    /**
155
     * Generates html forms from view with payment buttons
156
     * Note: you can customise the view via artisan:publish.
157
     * @param $payment_amount
158
     * @param $payment_no
159
     * @param $item_name
160
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
161
     */
162
    public function generatePaymentForm($payment_amount, $payment_no, $item_name)
163
    {
164
        $order = $this->generateWebMoneyMerchantOrderWithRequiredFields($payment_amount, $payment_no, $item_name);
165
166
        $this->requiredOrderParamsCheck($order);
167
168
        /* WM Merchant accepts windows-1251; use only latin characters for the product name*/
169
        $payment_fields = [];
170
        $payment_fields['LMI_PAYMENT_AMOUNT'] = $order['PAYMENT_AMOUNT'];
171
        $payment_fields['LMI_PAYMENT_NO'] = $order['PAYMENT_NO'];
172
        $payment_fields['LMI_PAYMENT_DESC_BASE64'] = $order['ITEM_NAME'];
173
        $payment_fields['LOCALE'] = config('webmoney-merchant.locale');
174
        $payment_fields['LMI_PAYEE_PURSE'] = config('webmoney-merchant.WM_LMI_PAYEE_PURSE');
175
        $payment_fields['LMI_PAYMENTFORM_SIGN'] = $this->getFormSignature($payment_fields['LMI_PAYMENT_AMOUNT'], $payment_fields['LMI_PAYMENT_NO']);
176
177
        return view('webmoney-merchant::payment_form', compact('payment_fields'));
178
    }
179
180
    /**
181
     * Validates the request params from WebMoneyMerchant.
182
     * @param Request $request
183
     * @return bool
184
     */
185
    public function validate(Request $request)
186
    {
187
        $validator = Validator::make($request->all(), [
188
            'LMI_PAYEE_PURSE' => 'required',
189
            'LMI_PAYMENT_AMOUNT' => 'required',
190
            'LMI_PAYMENT_NO' => 'required',
191
            'LMI_PAYER_IP' => 'required',
192
            'LMI_HASH' => 'required',
193
            'LMI_HASH2' => 'required',
194
        ]);
195
196
        if ($validator->fails()) {
197
            return false;
198
        }
199
200
        return true;
201
    }
202
203
    /**
204
     * Validates the payee purse from WebMoneyMerchant.
205
     * @param Request $request
206
     * @return bool
207
     */
208
    public function validatePayeePurse(Request $request)
209
    {
210
        if ($request->get('LMI_PAYEE_PURSE') != config('webmoney-merchant.WM_LMI_PAYEE_PURSE')) {
211
            return false;
212
        }
213
214
        return true;
215
    }
216
217
    /**
218
     * Validates the request signature from WebMoneyMerchant.
219
     * @param Request $request
220
     * @return bool
221
     */
222
    public function validateSignature(Request $request)
223
    {
224
        $sign = $this->getSignature($request);
225
226
        if (mb_strtoupper($request->get('LMI_HASH')) != mb_strtoupper($sign)) {
227
            return false;
228
        }
229
230
        return true;
231
    }
232
233
    /**
234
     * Validates the allowed ip, request params and signature from WebMoneyMerchant.
235
     * @param Request $request
236
     * @return bool
237
     */
238
    public function validateOrderRequestFromGate(Request $request)
239
    {
240
        if (! $this->AllowIP($request->ip()) || ! $this->validate($request) || ! $this->validatePayeePurse($request) || ! $this->validateSignature($request)) {
241
            $this->eventFillAndSend('webmoneymerchant.error', 'validateOrderRequestFromGate', $request);
242
243
            return false;
244
        }
245
246
        return true;
247
    }
248
249
    /**
250
     * Validates the required attributes of the found order.
251
     * @param Request $request
252
     * @param $order
253
     * @return bool
254
     */
255
    public function validateSearchOrderRequiredAttributes(Request $request, $order)
256
    {
257
        if (! $order) {
258
            $this->eventFillAndSend('webmoneymerchant.error', 'orderNotFound', $request);
259
260
            return false;
261
        }
262
263
        // Checks required found order attributes.
264
        $attr = ['WEBMONEY_orderStatus', 'WEBMONEY_orderSum'];
265
266
        foreach ($attr as $k => $value) {
267
            if (! $order->getAttribute($value)) {
268
                $this->eventFillAndSend('webmoneymerchant.error', $value.'Invalid', $request);
269
270
                return false;
271
            }
272
        }
273
274
        // Compares order attributes with request params.
275
        if ($order->getAttribute('WEBMONEY_orderSum') != $request->input('LMI_PAYMENT_AMOUNT')) {
276
            $this->eventFillAndSend('webmoneymerchant.error', $value.'Invalid', $request);
0 ignored issues
show
The variable $value seems to be defined by a foreach iteration on line 266. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
277
278
            return false;
279
        }
280
281
        return true;
282
    }
283
284
    /**
285
     * Calls SearchOrderFilter and check return order params.
286
     * @param Request $request
287
     * @return bool
288
     * @throws InvalidConfiguration
289
     */
290
    public function callFilterSearchOrder(Request $request)
291
    {
292
        $callable = config('webmoney-merchant.searchOrderFilter');
293
294
        if (! is_callable($callable)) {
295
            throw InvalidConfiguration::searchOrderFilterInvalid();
296
        }
297
298
        /*
299
         *  SearchOrderFilter
300
         *  Searches the order in the database and return the order details.
301
         *  Must return the array with:
302
         *
303
         *  orderStatus
304
         *  orderSum
305
         */
306
307
        $order = $callable($request, $request->input('LMI_PAYMENT_NO'));
308
309
        if (! $this->validateSearchOrderRequiredAttributes($request, $order)) {
310
            return false;
311
        }
312
313
        return $order;
314
    }
315
316
    /**
317
     * Calls PaidOrderFilter if the order is not paid.
318
     * @param Request $request
319
     * @param $order
320
     * @return mixed
321
     * @throws InvalidConfiguration
322
     */
323
    public function callFilterPaidOrder(Request $request, $order)
324
    {
325
        $callable = config('webmoney-merchant.paidOrderFilter');
326
327
        if (! is_callable($callable)) {
328
            throw InvalidConfiguration::orderPaidFilterInvalid();
329
        }
330
331
        // Unset the custom order attributes; for Eloquent support.
332
        unset($order['WEBMONEY_orderSum'], $order['WEBMONEY_orderStatus']);
333
334
        // Runs the `PaidOrderFilter` callback.
335
        return $callable($request, $order);
336
    }
337
338
    /**
339
     * Runs WebMoneyMerchant::payOrderFromGate($request) when the request from WebMoney Merchant has been received.
340
     * @param Request $request
341
     * @return mixed
342
     */
343
    public function payOrderFromGate(Request $request)
344
    {
345
        if (! $request->has('LMI_HASH')) {
346
            return 'OK';
347
        }
348
349
        if ($request->has('LMI_PREREQUEST')) {
350
            return 'YES';
351
        }
352
353
        // Validates the request params from the WebMoney Merchant server.
354
        if (! $this->validateOrderRequestFromGate($request)) {
355
            $this->eventFillAndSend('webmoneymerchant.error', 'validateOrderRequestFromGate', $request);
356
357
            return $this->responseError('validateOrderRequestFromGate');
358
        }
359
360
        // Searches and returns the order
361
        $order = $this->callFilterSearchOrder($request);
362
363
        if (! $order) {
364
            $this->eventFillAndSend('webmoneymerchant.error', 'searchOrderFilter', $request);
365
366
            return $this->responseError('searchOrderFilter');
367
        }
368
369
        // If the current order status is `paid`.
370
        // Sends the notification and returns the success response.
371
        if (mb_strtolower($order->WEBMONEY_orderStatus) === 'paid') {
372
            $this->eventFillAndSend('webmoneymerchant.info', 'The order is already paid', $request);
373
374
            return $this->responseError('The order is already paid');
375
        }
376
377
        // The current order is paid on WebMoney Merchant and not paid in the database.
378
379
        $this->eventFillAndSend('webmoneymerchant.success', 'paid order', $request);
380
381
        // PaidOrderFilter - update the order into the DB as paid & other actions.
382
        // If it returns false, then some error has occurred.
383
        if (! $this->callFilterPaidOrder($request, $order)) {
384
            $this->eventFillAndSend('webmoneymerchant.error', 'callFilterPaidOrder', $request);
385
386
            return $this->responseError('callFilterPaidOrder');
387
        }
388
389
        // The order is paid on WebMoney Merchant and updated in the database.
390
        return $this->responseOK();
391
    }
392
}
393