first commit

This commit is contained in:
2024-11-05 12:22:50 +01:00
commit e5682a3912
19641 changed files with 2948548 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,8 @@
version: 1.x-{build}
build: false
clone_depth: 2
clone_folder: c:\projects\sentry-php
skip_branch_with_pr: true
branches:
only:
- master

View File

@@ -0,0 +1,12 @@
<?php
$finder = Symfony\CS\Finder\DefaultFinder::create()
->in(__DIR__)
;
return Symfony\CS\Config\Config::create()
->setUsingCache(true)
->setUsingLinter(true)
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
->finder($finder)
;

View File

@@ -0,0 +1,19 @@
tools:
php_sim: false
php_pdepend: true
php_analyzer: true
php_code_coverage: true
external_code_coverage:
timeout: 2400 # There can be another pull request in progress
runs: 7 # PHP 5.3 + PHP 5.4 + PHP 5.5 + PHP 5.6 + PHP 7.0 + PHP 7.1 + PHP 7.2
build:
environment:
php:
version: 5.6.0
redis: false
postgresql: false
mongodb: false
filter:
excluded_paths: [vendor/*, test/*, bin/*, docs/*, examples/*]

View File

@@ -0,0 +1,69 @@
language: php
sudo: false
dist: trusty
php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- 7.2
- nightly
env:
matrix:
- REMOVE_XDEBUG="0"
- REMOVE_XDEBUG="1"
global:
- NODE_ENV=production
- TRAVIS_NODE_VERSION=8.9.1
matrix:
allow_failures:
- php: hhvm-3.12
- php: nightly
fast_finish: true
include:
- php: hhvm-3.12
env: REMOVE_XDEBUG="0" HHVM="1"
- php: 5.3
env: REMOVE_XDEBUG="0"
dist: precise
- php: 5.3
env: REMOVE_XDEBUG="1"
dist: precise
exclude:
- php: nightly
env: REMOVE_XDEBUG="1"
cache:
directories:
- $HOME/.composer/cache
before_install:
- if [ "$REMOVE_XDEBUG" = "1" ]; then phpenv config-rm xdebug.ini; fi
- composer self-update
install:
- nvm install $TRAVIS_NODE_VERSION
- travis_retry composer install --no-interaction --prefer-dist
script:
- composer phpcs
- composer tests-travis
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- if [ $(phpenv version-name) = "5.3" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
- if [ $(phpenv version-name) = "5.4" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
- if [ $(phpenv version-name) = "5.5" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
- if [ $(phpenv version-name) = "5.6" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
- if [ $(phpenv version-name) = "7.0" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
- if [ $(phpenv version-name) = "7.1" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
- if [ $(phpenv version-name) = "7.2" ] && [ "$REMOVE_XDEBUG" = "0" ]; then php ocular.phar code-coverage:upload --format=php-clover test/clover.xml --revision=$TRAVIS_COMMIT; fi
- npm install -g @zeus-ci/cli
- $(npm bin -g)/zeus upload -t "application/x-junit+xml" test/junit.xml
- $(npm bin -g)/zeus upload -t "application/x-clover+xml" test/clover.xml
notifications:
webhooks:
urls:
- https://zeus.ci/hooks/cf8597c4-ffba-11e7-89c9-0a580a281308/public/provider/travis/webhook
on_success: always
on_failure: always
on_start: always
on_cancel: always
on_error: always

View File

@@ -0,0 +1,4 @@
The Sentry PHP SDK was originally written by Michael van Tellingen
and is maintained by the Sentry Team.
http://github.com/getsentry/sentry-php/contributors

View File

@@ -0,0 +1,248 @@
# CHANGELOG
## 1.11.0 (2020-02-12)
- Fixed array and string offset access syntax with curly braces deprecations (#975)
- Fixed curl verify host for synchronous mode (#767)
- Use `mb_substr` instead of `substr` if available (#734)
- Make it possible to change `default_max_depth` in `Raven_Serializer` (#632)
## 1.10.0 (2018-11-09)
- Added passing data from context in monolog breadcrumb handler (#683)
- Do not return error id if we know we did not send the error (#667)
- Do not force IPv4 protocol by default (#654)
## 1.9.2 (2018-08-17)
- Remove secret_key from required keys for CLI test command. (#645)
- Proper case in Raven_Util class name usage. (#642)
- Support longer creditcard numbers. (#635)
- Use configured message limit when creating serializers. (#634)
- Do not truncate strings if message limit is set to zero. (#630)
- Add option to ignore SERVER_PORT getting added to url. (#629)
- Cleanup the PHP version reported. (#604)
## 1.9.1 (2018-06-19)
- Allow the use of a public DSN (private part of the DSN was deprecated in Sentry 9) (#615)
- Send transaction as transaction not as culprit (#601)
## 1.9.0 (2018-05-03)
- Fixed undefined variable (#588)
- Fix for exceptions throwing exceptions when setting event id (#587)
- Fix monolog handler not accepting Throwable (#586)
- Add `excluded_exceptions` option to exclude exceptions and their extending exceptions (#583)
- Fix `HTTP_X_FORWARDED_PROTO` header detection (#578)
- Fix sending events async in PHP 5 (#576)
- Avoid double reporting due to `ErrorException`s (#574)
- Make it possible to overwrite serializer message limit of 1024 (#559)
- Allow request data to be nested up to 5 levels deep (#554)
- Update serializer to handle UTF-8 characters correctly (#553)
## 1.8.4 (2018-03-20)
- Revert ignoring fatal errors on PHP 7+ (#571)
- Add PHP runtime information (#564)
- Cleanup the `site` value if it's empty (#555)
- Add `application/json` input handling (#546)
## 1.8.3 (2018-02-07)
- Serialize breadcrumbs to prevent issues with binary data (#538)
- Fix notice array_key_exists() expects parameter 2 to be array, null given (#527)
## 1.8.2 (2017-12-21)
- Improve handling DSN with "null" like values (#522)
- Prevent warning in Raven_Stacktrace (#493)
## 1.8.1 (2017-11-09)
- Add setters for the serializers on the `Raven_Client` (#515)
- Avoid to capture `E_ERROR` in PHP 7+, because it's also a `Throwable` that gets captured and duplicates the error (#514)
## 1.8.0 (2017-10-29)
- Use namespaced classes in test for PHPUnit (#506)
- Prevent segmentation fault on PHP `<5.6` (#504)
- Remove `ini_set` call for unneeded functionality (#501)
- Exclude single `.php` files from the app path (#500)
- Start testing PHP 7.2 (#489)
- Exclude anonymous frames from app path (#482)
## 1.7.1 (2017-08-02)
- Fix of filtering sensitive data when there is an exception with multiple 'values' (#483)
## 1.7.0 (2017-06-07)
- Corrected some issues with argument serialization in stacktraces (#399).
- The default exception handler will now re-raise exceptions when `call_existing` is true and no exception handler is registered (#421).
- Collect `User.ip_address` automatically (#419).
- Added a processor to remove web cookies. It will be enabled by default in `2.0` (#405).
- Added a processor to remove HTTP body data for POST, PUT, PATCH and DELETE requests. It will be enabled by default in `2.0` (#405).
- Added a processor to sanitize HTTP headers (e.g. the Authorization header) (#428).
- Added a processor to remove `pre_context`, `context_line` and `post_context` informations from reported exceptions (#429).
## 1.6.2 (2017-02-03)
- Fixed behavior where fatal errors weren't correctly being reported in most situations.
## 1.6.1 (2016-12-14)
- Correct handling of null in `user_context`.
## 1.6.0 (2016-12-09)
- Improved serialization of certain types to be more restrictive.
- `error_types` can now be configured via `RavenClient`.
- Class serialization has been expanded to include attributes.
- The session extension is no longer required.
- Monolog is no longer a required dependency.
- `user_context` now merges by default.
## 1.5.0 (2016-09-29)
- Added named transaction support.
## 1.4.0 (2016-09-20)
This version primarily overhauls the exception/stacktrace generation to fix
a few bugs and improve the quality of data (#359).
- Added `excluded_app_paths` config.
- Removed `shift_vars` config.
- Correct fatal error handling to only operate on expected types. This also fixes some behavior with the error suppression operator.
- Expose anonymous and similar frames in the stacktrace.
- Default `prefixes` to PHP's include paths.
- Remove `module` usage.
- Better handle empty argument context.
- Correct alignment of filename (current frame) and function (caller frame)
## 1.3.0 (2016-12-19)
- Fixed an issue causing the error suppression operator to not be respected (#335)
- Fixed some serialization behavior (#352)
- Fixed an issue with app paths and trailing slashes (#350)
- Handle non-latin encoding with source code context line (#345)
## 1.2.0 (2016-12-08)
- Handle non-latin encoding in source code and exception values (#342)
- Ensure pending events are sent on shutdown by default (#338)
- Add `captureLastError` helper (#334)
- Dont report duplicate errors with fatal error handler (#334)
- Enforce maximum length for string serialization (#329)
## 1.1.0 (2016-07-30)
- Uncoercable values should no longer prevent exceptions from sending
to the Sentry server.
- `install()` can no longer be called multiple times.
## 1.0.0 (2016-07-28)
- Removed deprecated error codes configuration from ErrorHandler.
- Removed env data from HTTP interface.
- Removed `message` attribute from exceptions.
- appPath and prefixes are now resolved fully.
- Fixed various getter methods requiring invalid args.
- Fixed data mutation with `send_callback`.
## 0.22.0 (2016-06-23)
- Improve handling of encodings.
- Improve resiliency of variable serialization.
- Add 'formatted' attribute to Message interface.
## 0.21.0 (2016-06-10)
- Added `transport` option.
- Added `install()` shortcut.
## 0.20.0 (2016-06-02)
- Handle missing function names on frames.
- Remove suppression operator usage in breadcrumbs buffer.
- Force serialization of context values.
## 0.19.0 (2016-05-27)
- Add `error_reporting` breadcrumb handler.
## 0.18.0 (2016-05-17)
- Remove session from serialized data.
- `send_callback` return value must now be false to prevent capture.
- Add various getter/setter methods for configuration.
## 0.17.0 (2016-05-11)
- Don't attempt to serialize fixed SDK inputs.
- Improvements to breadcrumbs support in Monolog.
## 0.16.0 (2016-05-03)
- Initial breadcrumbs support with Monolog handler.
## 0.15.0 (2016-04-29)
- Fixed some cases where serialization wouldn't happen.
- Added sdk attribute.
## 0.14.0 (2016-04-27)
- Added `prefixes` option for stripping absolute paths.
- Removed `abs_path` from stacktraces.
- Added `app_path` to specify application root for resolving `in_app` on frames.
- Moved Laravel support to `sentry-laravel` project.
- Fixed duplicate stack computation.
- Added `dsn` option to ease configuration.
- Fixed an issue with the curl async transport.
- Improved serialization of values.
## 0.13.0 (2015-09-09)
- Updated API to use new style interfaces.
- Remove session cookie in default processor.
- Expand docs for Laravel, Symfony2, and Monolog.
- Default error types can now be set as part of ErrorHandler configuration.
## 0.12.1 (2015-07-26)
- Dont send empty values for various context.
## 0.12.0 (2015-05-19)
- Bumped protocol version to 6.
- Fixed an issue with the async curl handler (GH-216).
- Removed UDP transport.
## 0.11.0 (2015-03-25)
- New configuration parameter: `release`
- New configuration parameter: `message_limit`
- New configuration parameter: `curl_ssl_version`
- New configuration parameter: `curl_ipv4`
- New configuration parameter: `verify_ssl`
- Updated remote endpoint to use modern project-based path.
- Expanded default sanitizer support to include `auth_pw` attribute.
## 0.10.0 (2014-09-03)
- Added a default certificate bundle which includes common root CA's as well as getsentry.com's CA.
## 0.9.1 (2014-08-26)
- Change default curl connection to `sync`
- Improve CLI reporting
## 0.9.0 (2014-06-04)
- Protocol version 5
- Default to asynchronous HTTP handler using curl_multi.
(For previous versions see the commit history)

View File

@@ -0,0 +1,12 @@
Copyright (c) 2012 Sentry Team and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the Raven, Sentry, nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,21 @@
.PHONY: test
develop: update-submodules
composer install --dev
make setup-git
update-submodules:
git submodule init
git submodule update
cs:
vendor/bin/php-cs-fixer fix --config-file=.php_cs --verbose --diff
cs-dry-run:
vendor/bin/php-cs-fixer fix --config-file=.php_cs --verbose --diff --dry-run
test: cs-dry-run
vendor/bin/phpunit
setup-git:
git config branch.autosetuprebase always

View File

@@ -0,0 +1,170 @@
<p align="center">
<a href="https://sentry.io" target="_blank" align="center">
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
</a>
</p>
# Sentry for PHP
> Please note that the `1.x` branch of the Sentry PHP SDK is no longer actively maintained and will only receive bug-fix and security updates.
>
> For the most recent Sentry PHP SDK see the [default branch](https://github.com/getsentry/sentry-php).
---
[![Build Status](https://secure.travis-ci.org/getsentry/sentry-php.png?branch=master)](http://travis-ci.org/getsentry/sentry-php)
[![Total Downloads](https://poser.pugx.org/sentry/sentry/downloads)](https://packagist.org/packages/sentry/sentry)
[![Monthly Downloads](https://poser.pugx.org/sentry/sentry/d/monthly)](https://packagist.org/packages/sentry/sentry)
[![Latest Stable Version](https://poser.pugx.org/sentry/sentry/v/stable)](https://packagist.org/packages/sentry/sentry)
[![License](https://poser.pugx.org/sentry/sentry/license)](https://packagist.org/packages/sentry/sentry)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/getsentry/sentry-php/master.svg)](https://scrutinizer-ci.com/g/getsentry/sentry-php/)
[![Code Coverage](https://img.shields.io/scrutinizer/coverage/g/getsentry/sentry-php/master.svg)](https://scrutinizer-ci.com/g/getsentry/sentry-php/)
The Sentry PHP error reporter tracks errors and exceptions that happen during the
execution of your application and provides instant notification with detailed
informations needed to prioritize, identify, reproduce and fix each issue. Learn
more about [automatic PHP error reporting with Sentry](https://sentry.io/for/php/).
## Features
- Automatically report (un)handled exceptions and errors
- Send customized diagnostic data
- Process and sanitize data before sending it over the network
## Usage
```php
// Instantiate a new client with a compatible DSN and install built-in
// handlers
$client = (new Raven_Client('http://public@example.com/1'))->install();
// Capture an exception
$event_id = $client->captureException($ex);
// Give the user feedback
echo "Sorry, there was an error!";
echo "Your reference ID is " . $event_id;
```
For more information, see our [documentation](https://docs.getsentry.com/hosted/clients/php/).
## Integration with frameworks
Other packages exists to integrate this SDK into the most common frameworks.
### Official integrations
The following integrations are fully supported and maintained by the Sentry team.
- [Symfony](https://github.com/getsentry/sentry-symfony)
- [Laravel](https://github.com/getsentry/sentry-laravel)
### 3rd party integrations
The following integrations are available and maintained by members of the Sentry community.
- [Nette](https://github.com/Salamek/raven-nette)
- [ZendFramework](https://github.com/facile-it/sentry-module)
- [WordPress](https://wordpress.org/plugins/wp-sentry-integration/)
- [Drupal](https://www.drupal.org/project/raven)
- [OpenCart](https://github.com/BurdaPraha/oc_sentry)
- [Magento2](https://github.com/justbetter/magento2-sentry)
- [October CMS](https://github.com/OFFLINE-GmbH/oc-sentry-plugin/)
- ... feel free to be famous, create a port to your favourite platform!
## Community
- [Documentation](https://docs.getsentry.com/hosted/clients/php/)
- [Bug Tracker](http://github.com/getsentry/sentry-php/issues)
- [Code](http://github.com/getsentry/sentry-php)
- [Mailing List](https://groups.google.com/group/getsentry)
- [IRC](irc://irc.freenode.net/sentry) (irc.freenode.net, #sentry)
Contributing
------------
Dependencies are managed through composer:
```
$ composer install
```
Tests can then be run via phpunit:
```
$ vendor/bin/phpunit
```
Tagging a Release
-----------------
1. Make sure ``CHANGES`` is up to date (add the release date) and ``master`` is green.
2. Create a new branch for the minor version (if not present):
```
$ git checkout -b releases/1.11.x
```
3. Update the hardcoded version tag in ``Client.php``:
```php
class Raven_Client
{
const VERSION = '1.11.0';
}
```
4. Commit the change:
```
$ git commit -a -m "1.11.0"
```
5. Tag the branch:
```
git tag 1.11.0
```
6. Push the tag:
```
git push --tags
```
7. Switch back to ``master``:
```
git checkout master
```
8. Add the next minor release to the ``CHANGES`` file:
```
## 1.12.0 (unreleased)
```
9. Update the version in ``Client.php``:
```php
class Raven_Client
{
const VERSION = '1.12.x-dev';
}
```
10. Lastly, update the composer version in ``composer.json``:
```json
"extra": {
"branch-alias": {
"dev-master": "1.12.x-dev"
}
}
```
All done! Composer will pick up the tag and configuration automatically.

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,92 @@
#!/usr/bin/env php
<?php
// Maximize error reporting
error_reporting(E_ALL | E_STRICT);
// TODO: if we could get rid of this and have composer figure things out it'd make it
// a bit more sane
require(dirname(__file__) . '/../lib/Raven/Autoloader.php');
Raven_Autoloader::register();
function raven_cli_test($command, $args)
{
// Do something silly
try {
throw new Exception('This is a test exception sent from the Raven CLI.');
} catch (Exception $ex) {
return $ex;
}
}
function cmd_test($dsn)
{
// Parse DSN as a test
try {
if (empty(Raven_Client::parseDSN($dsn))) {
exit('ERROR: Missing DSN value');
}
} catch (InvalidArgumentException $ex) {
exit("ERROR: There was an error parsing your DSN:\n " . $ex->getMessage());
}
$client = new Raven_Client($dsn, array(
'trace' => true,
'curl_method' => 'sync',
'app_path' => realpath(__DIR__ . '/..'),
'base_path' => realpath(__DIR__ . '/..'),
));
$config = get_object_vars($client);
$required_keys = array('server', 'project', 'public_key');
echo "Client configuration:\n";
foreach ($required_keys as $key) {
if (empty($config[$key])) {
exit("ERROR: Missing configuration for $key");
}
if (is_array($config[$key])) {
echo "-> $key: [".implode(", ", $config[$key])."]\n";
} else {
echo "-> $key: $config[$key]\n";
}
}
echo "\n";
echo "Sending a test event:\n";
$ex = raven_cli_test("command name", array("foo" => "bar"));
$event_id = $client->captureException($ex);
echo "-> event ID: $event_id\n";
$last_error = $client->getLastError();
if (!empty($last_error)) {
exit("ERROR: There was an error sending the test event:\n " . $last_error);
}
echo "\n";
echo "Done!";
}
function main() {
global $argv;
if (!isset($argv[1])) {
exit('Usage: sentry test <dsn>');
}
$cmd = $argv[1];
switch ($cmd) {
case 'test':
cmd_test(@$argv[2]);
break;
default:
exit('Usage: sentry test <dsn>');
}
}
main();

View File

@@ -0,0 +1,59 @@
{
"name": "sentry/sentry",
"type": "library",
"description": "A PHP client for Sentry (http://getsentry.com)",
"keywords": ["log", "logging"],
"homepage": "http://getsentry.com",
"license": "BSD-3-Clause",
"authors": [
{
"name": "David Cramer",
"email": "dcramer@gmail.com"
}
],
"require-dev": {
"friendsofphp/php-cs-fixer": "^1.8.0",
"phpunit/phpunit": "^4.8.35 || ^5.7",
"monolog/monolog": "^1.0"
},
"require": {
"php": "^5.3|^7.0",
"ext-curl": "*"
},
"suggest": {
"ext-hash": "*",
"ext-json": "*",
"ext-mbstring": "*",
"monolog/monolog": "Automatically capture Monolog events as breadcrumbs"
},
"conflict": {
"raven/raven": "*"
},
"bin": [
"bin/sentry"
],
"autoload": {
"psr-0" : {
"Raven_" : "lib/"
}
},
"scripts": {
"tests": [
"vendor/bin/phpunit --verbose"
],
"tests-travis": [
"vendor/bin/phpunit --verbose --configuration phpunit.xml --coverage-clover test/clover.xml --log-junit test/junit.xml"
],
"tests-report": [
"vendor/bin/phpunit --verbose --configuration phpunit.xml --coverage-html test/html-report"
],
"phpcs": [
"vendor/bin/php-cs-fixer fix --config-file=.php_cs --verbose --diff --dry-run"
]
},
"extra": {
"branch-alias": {
"dev-master": "1.11.x-dev"
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,44 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Autoloads Raven classes.
*
* @package raven
*/
class Raven_Autoloader
{
/**
* Registers Raven_Autoloader as an SPL autoloader.
*/
public static function register()
{
spl_autoload_register(array('Raven_Autoloader', 'autoload'));
}
/**
* Handles autoloading of classes.
*
* @param string $class A class name.
*/
public static function autoload($class)
{
if (substr($class, 0, 6) !== 'Raven_') {
return;
}
$file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php';
if (is_file($file)) {
/** @noinspection PhpIncludeInspection */
require $file;
}
}
}

View File

@@ -0,0 +1,76 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Raven Breadcrumbs
*
* @package raven
*/
class Raven_Breadcrumbs
{
public $count;
public $pos;
public $size;
/**
* @var array[]
*/
public $buffer;
public function __construct($size = 100)
{
$this->size = $size;
$this->reset();
}
public function reset()
{
$this->count = 0;
$this->pos = 0;
$this->buffer = array();
}
public function record($crumb)
{
if (empty($crumb['timestamp'])) {
$crumb['timestamp'] = microtime(true);
}
$this->buffer[$this->pos] = $crumb;
$this->pos = ($this->pos + 1) % $this->size;
$this->count++;
}
/**
* @return array[]
*/
public function fetch()
{
$results = array();
for ($i = 0; $i <= ($this->size - 1); $i++) {
$idx = ($this->pos + $i) % $this->size;
if (isset($this->buffer[$idx])) {
$results[] = $this->buffer[$idx];
}
}
return $results;
}
public function is_empty()
{
return $this->count === 0;
}
public function to_json()
{
return array(
'values' => $this->fetch(),
);
}
}

View File

@@ -0,0 +1,45 @@
<?php
class Raven_Breadcrumbs_ErrorHandler
{
protected $existingHandler;
/**
* @var Raven_Client the client object that sends the message to the server
*/
protected $ravenClient;
/**
* @param Raven_Client $ravenClient
*/
public function __construct(Raven_Client $ravenClient)
{
$this->ravenClient = $ravenClient;
}
public function handleError($code, $message, $file = '', $line = 0, $context = array())
{
$this->ravenClient->breadcrumbs->record(array(
'category' => 'error_reporting',
'message' => $message,
'level' => $this->ravenClient->translateSeverity($code),
'data' => array(
'code' => $code,
'line' => $line,
'file' => $file,
),
));
if ($this->existingHandler !== null) {
return call_user_func($this->existingHandler, $code, $message, $file, $line, $context);
} else {
return false;
}
}
public function install()
{
$this->existingHandler = set_error_handler(array($this, 'handleError'), E_ALL);
return $this;
}
}

View File

@@ -0,0 +1,102 @@
<?php
use Monolog\Logger;
use Monolog\Handler\AbstractProcessingHandler;
class Raven_Breadcrumbs_MonologHandler extends AbstractProcessingHandler
{
/**
* Translates Monolog log levels to Raven log levels.
*/
protected $logLevels = array(
Logger::DEBUG => Raven_Client::DEBUG,
Logger::INFO => Raven_Client::INFO,
Logger::NOTICE => Raven_Client::INFO,
Logger::WARNING => Raven_Client::WARNING,
Logger::ERROR => Raven_Client::ERROR,
Logger::CRITICAL => Raven_Client::FATAL,
Logger::ALERT => Raven_Client::FATAL,
Logger::EMERGENCY => Raven_Client::FATAL,
);
protected $excMatch = '/^exception \'([^\']+)\' with message \'(.+)\' in .+$/s';
/**
* @var Raven_Client the client object that sends the message to the server
*/
protected $ravenClient;
/**
* @param Raven_Client $ravenClient
* @param int $level The minimum logging level at which this handler will be triggered
* @param bool $bubble Whether the messages that are handled can bubble up the stack or not
*/
public function __construct(Raven_Client $ravenClient, $level = Logger::DEBUG, $bubble = true)
{
parent::__construct($level, $bubble);
$this->ravenClient = $ravenClient;
}
/**
* @param string $message
* @return array|null
*/
protected function parseException($message)
{
if (preg_match($this->excMatch, $message, $matches)) {
return array($matches[1], $matches[2]);
}
return null;
}
/**
* {@inheritdoc}
*/
protected function write(array $record)
{
// sentry uses the 'nobreadcrumb' attribute to skip reporting
if (!empty($record['context']['nobreadcrumb'])) {
return;
}
if (isset($record['context']['exception']) && ($record['context']['exception'] instanceof \Exception || (PHP_VERSION_ID >= 70000 && $record['context']['exception'] instanceof \Throwable))) {
/**
* @var \Exception|\Throwable $exc
*/
$exc = $record['context']['exception'];
$crumb = array(
'type' => 'error',
'level' => $this->logLevels[$record['level']],
'category' => $record['channel'],
'data' => array(
'type' => get_class($exc),
'value' => $exc->getMessage(),
),
);
} else {
// TODO(dcramer): parse exceptions out of messages and format as above
if ($error = $this->parseException($record['message'])) {
$crumb = array(
'type' => 'error',
'level' => $this->logLevels[$record['level']],
'category' => $record['channel'],
'data' => array(
'type' => $error[0],
'value' => $error[1],
),
);
} else {
$crumb = array(
'level' => $this->logLevels[$record['level']],
'category' => $record['channel'],
'message' => $record['message'],
'data' => $record['context'],
);
}
}
$this->ravenClient->breadcrumbs->record($crumb);
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,198 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Raven_Compat
{
public static function gethostname()
{
if (function_exists('gethostname')) {
return gethostname();
}
return self::_gethostname();
}
public static function _gethostname()
{
return php_uname('n');
}
public static function hash_hmac($algo, $data, $key, $raw_output = false)
{
if (function_exists('hash_hmac')) {
return hash_hmac($algo, $data, $key, $raw_output);
}
return self::_hash_hmac($algo, $data, $key, $raw_output);
}
/**
* Implementation from 'KC Cloyd'.
*
* @param string $algo Name of selected hashing algorithm
* @param string $data Message to be hashed
* @param string $key Shared secret key used for generating the HMAC variant of the message digest
* @param bool $raw_output Must be binary
* @return string
* @doc http://php.net/manual/en/function.hash-hmac.php
*/
public static function _hash_hmac($algo, $data, $key, $raw_output = false)
{
$algo = strtolower($algo);
$pack = 'H'.strlen($algo('test'));
$size = 64;
$opad = str_repeat(chr(0x5C), $size);
$ipad = str_repeat(chr(0x36), $size);
if (strlen($key) > $size) {
$key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
} else {
$key = str_pad($key, $size, chr(0x00));
}
$keyLastPos = strlen($key) - 1;
for ($i = 0; $i < $keyLastPos; $i++) {
$opad[$i] = $opad[$i] ^ $key[$i];
$ipad[$i] = $ipad[$i] ^ $key[$i];
}
$output = $algo($opad.pack($pack, $algo($ipad.$data)));
return ($raw_output) ? pack($pack, $output) : $output;
}
/**
* Note that we discard the options given to be compatible
* with PHP < 5.3
*
* @param mixed $value
* @param int $options
* @param int $depth Set the maximum depth
* @return string
*/
public static function json_encode($value, $options = 0, $depth = 512)
{
if (function_exists('json_encode')) {
if (PHP_VERSION_ID < 50300) {
return json_encode($value);
} elseif (PHP_VERSION_ID < 50500) {
return json_encode($value, $options);
} else {
return json_encode($value, $options, $depth);
}
}
// @codeCoverageIgnoreStart
return self::_json_encode($value, $depth);
// @codeCoverageIgnoreEnd
}
/**
* @param mixed $value
* @param int $depth Set the maximum depth
* @return string|false
*/
public static function _json_encode($value, $depth = 513)
{
if (extension_loaded('xdebug')) {
ini_set('xdebug.max_nesting_level', 2048);
}
return self::_json_encode_lowlevel($value, $depth);
}
/**
* Implementation taken from
* http://www.mike-griffiths.co.uk/php-json_encode-alternative/
*
* @param mixed $value
* @param int $depth Set the maximum depth
* @return string|false
*/
private static function _json_encode_lowlevel($value, $depth)
{
static $jsonReplaces = array(
array('\\', '/', "\n", "\t", "\r", "\f", '"'),
array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\f', '\"'));
if (is_null($value)) {
return 'null';
}
if ($value === false) {
return 'false';
}
if ($value === true) {
return 'true';
}
if (is_scalar($value)) {
// Always use '.' for floats.
if (is_float($value)) {
return floatval(str_replace(',', '.', strval($value)));
}
if (is_string($value)) {
return sprintf('"%s"',
str_replace($jsonReplaces[0], $jsonReplaces[1], $value));
} else {
return $value;
}
} elseif ($depth <= 1) {
return false;
}
$isList = true;
for ($i = 0, reset($value); $i<count($value); $i++, next($value)) {
if (key($value) !== $i) {
$isList = false;
break;
}
}
$result = array();
if ($isList) {
foreach ($value as $v) {
$this_value = self::_json_encode($v, $depth - 1);
if ($this_value === false) {
return false;
}
$result[] = $this_value;
}
return '[' . join(',', $result) . ']';
} else {
foreach ($value as $k => $v) {
$this_value = self::_json_encode($v, $depth - 1);
if ($this_value === false) {
return false;
}
$result[] = self::_json_encode($k, $depth - 1).':'.$this_value;
}
return '{' . join(',', $result) . '}';
}
}
public static function strlen($string)
{
if (extension_loaded('mbstring')) {
return mb_strlen($string, 'UTF-8');
}
return strlen($string);
}
public static function substr($string, $start, $length)
{
if (extension_loaded('mbstring')) {
return mb_substr($string, $start, $length, 'UTF-8');
}
return substr($string, $start, $length);
}
}

View File

@@ -0,0 +1,36 @@
<?php
/**
* Storage for additional client context.
*
* @package raven
*/
class Raven_Context
{
/**
* @var array
*/
public $tags;
/**
* @var array
*/
public $extra;
/**
* @var array|null
*/
public $user;
public function __construct()
{
$this->clear();
}
/**
* Clean up existing context.
*/
public function clear()
{
$this->tags = array();
$this->extra = array();
$this->user = null;
}
}

View File

@@ -0,0 +1,126 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Asynchronous Curl connection manager.
*
* @package raven
*/
// TODO(dcramer): handle ca_cert
class Raven_CurlHandler
{
protected $join_timeout;
protected $multi_handle;
protected $options;
protected $requests;
public function __construct($options, $join_timeout = 5)
{
$this->options = $options;
$this->multi_handle = curl_multi_init();
$this->requests = array();
$this->join_timeout = $join_timeout;
$this->registerShutdownFunction();
}
public function __destruct()
{
$this->join();
}
public function enqueue($url, $data = null, $headers = array())
{
$ch = curl_init();
$new_headers = array();
foreach ($headers as $key => $value) {
array_push($new_headers, $key .': '. $value);
}
// XXX(dcramer): Prevent 100-continue response form server (Fixes GH-216)
$new_headers[] = 'Expect:';
curl_setopt($ch, CURLOPT_HTTPHEADER, $new_headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt_array($ch, $this->options);
if (isset($data)) {
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
}
curl_multi_add_handle($this->multi_handle, $ch);
$fd = (int)$ch;
$this->requests[$fd] = 1;
$this->select();
return $fd;
}
public function registerShutdownFunction()
{
register_shutdown_function(array($this, 'join'));
}
public function join($timeout = null)
{
if (!isset($timeout)) {
$timeout = $this->join_timeout;
}
$start = time();
do {
$this->select();
if (count($this->requests) === 0) {
break;
}
usleep(10000);
} while ($timeout !== 0 && time() - $start < $timeout);
}
/**
* @doc http://php.net/manual/en/function.curl-multi-exec.php
*/
protected function select()
{
$active = false;
do {
$mrc = curl_multi_exec($this->multi_handle, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($this->multi_handle) !== -1) {
do {
$mrc = curl_multi_exec($this->multi_handle, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
} else {
return;
}
}
while ($info = curl_multi_info_read($this->multi_handle)) {
$ch = $info['handle'];
$fd = (int)$ch;
curl_multi_remove_handle($this->multi_handle, $ch);
if (!isset($this->requests[$fd])) {
return;
}
unset($this->requests[$fd]);
}
}
}

View File

@@ -0,0 +1,231 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Event handlers for exceptions and errors
*
* $client = new Raven_Client('http://public:secret/example.com/1');
* $error_handler = new Raven_ErrorHandler($client);
* $error_handler->registerExceptionHandler();
* $error_handler->registerErrorHandler();
* $error_handler->registerShutdownFunction();
*
* @package raven
*/
// TODO(dcramer): deprecate default error types in favor of runtime configuration
// unless a reason can be determined that making them dynamic is better. They
// currently are not used outside of the fatal handler.
class Raven_ErrorHandler
{
protected $old_exception_handler;
protected $call_existing_exception_handler = false;
protected $old_error_handler;
protected $call_existing_error_handler = false;
protected $reservedMemory;
/** @var Raven_Client */
protected $client;
protected $send_errors_last = false;
protected $fatal_error_types = array(
E_ERROR,
E_PARSE,
E_CORE_ERROR,
E_CORE_WARNING,
E_COMPILE_ERROR,
E_COMPILE_WARNING,
E_STRICT,
);
/**
* @var array
* Error types which should be processed by the handler.
* A 'null' value implies "whatever error_reporting is at time of error".
*/
protected $error_types = null;
/** @var \Exception|null */
private $lastHandledException;
public function __construct($client, $send_errors_last = false, $error_types = null,
$__error_types = null)
{
// support legacy fourth argument for error types
if ($error_types === null) {
$error_types = $__error_types;
}
$this->client = $client;
$this->error_types = $error_types;
$this->fatal_error_types = array_reduce($this->fatal_error_types, array($this, 'bitwiseOr'));
if ($send_errors_last) {
$this->send_errors_last = true;
$this->client->store_errors_for_bulk_send = true;
}
}
public function bitwiseOr($a, $b)
{
return $a | $b;
}
public function handleException($e, $isError = false, $vars = null)
{
$event_id = $this->client->captureException($e, null, null, $vars);
try {
$e->event_id = $event_id;
} catch (\Exception $e) {
// Ignore any errors while setting the event id on the exception object
// @see: https://github.com/getsentry/sentry-php/issues/579
}
$this->lastHandledException = $e;
if (!$isError && $this->call_existing_exception_handler) {
if ($this->old_exception_handler !== null) {
call_user_func($this->old_exception_handler, $e);
} else {
throw $e;
}
}
}
public function handleError($type, $message, $file = '', $line = 0, $context = array())
{
// http://php.net/set_error_handler
// The following error types cannot be handled with a user defined function: E_ERROR,
// E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and
// most of E_STRICT raised in the file where set_error_handler() is called.
if (error_reporting() !== 0) {
$error_types = $this->error_types;
if ($error_types === null) {
$error_types = error_reporting();
}
if ($error_types & $type) {
$e = new ErrorException($message, 0, $type, $file, $line);
$this->handleException($e, true, $context);
}
}
if ($this->call_existing_error_handler) {
if ($this->old_error_handler !== null) {
return call_user_func(
$this->old_error_handler,
$type,
$message,
$file,
$line,
$context
);
} else {
return false;
}
}
return true;
}
public function handleFatalError()
{
unset($this->reservedMemory);
if (null === $error = error_get_last()) {
return;
}
if ($this->shouldCaptureFatalError($error['type'], $error['message'])) {
$e = new ErrorException(
@$error['message'], 0, @$error['type'],
@$error['file'], @$error['line']
);
$this->client->useCompression = $this->client->useCompression && PHP_VERSION_ID > 70000;
$this->handleException($e, true);
}
}
/**
* @param int $type
* @param string|null $message
* @return bool
*/
public function shouldCaptureFatalError($type, $message = null)
{
if (PHP_VERSION_ID >= 70000 && $this->lastHandledException) {
if ($type === E_CORE_ERROR && strpos($message, 'Exception thrown without a stack frame') === 0) {
return false;
}
if ($type === E_ERROR) {
$expectedMessage = 'Uncaught '
. \get_class($this->lastHandledException)
. ': '
. $this->lastHandledException->getMessage();
if (strpos($message, $expectedMessage) === 0) {
return false;
}
}
}
return (bool) ($type & $this->fatal_error_types);
}
/**
* Register a handler which will intercept unhandled exceptions and report them to the
* associated Sentry client.
*
* @param bool $call_existing Call any existing exception handlers after processing
* this instance.
* @return Raven_ErrorHandler
*/
public function registerExceptionHandler($call_existing = true)
{
$this->old_exception_handler = set_exception_handler(array($this, 'handleException'));
$this->call_existing_exception_handler = $call_existing;
return $this;
}
/**
* Register a handler which will intercept standard PHP errors and report them to the
* associated Sentry client.
*
* @param bool $call_existing Call any existing errors handlers after processing
* this instance.
* @param array $error_types All error types that should be sent.
* @return Raven_ErrorHandler
*/
public function registerErrorHandler($call_existing = true, $error_types = null)
{
if ($error_types !== null) {
$this->error_types = $error_types;
}
$this->old_error_handler = set_error_handler(array($this, 'handleError'), E_ALL);
$this->call_existing_error_handler = $call_existing;
return $this;
}
/**
* Register a fatal error handler, which will attempt to capture errors which
* shutdown the PHP process. These are commonly things like OOM or timeouts.
*
* @param int $reservedMemorySize Number of kilobytes memory space to reserve,
* which is utilized when handling fatal errors.
* @return Raven_ErrorHandler
*/
public function registerShutdownFunction($reservedMemorySize = 10)
{
register_shutdown_function(array($this, 'handleFatalError'));
$this->reservedMemory = str_repeat('x', 1024 * $reservedMemorySize);
return $this;
}
}

View File

@@ -0,0 +1,4 @@
<?php
class Raven_Exception extends Exception
{
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* Base class for data processing.
*
* @package raven
*/
abstract class Raven_Processor
{
/**
* This constant defines the mask string used to strip sensitive information.
*/
const STRING_MASK = '********';
/**
* @var Raven_Client The Raven client
*/
protected $client;
/**
* Class constructor.
*
* @param Raven_Client $client The Raven client
*/
public function __construct(Raven_Client $client)
{
$this->client = $client;
}
/**
* Override the default processor options
*
* @param array $options Associative array of processor options
*/
public function setProcessorOptions(array $options)
{
}
/**
* Process and sanitize data, modifying the existing value if necessary.
*
* @param array $data Array of log data
*/
abstract public function process(&$data);
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* This processor removes all the cookies from the request to ensure no sensitive
* informations are sent to the server.
*
* @author Stefano Arlandini <sarlandini@alice.it>
*/
final class Raven_Processor_RemoveCookiesProcessor extends Raven_Processor
{
/**
* {@inheritdoc}
*/
public function process(&$data)
{
if (isset($data['request'])) {
if (isset($data['request']['cookies'])) {
$data['request']['cookies'] = self::STRING_MASK;
}
if (isset($data['request']['headers']) && isset($data['request']['headers']['Cookie'])) {
$data['request']['headers']['Cookie'] = self::STRING_MASK;
}
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* This processor removes all the data of the HTTP body to ensure no sensitive
* informations are sent to the server in case the request method is POST, PUT,
* PATCH or DELETE.
*
* @author Stefano Arlandini <sarlandini@alice.it>
*/
final class Raven_Processor_RemoveHttpBodyProcessor extends Raven_Processor
{
/**
* {@inheritdoc}
*/
public function process(&$data)
{
if (isset($data['request'], $data['request']['method']) && in_array(strtoupper($data['request']['method']), array('POST', 'PUT', 'PATCH', 'DELETE'))) {
$data['request']['data'] = self::STRING_MASK;
}
}
}

View File

@@ -0,0 +1,160 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Asterisk out passwords from password fields in frames, http,
* and basic extra data.
*
* @package raven
*/
class Raven_Processor_SanitizeDataProcessor extends Raven_Processor
{
const MASK = self::STRING_MASK;
const FIELDS_RE = '/(authorization|password|passwd|secret|password_confirmation|card_number|auth_pw)/i';
const VALUES_RE = '/^(?:\d[ -]*?){13,19}$/';
protected $fields_re;
protected $values_re;
protected $session_cookie_name;
/**
* {@inheritdoc}
*/
public function __construct(Raven_Client $client)
{
parent::__construct($client);
$this->fields_re = self::FIELDS_RE;
$this->values_re = self::VALUES_RE;
$this->session_cookie_name = ini_get('session.name');
}
/**
* {@inheritdoc}
*/
public function setProcessorOptions(array $options)
{
if (isset($options['fields_re'])) {
$this->fields_re = $options['fields_re'];
}
if (isset($options['values_re'])) {
$this->values_re = $options['values_re'];
}
}
/**
* Replace any array values with our mask if the field name or the value matches a respective regex
*
* @param mixed $item Associative array value
* @param string $key Associative array key
*/
public function sanitize(&$item, $key)
{
if (empty($item)) {
return;
}
if (preg_match($this->values_re, $item)) {
$item = self::STRING_MASK;
}
if (empty($key)) {
return;
}
if (preg_match($this->fields_re, $key)) {
$item = self::STRING_MASK;
}
}
public function sanitizeException(&$data)
{
foreach ($data['exception']['values'] as &$value) {
$this->sanitizeStacktrace($value['stacktrace']);
}
}
public function sanitizeHttp(&$data)
{
$http = &$data['request'];
if (!empty($http['cookies']) && is_array($http['cookies'])) {
$cookies = &$http['cookies'];
if (!empty($cookies[$this->session_cookie_name])) {
$cookies[$this->session_cookie_name] = self::STRING_MASK;
}
}
if (!empty($http['data']) && is_array($http['data'])) {
array_walk_recursive($http['data'], array($this, 'sanitize'));
}
}
public function sanitizeStacktrace(&$data)
{
foreach ($data['frames'] as &$frame) {
if (empty($frame['vars'])) {
continue;
}
array_walk_recursive($frame['vars'], array($this, 'sanitize'));
}
}
/**
* {@inheritdoc}
*/
public function process(&$data)
{
if (!empty($data['exception'])) {
$this->sanitizeException($data);
}
if (!empty($data['stacktrace'])) {
$this->sanitizeStacktrace($data['stacktrace']);
}
if (!empty($data['request'])) {
$this->sanitizeHttp($data);
}
if (!empty($data['extra'])) {
array_walk_recursive($data['extra'], array($this, 'sanitize'));
}
}
/**
* @return string
*/
public function getFieldsRe()
{
return $this->fields_re;
}
/**
* @param string $fields_re
*/
public function setFieldsRe($fields_re)
{
$this->fields_re = $fields_re;
}
/**
* @return string
*/
public function getValuesRe()
{
return $this->values_re;
}
/**
* @param string $values_re
*/
public function setValuesRe($values_re)
{
$this->values_re = $values_re;
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* This processor sanitizes the configured HTTP headers to ensure no sensitive
* informations are sent to the server.
*
* @author Stefano Arlandini <sarlandini@alice.it>
*/
final class Raven_Processor_SanitizeHttpHeadersProcessor extends Raven_Processor
{
/**
* @var string[] $httpHeadersToSanitize The list of HTTP headers to sanitize
*/
private $httpHeadersToSanitize = array();
/**
* {@inheritdoc}
*/
public function __construct(Raven_Client $client)
{
parent::__construct($client);
}
/**
* {@inheritdoc}
*/
public function setProcessorOptions(array $options)
{
$this->httpHeadersToSanitize = array_merge($this->getDefaultHeaders(), isset($options['sanitize_http_headers']) ? $options['sanitize_http_headers'] : array());
}
/**
* {@inheritdoc}
*/
public function process(&$data)
{
if (isset($data['request']) && isset($data['request']['headers'])) {
foreach ($data['request']['headers'] as $header => &$value) {
if (in_array($header, $this->httpHeadersToSanitize)) {
$value = self::STRING_MASK;
}
}
}
}
/**
* Gets the list of default headers that must be sanitized.
*
* @return string[]
*/
private function getDefaultHeaders()
{
return array('Authorization', 'Proxy-Authorization', 'X-Csrf-Token', 'X-CSRFToken', 'X-XSRF-TOKEN');
}
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* This processor removes the `pre_context`, `context_line` and `post_context`
* informations from all exceptions captured by an event.
*
* @author Stefano Arlandini <sarlandini@alice.it>
*/
class Raven_Processor_SanitizeStacktraceProcessor extends Raven_Processor
{
/**
* {@inheritdoc}
*/
public function process(&$data)
{
if (!isset($data['exception'], $data['exception']['values'])) {
return;
}
foreach ($data['exception']['values'] as &$exception) {
if (!isset($exception['stacktrace'])) {
continue;
}
foreach ($exception['stacktrace']['frames'] as &$frame) {
unset($frame['pre_context'], $frame['context_line'], $frame['post_context']);
}
}
}
}

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,41 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Serializes a value into a representation that should reasonably suggest
* both the type and value, and be serializable into JSON.
* @package raven
*/
class Raven_ReprSerializer extends Raven_Serializer
{
protected function serializeValue($value)
{
if ($value === null) {
return 'null';
} elseif ($value === false) {
return 'false';
} elseif ($value === true) {
return 'true';
} elseif (is_float($value) && (int) $value == $value) {
return $value.'.0';
} elseif (is_integer($value) || is_float($value)) {
return (string) $value;
} elseif (is_object($value) || gettype($value) == 'object') {
return 'Object '.get_class($value);
} elseif (is_resource($value)) {
return 'Resource '.get_resource_type($value);
} elseif (is_array($value)) {
return 'Array of length ' . count($value);
} else {
return $this->serializeString($value);
}
}
}

View File

@@ -0,0 +1,19 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
@trigger_error('The '.__NAMESPACE__.'\Raven_SanitizeDataProcessor class is deprecated since version 1.7 and will be removed in 2.0. Use the Raven_Processor_SanitizeDataProcessor class in the same namespace instead.', E_USER_DEPRECATED);
/**
* {@inheritdoc}
*/
class Raven_SanitizeDataProcessor extends Raven_Processor_SanitizeDataProcessor
{
}

View File

@@ -0,0 +1,200 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This helper is based on code from Facebook's Phabricator project
*
* https://github.com/facebook/phabricator
*
* Specifically, it is an adaptation of the PhutilReadableSerializer class.
*
* @package raven
*/
class Raven_Serializer
{
/*
* The default mb detect order
*
* @see http://php.net/manual/en/function.mb-detect-encoding.php
*/
const DEFAULT_MB_DETECT_ORDER = 'auto';
/*
* Suggested detect order for western countries
*/
const WESTERN_MB_DETECT_ORDER = 'UTF-8, ASCII, ISO-8859-1, ISO-8859-2, ISO-8859-3, ISO-8859-4, ISO-8859-5, ISO-8859-6, ISO-8859-7, ISO-8859-8, ISO-8859-9, ISO-8859-10, ISO-8859-13, ISO-8859-14, ISO-8859-15, ISO-8859-16, Windows-1251, Windows-1252, Windows-1254';
/**
* This is the default mb detect order for the detection of encoding
*
* @var string
*/
protected $mb_detect_order = self::DEFAULT_MB_DETECT_ORDER;
/**
* The default maximum message lengths. Longer strings will be truncated
*
* @var int
*/
protected $message_limit = Raven_Client::MESSAGE_LIMIT;
/**
* The default max depth.
*
* @var int
*/
protected $default_max_depth = 3;
/**
* @param null|string $mb_detect_order
* @param null|int $message_limit
*/
public function __construct($mb_detect_order = null, $message_limit = null)
{
if ($mb_detect_order != null) {
$this->mb_detect_order = $mb_detect_order;
}
if ($message_limit != null) {
$this->message_limit = (int) $message_limit;
}
}
/**
* Serialize an object (recursively) into something safe for data
* sanitization and encoding.
*
* @param mixed $value
* @param int|null $max_depth
* @param int $_depth
* @return string|bool|double|int|null|object|array
*/
public function serialize($value, $max_depth = null, $_depth = 0)
{
$className = is_object($value) ? get_class($value) : null;
if (is_null($max_depth)) {
$max_depth = $this->getDefaultMaxDepth();
}
$toArray = is_array($value) || $className === 'stdClass';
if ($toArray && $_depth < $max_depth) {
$new = array();
foreach ($value as $k => $v) {
$new[$this->serializeValue($k)] = $this->serialize($v, $max_depth, $_depth + 1);
}
return $new;
}
return $this->serializeValue($value);
}
protected function serializeString($value)
{
$value = (string) $value;
// Check if mbstring extension is loaded
if (extension_loaded('mbstring')) {
// we always guarantee this is coerced, even if we can't detect encoding
if ($currentEncoding = mb_detect_encoding($value, $this->mb_detect_order)) {
$value = mb_convert_encoding($value, 'UTF-8', $currentEncoding);
} else {
$value = mb_convert_encoding($value, 'UTF-8');
}
}
if ($this->message_limit !== 0 && Raven_Compat::strlen($value) > $this->message_limit) {
$value = Raven_Compat::substr($value, 0, $this->message_limit - 10) . ' {clipped}';
}
return $value;
}
/**
* @param mixed $value
* @return string|bool|double|int|null
*/
protected function serializeValue($value)
{
if (is_null($value) || is_bool($value) || is_float($value) || is_integer($value)) {
return $value;
} elseif (is_object($value) || gettype($value) == 'object') {
return 'Object '.get_class($value);
} elseif (is_resource($value)) {
return 'Resource '.get_resource_type($value);
} elseif (is_array($value)) {
return 'Array of length ' . count($value);
} else {
return $this->serializeString($value);
}
}
/**
* @return string
* @codeCoverageIgnore
*/
public function getMbDetectOrder()
{
return $this->mb_detect_order;
}
/**
* @param string $mb_detect_order
*
* @return Raven_Serializer
* @codeCoverageIgnore
*/
public function setMbDetectOrder($mb_detect_order)
{
$this->mb_detect_order = $mb_detect_order;
return $this;
}
/**
* @return int
* @codeCoverageIgnore
*/
public function getMessageLimit()
{
return $this->message_limit;
}
/**
* @param int $message_limit
* @codeCoverageIgnore
*/
public function setMessageLimit($message_limit)
{
$this->message_limit = (int)$message_limit;
}
/**
* @return int
*/
public function getDefaultMaxDepth()
{
return $this->default_max_depth;
}
/**
* @param int $max_depth
*/
public function setDefaultMaxDepth($max_depth)
{
$this->default_max_depth = (int)$max_depth;
}
}

View File

@@ -0,0 +1,311 @@
<?php
/**
* Small helper class to inspect the stacktrace
*
* @package raven
*/
class Raven_Stacktrace
{
public static $statements = array(
'include',
'include_once',
'require',
'require_once',
);
public static function get_stack_info($frames,
$trace = false,
$errcontext = null,
$frame_var_limit = Raven_Client::MESSAGE_LIMIT,
$strip_prefixes = null,
$app_path = null,
$excluded_app_paths = null,
Raven_Serializer $serializer = null,
Raven_ReprSerializer $reprSerializer = null)
{
$serializer = $serializer ?: new Raven_Serializer();
$reprSerializer = $reprSerializer ?: new Raven_ReprSerializer();
/**
* PHP stores calls in the stacktrace, rather than executing context. Sentry
* wants to know "when Im calling this code, where am I", and PHP says "I'm
* calling this function" not "I'm in this function". Due to that, we shift
* the context for a frame up one, meaning the variables (which are the calling
* args) come from the previous frame.
*/
$result = array();
for ($i = 0; $i < count($frames); $i++) {
$frame = isset($frames[$i]) ? $frames[$i] : array();
$nextframe = isset($frames[$i + 1]) ? $frames[$i + 1] : array();
if (!array_key_exists('file', $frame)) {
$context = array();
if (!empty($frame['class'])) {
$context['line'] = sprintf('%s%s%s', $frame['class'], $frame['type'], $frame['function']);
try {
$reflect = new ReflectionClass($frame['class']);
$context['filename'] = $filename = $reflect->getFileName();
} catch (ReflectionException $e) {
// Forget it if we run into errors, it's not worth it.
}
} elseif (!empty($frame['function'])) {
$context['line'] = sprintf('%s(anonymous)', $frame['function']);
} else {
$context['line'] = sprintf('(anonymous)');
}
if (empty($context['filename'])) {
$context['filename'] = $filename = '[Anonymous function]';
}
$abs_path = '';
$context['prefix'] = '';
$context['suffix'] = '';
$context['lineno'] = 0;
} else {
$context = self::read_source_file($frame['file'], $frame['line']);
$abs_path = $frame['file'];
}
// strip base path if present
$context['filename'] = self::strip_prefixes($context['filename'], $strip_prefixes);
if ($i === 0 && isset($errcontext)) {
// If we've been given an error context that can be used as the vars for the first frame.
$vars = $errcontext;
} else {
if ($trace) {
$vars = self::get_frame_context($nextframe, $frame_var_limit);
} else {
$vars = array();
}
}
$data = array(
'filename' => $context['filename'],
'lineno' => (int) $context['lineno'],
'function' => isset($nextframe['function']) ? $nextframe['function'] : null,
'pre_context' => $serializer->serialize($context['prefix']),
'context_line' => $serializer->serialize($context['line']),
'post_context' => $serializer->serialize($context['suffix']),
);
// detect in_app based on app path
if ($app_path) {
$norm_abs_path = @realpath($abs_path) ?: $abs_path;
if (!$abs_path) {
$in_app = false;
} else {
$in_app = (bool)(substr($norm_abs_path, 0, strlen($app_path)) === $app_path);
}
if ($in_app && $excluded_app_paths) {
foreach ($excluded_app_paths as $path) {
if (substr($norm_abs_path, 0, strlen($path)) === $path) {
$in_app = false;
break;
}
}
}
$data['in_app'] = $in_app;
}
// dont set this as an empty array as PHP will treat it as a numeric array
// instead of a mapping which goes against the defined Sentry spec
if (!empty($vars)) {
$cleanVars = array();
foreach ($vars as $key => $value) {
$value = $reprSerializer->serialize($value);
if (is_string($value) || is_numeric($value)) {
$cleanVars[(string)$key] = Raven_Compat::substr($value, 0, $frame_var_limit);
} else {
$cleanVars[(string)$key] = $value;
}
}
$data['vars'] = $cleanVars;
}
$result[] = $data;
}
return array_reverse($result);
}
public static function get_default_context($frame, $frame_arg_limit = Raven_Client::MESSAGE_LIMIT)
{
if (!isset($frame['args'])) {
return array();
}
$i = 1;
$args = array();
foreach ($frame['args'] as $arg) {
$args['param'.$i] = self::serialize_argument($arg, $frame_arg_limit);
$i++;
}
return $args;
}
public static function get_frame_context($frame, $frame_arg_limit = Raven_Client::MESSAGE_LIMIT)
{
if (!isset($frame['args'])) {
return array();
}
// The reflection API seems more appropriate if we associate it with the frame
// where the function is actually called (since we're treating them as function context)
if (!isset($frame['function'])) {
return self::get_default_context($frame, $frame_arg_limit);
}
if (strpos($frame['function'], '__lambda_func') !== false) {
return self::get_default_context($frame, $frame_arg_limit);
}
if (isset($frame['class']) && $frame['class'] == 'Closure') {
return self::get_default_context($frame, $frame_arg_limit);
}
if (strpos($frame['function'], '{closure}') !== false) {
return self::get_default_context($frame, $frame_arg_limit);
}
if (in_array($frame['function'], self::$statements)) {
if (empty($frame['args'])) {
// No arguments
return array();
} else {
// Sanitize the file path
return array(
'param1' => self::serialize_argument($frame['args'][0], $frame_arg_limit),
);
}
}
try {
if (isset($frame['class'])) {
if (method_exists($frame['class'], $frame['function'])) {
$reflection = new ReflectionMethod($frame['class'], $frame['function']);
} elseif ($frame['type'] === '::') {
$reflection = new ReflectionMethod($frame['class'], '__callStatic');
} else {
$reflection = new ReflectionMethod($frame['class'], '__call');
}
} elseif (function_exists($frame['function'])) {
$reflection = new ReflectionFunction($frame['function']);
} else {
return self::get_default_context($frame, $frame_arg_limit);
}
} catch (ReflectionException $e) {
return self::get_default_context($frame, $frame_arg_limit);
}
$params = $reflection->getParameters();
$args = array();
foreach ($frame['args'] as $i => $arg) {
$arg = self::serialize_argument($arg, $frame_arg_limit);
if (isset($params[$i])) {
// Assign the argument by the parameter name
$args[$params[$i]->name] = $arg;
} else {
$args['param'.$i] = $arg;
}
}
return $args;
}
private static function serialize_argument($arg, $frame_arg_limit)
{
if (is_array($arg)) {
$_arg = array();
foreach ($arg as $key => $value) {
if (is_string($value) || is_numeric($value)) {
$_arg[$key] = Raven_Compat::substr($value, 0, $frame_arg_limit);
} else {
$_arg[$key] = $value;
}
}
return $_arg;
} elseif (is_string($arg) || is_numeric($arg)) {
return Raven_Compat::substr($arg, 0, $frame_arg_limit);
} else {
return $arg;
}
}
private static function strip_prefixes($filename, $prefixes)
{
if ($prefixes === null) {
return $filename;
}
foreach ($prefixes as $prefix) {
if (substr($filename, 0, strlen($prefix)) === $prefix) {
return substr($filename, strlen($prefix));
}
}
return $filename;
}
private static function read_source_file($filename, $lineno, $context_lines = 5)
{
$frame = array(
'prefix' => array(),
'line' => '',
'suffix' => array(),
'filename' => $filename,
'lineno' => $lineno,
);
if ($filename === null || $lineno === null) {
return $frame;
}
// Code which is eval'ed have a modified filename.. Extract the
// correct filename + linenumber from the string.
$matches = array();
$matched = preg_match("/^(.*?)\\((\\d+)\\) : eval\\(\\)'d code$/",
$filename, $matches);
if ($matched) {
$frame['filename'] = $filename = $matches[1];
$frame['lineno'] = $lineno = $matches[2];
}
// In the case of an anonymous function, the filename is sent as:
// "</path/to/filename>(<lineno>) : runtime-created function"
// Extract the correct filename + linenumber from the string.
$matches = array();
$matched = preg_match("/^(.*?)\\((\\d+)\\) : runtime-created function$/",
$filename, $matches);
if ($matched) {
$frame['filename'] = $filename = $matches[1];
$frame['lineno'] = $lineno = $matches[2];
}
if (!file_exists($filename)) {
return $frame;
}
try {
$file = new SplFileObject($filename);
$target = max(0, ($lineno - ($context_lines + 1)));
$file->seek($target);
$cur_lineno = $target+1;
while (!$file->eof()) {
$line = rtrim($file->current(), "\r\n");
if ($cur_lineno == $lineno) {
$frame['line'] = $line;
} elseif ($cur_lineno < $lineno) {
$frame['prefix'][] = $line;
} elseif ($cur_lineno > $lineno) {
$frame['suffix'][] = $line;
}
$cur_lineno++;
if ($cur_lineno > $lineno + $context_lines) {
break;
}
$file->next();
}
} catch (RuntimeException $exc) {
return $frame;
}
return $frame;
}
}

View File

@@ -0,0 +1,54 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Raven_TransactionStack
{
public function __construct()
{
$this->stack = array();
}
public function clear()
{
$this->stack = array();
}
public function peek()
{
$len = count($this->stack);
if ($len === 0) {
return null;
}
return $this->stack[$len - 1];
}
public function push($context)
{
$this->stack[] = $context;
}
/** @noinspection PhpInconsistentReturnPointsInspection
* @param string|null $context
* @return mixed
*/
public function pop($context = null)
{
if (!$context) {
return array_pop($this->stack);
}
while (!empty($this->stack)) {
if (array_pop($this->stack) === $context) {
return $context;
}
}
// @codeCoverageIgnoreStart
}
// @codeCoverageIgnoreEnd
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* This file is part of Raven.
*
* (c) Sentry Team
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Utilities
*
* @package raven
*/
class Raven_Util
{
/**
* Because we love Python, this works much like dict.get() in Python.
*
* Returns $var from $array if set, otherwise returns $default.
*
* @param array $array
* @param string $var
* @param mixed $default
* @return mixed
*/
public static function get($array, $var, $default = null)
{
if (isset($array[$var])) {
return $array[$var];
}
return $default;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,11 @@
<?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Location: ../');
exit;

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupStaticAttributes="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
bootstrap="test/bootstrap.php"
>
<testsuites>
<testsuite name="Raven Test Suite">
<directory>./test/Raven/</directory>
<directory suffix=".phpt">./test/Raven/phpt/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">./lib/Raven/</directory>
</whitelist>
</filter>
</phpunit>