This email was automatically sent by the 1Pilot Client installed on $siteUrl.
129 |
130 | Ground control to Major Tom
131 | Ground control to Major Tom
132 | Take your protein pills and put your helmet on
133 |
134 | Ground control to Major Tom
135 | (10, 9, 8, 7)
136 | Commencing countdown, engines on
137 | (6, 5, 4, 3)
138 | Check ignition, and may God's love be with you
139 | (2, 1, liftoff)
140 |
141 | This is ground control to Major Tom,
142 |
143 | You've really made the grade
144 | And the papers want to know whose shirts you wear
145 | Now it's time to leave the capsule if you dare
146 |
147 | This is Major Tom to ground control
148 | I'm stepping through the door
149 | And I'm floating in the most of peculiar way
150 | And the stars look very different today
151 |
152 | For here am I sitting in a tin can
153 | Far above the world
154 | Planet Earth is blue, and there's nothing I can do
155 |
156 | Though I'm past 100,000 miles
157 | I'm feeling very still
158 | And I think my spaceship knows which way to go
159 | Tell my wife I love her very much, she knows
160 |
161 | Ground control to Major Tom,
162 | Your circuit's dead, there's something wrong
163 | Can you hear me Major Tom?
164 | Can you hear me Major Tom?
165 | Can you hear me Major Tom?
166 | Can you...
167 |
168 | Here am I floating round my tin can
169 | Far above the moon
170 | Planet Earth is blue, and there's nothing I can do...
171 |
172 | Ground control to Major Tom,
173 | Your circuit's dead, there's something wrong
174 | Can you hear me Major Tom?
175 | Can you hear me Major Tom?
176 | Can you hear me Major Tom?
177 | Can you...
178 |
179 | Space Oddity
180 | David Bowie
181 | EOF;
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/src/Controllers/PingController.php:
--------------------------------------------------------------------------------
1 | middleware(Authentication::class);
16 | }
17 |
18 | /**
19 | * Retrieve runtime and composer package version information
20 | */
21 | public function index()
22 | {
23 | return [
24 | 'message' => "pong",
25 | ];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Controllers/VersionController.php:
--------------------------------------------------------------------------------
1 | middleware(Authentication::class);
17 | }
18 |
19 | /**
20 | * Retrieve runtime and composer package version information
21 | */
22 | public function index()
23 | {
24 | return [
25 | 'core' => $this->getVersions(),
26 | 'servers' => $this->getServers(),
27 | 'plugins' => Composer::instance()->getPackagesData(),
28 | 'extra' => $this->getExtra(),
29 | 'files' => Files::instance()->getFilesProperties(),
30 | 'errors' => $this->errorsOverview(),
31 | ];
32 | }
33 |
34 | public function getVersions()
35 | {
36 | $laravel = Composer::instance()->getLatestPackageVersion('laravel/framework', app()->version());
37 |
38 | return [
39 | 'version' => app()->version(),
40 | 'new_version' => $laravel['compatible'] ?? null,
41 | 'last_available_version' => $laravel['available'] ?? null,
42 | ];
43 | }
44 |
45 | /**
46 | * Get system versions
47 | *
48 | * @return array
49 | */
50 | public function getServers()
51 | {
52 | $serverWeb = $_SERVER['SERVER_SOFTWARE'] ?? getenv('SERVER_SOFTWARE') ?? null;
53 |
54 | return [
55 | 'php' => phpversion(),
56 | 'web' => $serverWeb,
57 | 'mysql' => $this->getDbVersion(),
58 | ];
59 | }
60 |
61 | /**
62 | * @return array
63 | */
64 | private function getExtra()
65 | {
66 | $configs = $this->config('app.debug', 'app.env', 'app.timezone');
67 |
68 | if (version_compare(app()->version(), '6', '<=')) {
69 | $configs += $this->config('mail.driver', 'mail.host');
70 | } else {
71 | $configs += $this->config('mail.default');
72 |
73 | if (config('mail.default') == 'smtp') {
74 | $configs += $this->config('mail.mailers.smtp.host');
75 | }
76 | }
77 |
78 | $configs += $this->config(
79 | 'mail.from.address',
80 | 'mail.from.name',
81 | 'queue.default',
82 | 'cache.default',
83 | 'logging.default',
84 | 'session.driver',
85 | 'session.lifetime'
86 | );
87 |
88 | $configs['storage_dir_writable'] = is_writable(base_path('storage'));
89 | $configs['cache_dir_writable'] = is_writable(base_path('bootstrap/cache'));
90 |
91 | return $configs;
92 | }
93 |
94 | private function errorsOverview()
95 | {
96 | try {
97 | return (new LogsOverview())->get();
98 | } catch (\Exception $e) {
99 | }
100 | }
101 |
102 | /**
103 | * @return string|null
104 | */
105 | private function getDbVersion()
106 | {
107 | $connection = config('database.default');
108 | $driver = config("database.connections.{$connection}.driver");
109 |
110 | try {
111 | switch ($driver) {
112 | case 'mysql':
113 | return $this->mysqlVersion();
114 | case 'sqlite':
115 | return $this->sqliteVersion();
116 | }
117 | } catch (\Exception $e) {
118 | // nothing
119 | }
120 | }
121 |
122 |
123 | /**
124 | * @return string|null
125 | */
126 | private function mysqlVersion()
127 | {
128 | $connection = config('database.default');
129 |
130 | if (
131 | in_array(config("database.connections.{$connection}.database"), ['homestead', 'forge', '']) &&
132 | in_array(config("database.connections.{$connection}.username"), ['homestead', 'forge', '']) &&
133 | in_array(config("database.connections.{$connection}.password"), ['secret', ''])
134 | ) {
135 | // default config value, connection will not work
136 | return null;
137 | }
138 |
139 | $result = DB::select('SELECT VERSION() as version;');
140 |
141 | return $result[0]->version ?? null;
142 | }
143 |
144 | /**
145 | * @return string|null
146 | */
147 | private function sqliteVersion()
148 | {
149 | $result = DB::select('select "SQLite " || sqlite_version() as version');
150 |
151 | return $result[0]->version ?? null;
152 | }
153 |
154 | /**
155 | * @param ...$keys
156 | *
157 | * @return array
158 | */
159 | private function config(...$keys)
160 | {
161 | return collect($keys)->mapWithKeys(function ($item) {
162 | return [$item => config($item)];
163 | })->toArray();
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/Exceptions/OnePilotException.php:
--------------------------------------------------------------------------------
1 | code >= 400 && $this->code < 600) ? $this->code : 500;
40 |
41 | $content = [
42 | 'message' => $this->getMessage(),
43 | 'status' => $httpCode,
44 | 'data' => [],
45 | ];
46 |
47 | if (!empty($previous = $this->getPrevious())) {
48 | $content['data']['previous'] = [
49 | 'message' => $previous->getMessage(),
50 | ];
51 | }
52 |
53 | return response($content, $httpCode);
54 | }
55 |
56 | public function report()
57 | {
58 | // disable reporting
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Middlewares/Authentication.php:
--------------------------------------------------------------------------------
1 | header('hash');
13 | $stamp = $request->header('stamp');
14 |
15 | if (!$signature) {
16 | throw OnePilotException::missingSignature();
17 | }
18 |
19 | if (!$this->isValidateTimeStamp($stamp)) {
20 | throw OnePilotException::invalidTimestamp();
21 | }
22 |
23 | if (!$this->isValidSignature($signature, $stamp)) {
24 | throw OnePilotException::invalidSignature($signature);
25 | }
26 |
27 | return $next($request);
28 | }
29 |
30 | protected function isValidSignature(string $signature, string $stamp)
31 | {
32 | $secret = config('onepilot.private_key');
33 |
34 | if (empty($secret)) {
35 | throw OnePilotException::signingPrivateKeyNotSet();
36 | }
37 |
38 | $computedSignature = hash_hmac('sha256', $stamp, $secret);
39 |
40 | return hash_equals($signature, $computedSignature);
41 | }
42 |
43 | /**
44 | * Validate timestamp. The meaning of this check is to enhance security by
45 | * making sure any token can only be used in a short period of time.
46 | *
47 | * @return boolean
48 | */
49 | private function isValidateTimeStamp($stamp)
50 | {
51 | if ($secret = config('onepilot.skip_time_stamp_validation')) {
52 | return true;
53 | }
54 |
55 | if (($stamp > time() - 360) && ($stamp < time() + 360)) {
56 | return true;
57 | }
58 |
59 | return false;
60 | }
61 | }
--------------------------------------------------------------------------------
/src/Traits/Instantiable.php:
--------------------------------------------------------------------------------
1 | getJson('onepilot/validate');
13 |
14 | $response
15 | ->assertStatus(400)
16 | ->assertJson([
17 | 'message' => "The request did not contain a header named `HTTP_HASH`.",
18 | 'status' => 400,
19 | 'data' => [],
20 | ]);
21 | }
22 |
23 | /** @test */
24 | public function it_will_fail_when_no_authentication_headers_are_set()
25 | {
26 | $response = $this->getJson('onepilot/ping', []);
27 |
28 | $response
29 | ->assertStatus(400)
30 | ->assertJson([
31 | 'message' => "The request did not contain a header named `HTTP_HASH`.",
32 | 'status' => 400,
33 | 'data' => [],
34 | ]);
35 | }
36 |
37 | /** @test */
38 | public function it_will_fail_when_using_past_stamp()
39 | {
40 | $this->setTimestamp(1500000000);
41 |
42 | $response = $this->getJson('onepilot/ping', $this->authenticationHeaders());
43 |
44 | $response
45 | ->assertStatus(400)
46 | ->assertJson([
47 | 'message' => "The timestamp found in the header is invalid",
48 | 'status' => 400,
49 | 'data' => [],
50 | ]);
51 | }
52 |
53 | /** @test */
54 | public function it_will_fail_when_using_empty_stamp()
55 | {
56 | $this->setTimestamp("");
57 |
58 | $response = $this->getJson('onepilot/ping', $this->authenticationHeaders());
59 |
60 | $response
61 | ->assertStatus(400)
62 | ->assertJson([
63 | 'message' => "The timestamp found in the header is invalid",
64 | 'status' => 400,
65 | 'data' => [],
66 | ]);
67 | }
68 |
69 | /** @test */
70 | public function it_will_work_when_using_past_stamp_with_skip_time_stamp_validation_enabled()
71 | {
72 | $this->setTimestamp(1500000000);
73 |
74 | config(['onepilot.skip_time_stamp_validation' => true]);
75 |
76 | $response = $this->getJson('onepilot/ping', $this->authenticationHeaders());
77 |
78 | $response
79 | ->assertStatus(200)
80 | ->assertJson([
81 | 'message' => "pong",
82 | ]);
83 | }
84 |
85 | /** @test */
86 | public function it_will_work_when_using_valid_stamp_and_hash()
87 | {
88 | $response = $this->getJson('onepilot/ping', $this->authenticationHeaders());
89 |
90 | $response
91 | ->assertStatus(200)
92 | ->assertJson([
93 | 'message' => "pong",
94 | ]);
95 | }
96 |
97 | }
98 |
--------------------------------------------------------------------------------
/tests/Integration/ComposerUpdatesTest.php:
--------------------------------------------------------------------------------
1 | getPackagesData());
19 |
20 | $laravelFramework = $this->findPackage($packages, 'laravel/framework');
21 |
22 | $this->assertVersionGreaterThan('5.5.40', $laravelFramework['new_version']);
23 | $this->assertVersionLessThan('5.6.0', $laravelFramework['new_version']);
24 | $this->assertVersionGreaterThan('5.7.0', $laravelFramework['last_available_version']);
25 |
26 | $symfonyConsole = $this->findPackage($packages, 'symfony/console');
27 |
28 | $this->assertVersionGreaterThan('3.4.1', $symfonyConsole['new_version']);
29 | $this->assertVersionLessThan('4.0.0', $symfonyConsole['new_version']);
30 | $this->assertVersionGreaterThan('4.1.0', $symfonyConsole['last_available_version']);
31 | }
32 |
33 | /** @test */
34 | public function laravel_56()
35 | {
36 | FakePackageDetector::setPackagesFromPath(__DIR__ . '/../data/composer/laravel56-installed-packages.json');
37 | FakePackageDetector::generatePackagesConstraints();
38 |
39 | $packages = collect((new Composer)->getPackagesData());
40 |
41 | $laravelFramework = $this->findPackage($packages, 'laravel/framework');
42 |
43 | $this->assertVersionGreaterThan('5.6.35', $laravelFramework['new_version']);
44 | $this->assertVersionLessThan('5.7.0', $laravelFramework['new_version']);
45 | $this->assertVersionGreaterThan('5.7.0', $laravelFramework['last_available_version']);
46 |
47 | $symfonyConsole = $this->findPackage($packages, 'symfony/console');
48 |
49 | $this->assertVersionGreaterThan('4.0.0', $symfonyConsole['new_version']);
50 | $this->assertVersionLessThan('5.0.0', $symfonyConsole['new_version']);
51 |
52 | $carbon = $this->findPackage($packages, 'nesbot/carbon');
53 |
54 | $this->assertEquals('1.25.0', $carbon['version']);
55 | $this->assertVersionLessThan('1.26.0', $carbon['new_version'] ?? 0);
56 | $this->assertVersionGreaterThan('2.0.0', $carbon['last_available_version']);
57 | }
58 |
59 | /** @test */
60 | public function laravel_57()
61 | {
62 | FakePackageDetector::setPackagesFromPath(__DIR__ . '/../data/composer/laravel57-installed-packages.json');
63 | FakePackageDetector::generatePackagesConstraints();
64 |
65 | $packages = collect((new Composer)->getPackagesData());
66 |
67 | $laravelFramework = $this->findPackage($packages, 'laravel/framework');
68 |
69 | $this->assertVersionGreaterThan('5.7.0', $laravelFramework['new_version']);
70 | $this->assertVersionLessThan('5.8.0', $laravelFramework['new_version']);
71 |
72 | $symfonyConsole = $this->findPackage($packages, 'symfony/console');
73 |
74 | $this->assertVersionGreaterThan('4.1.0', $symfonyConsole['new_version']);
75 | $this->assertVersionLessThan('5.0.0', $symfonyConsole['new_version']);
76 |
77 | $carbon = $this->findPackage($packages, 'nesbot/carbon');
78 |
79 | $this->assertVersionGreaterThan('1.26.3', $carbon['new_version']);
80 | $this->assertVersionLessThan('2.0.0', $carbon['new_version']);
81 | $this->assertVersionGreaterThan('2.0.0', $carbon['last_available_version']);
82 | }
83 |
84 | /** @test */
85 | public function packages_not_publicly_available_on_packagist_are_also_returned()
86 | {
87 | FakePackageDetector::setPackagesFromArray([
88 | [
89 | 'name' => 'composer/semver',
90 | 'version' => '1.4.0',
91 | 'require' => ['php' => '>=7.0.0'],
92 | ],
93 | [
94 | 'name' => 'laravel/socialite',
95 | 'version' => '3.2.0',
96 | ],
97 | [
98 | 'name' => 'laravel/nova',
99 | 'version' => '1.0.1',
100 | ],
101 | [
102 | 'name' => '1pilotapp/unknown',
103 | 'version' => '1.0.1',
104 | 'require' => ['laravel/socialite' => '^3.2'],
105 | ],
106 | ]);
107 |
108 | FakePackageDetector::generatePackagesConstraints();
109 |
110 | $packages = collect((new Composer)->getPackagesData());
111 |
112 | $this->assertCount(4, $packages);
113 |
114 | $composerSemver = $this->findPackage($packages, 'composer/semver');
115 | $this->assertVersionGreaterThanOrEqual('1.5.0', $composerSemver['new_version']);
116 | $this->assertNull($composerSemver['last_available_version']);
117 |
118 | $laravelSocialite = $this->findPackage($packages, 'laravel/socialite');
119 | $this->assertVersionGreaterThan('3.2.9', $laravelSocialite['new_version']);
120 | $this->assertVersionLessThan('4.0.0', $laravelSocialite['new_version']);
121 | $this->assertVersionGreaterThan('4.1.0', $laravelSocialite['last_available_version']);
122 |
123 | $laravelNova = $this->findPackage($packages, 'laravel/nova');
124 | $this->assertEquals('1.0.1', $laravelNova['version']);
125 | $this->assertNull($laravelNova['new_version']);
126 | $this->assertNull($laravelNova['last_available_version']);
127 | }
128 |
129 | private function findPackage(Collection $packages, string $name)
130 | {
131 | return $packages->first(function ($package) use ($name) {
132 | return $package['code'] === $name;
133 | });
134 | }
135 |
136 | private function assertVersionGreaterThan($expected, $actual)
137 | {
138 | $this->assertVersionCompare($expected, $actual, '>');
139 | }
140 |
141 | private function assertVersionGreaterThanOrEqual($expected, $actual)
142 | {
143 | $this->assertVersionCompare($expected, $actual, '>=');
144 | }
145 |
146 | private function assertVersionLessThan($expected, $actual)
147 | {
148 | $this->assertVersionCompare($expected, $actual, '<');
149 | }
150 |
151 | private function assertVersionCompare($expected, $actual, $operator)
152 | {
153 | $this->assertTrue(
154 | version_compare($actual, $expected, $operator),
155 | "Failed asserting that '{$actual}' is $operator than '$expected'"
156 | );
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/tests/Integration/MailTesterTest.php:
--------------------------------------------------------------------------------
1 | postJson('onepilot/mail-tester', [
21 | 'email' => 'test-mail@example.com',
22 | ], $this->authenticationHeaders());
23 | }
24 | }
25 |
26 | /** @test */
27 | public function require_authentication_headers()
28 | {
29 | $response = $this->postJson('onepilot/mail-tester');
30 |
31 | $response->assertStatus(400);
32 | }
33 |
34 | /** @test */
35 | public function response_is_success()
36 | {
37 | self::$response->assertStatus(200);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/tests/Integration/ValidationsTest.php:
--------------------------------------------------------------------------------
1 | getJson('onepilot/validate', $this->authenticationHeaders());
21 | }
22 | }
23 |
24 | /** @test */
25 | public function response_is_success()
26 | {
27 | self::$response->assertStatus(200);
28 | }
29 |
30 | /** @test */
31 | public function core_and_php_versions_are_right()
32 | {
33 | $data = self::$response->getOriginalContent();
34 |
35 | $this->assertEquals(app()->version(), Arr::get($data, 'core.version'));
36 |
37 | $this->assertEquals(phpversion(), Arr::get($data, 'servers.php'));
38 | }
39 |
40 | /** @test */
41 | public function all_real_packages_are_returned()
42 | {
43 | // ignore the project package (if it not have a version number)
44 | $packages = (new FakePackageDetector)->getPackages()
45 | ->filter(function ($package) {
46 | return !empty($package->version);
47 | })
48 | ->count();
49 |
50 | $data = self::$response->getOriginalContent();
51 | $this->assertEquals($packages, count($data['plugins']));
52 | }
53 |
54 | /** @test */
55 | public function extra_parameters()
56 | {
57 | $data = self::$response->getOriginalContent();
58 |
59 | $this->assertEquals(App::environment(), $data['extra']['app.env'] ?? null);
60 |
61 | $this->assertEquals(config('app.debug'), $data['extra']['app.debug'] ?? null);
62 | }
63 |
64 | /** @test */
65 | public function files_contains_config_app()
66 | {
67 | $files = collect(self::$response->getOriginalContent()['files'] ?? []);
68 |
69 | $configApp = $files->first(function ($file) {
70 | return $file['path'] == 'config'. DIRECTORY_SEPARATOR .'app.php';
71 | });
72 |
73 | $this->assertNotNull($configApp['checksum']);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/TestCase.php:
--------------------------------------------------------------------------------
1 | $this->privateKey]);
21 |
22 | $this->setTimestamp();
23 |
24 | $this->app->bind(PackageDetector::class, FakePackageDetector::class);
25 |
26 | FakePackageDetector::setPackagesFromPath(__DIR__ . '/data/composer/installed-packages-light.json');
27 | FakePackageDetector::generatePackagesConstraints();
28 | }
29 |
30 | /**
31 | * @param \Illuminate\Foundation\Application $app
32 | *
33 | * @return array
34 | */
35 | protected function getPackageProviders($app)
36 | {
37 | return [
38 | ClientServiceProvider::class,
39 | ];
40 | }
41 |
42 | /**
43 | * Set timestamp and regenerate hash
44 | *
45 | * @param null $timestamp
46 | */
47 | protected function setTimestamp($timestamp = null)
48 | {
49 | $this->timestamp = $timestamp ?? time();
50 |
51 | $this->hash = $this->generateAuthenticationHash($this->privateKey, $this->timestamp);
52 | }
53 |
54 | /**
55 | * @param string $privateKey
56 | * @param int $timestamp
57 | *
58 | * @return string Hash
59 | */
60 | protected function generateAuthenticationHash($privateKey, $timestamp)
61 | {
62 | return hash_hmac('sha256', $timestamp, $privateKey);
63 | }
64 |
65 | protected function authenticationHeaders()
66 | {
67 | return [
68 | 'hash' => $this->hash,
69 | 'stamp' => $this->timestamp,
70 | ];
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tests/data/composer/installed-packages-light.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "require": {
4 | "php": ">=7.0.0",
5 | "fideloper/proxy": "~3.3",
6 | "laravel/framework": "5.5.*",
7 | "laravel/tinker": "~1.0"
8 | }
9 | },
10 | {
11 | "name": "laravel/framework",
12 | "version": "v5.5.14",
13 | "version_normalized": "5.5.14.0",
14 | "require": {
15 | "doctrine/inflector": "~1.1",
16 | "erusev/parsedown": "~1.6",
17 | "ext-mbstring": "*",
18 | "ext-openssl": "*",
19 | "league/flysystem": "~1.0",
20 | "monolog/monolog": "~1.12",
21 | "mtdowling/cron-expression": "~1.0",
22 | "nesbot/carbon": "~1.20",
23 | "php": ">=7.0",
24 | "psr/container": "~1.0",
25 | "psr/simple-cache": "^1.0",
26 | "ramsey/uuid": "~3.0",
27 | "swiftmailer/swiftmailer": "~6.0",
28 | "symfony/console": "~3.3",
29 | "symfony/debug": "~3.3",
30 | "symfony/finder": "~3.3",
31 | "symfony/http-foundation": "~3.3",
32 | "symfony/http-kernel": "~3.3",
33 | "symfony/process": "~3.3",
34 | "symfony/routing": "~3.3",
35 | "symfony/var-dumper": "~3.3",
36 | "tijsverkoyen/css-to-inline-styles": "~2.2",
37 | "vlucas/phpdotenv": "~2.2"
38 | }
39 | },
40 | {
41 | "name": "symfony/http-kernel",
42 | "version": "v3.3.10",
43 | "version_normalized": "3.3.10.0",
44 | "require": {
45 | "php": "^5.5.9|>=7.0.8",
46 | "psr/log": "~1.0",
47 | "symfony/debug": "~2.8|~3.0",
48 | "symfony/event-dispatcher": "~2.8|~3.0",
49 | "symfony/http-foundation": "~3.3"
50 | }
51 | },
52 | {
53 | "name": "erusev/parsedown",
54 | "version": "1.6.0",
55 | "version_normalized": "1.6.0.0"
56 | }
57 | ]
--------------------------------------------------------------------------------
/tests/data/composer/laravel55-installed-packages.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "name": "laravel/laravel",
4 | "require": {
5 | "php": ">=7.0.0",
6 | "fideloper/proxy": "~3.3",
7 | "laravel/framework": "5.5.*",
8 | "laravel/tinker": "~1.0"
9 | }
10 | },
11 | {
12 | "name": "composer/semver",
13 | "version": "1.4.2",
14 | "version_normalized": "1.4.2.0",
15 | "require": {
16 | "php": "^5.3.2 || ^7.0"
17 | }
18 | },
19 | {
20 | "name": "dnoegel/php-xdg-base-dir",
21 | "version": "0.1",
22 | "version_normalized": "0.1.0.0",
23 | "require": {
24 | "php": ">=5.3.2"
25 | }
26 | },
27 | {
28 | "name": "doctrine/inflector",
29 | "version": "v1.2.0",
30 | "version_normalized": "1.2.0.0",
31 | "require": {
32 | "php": "^7.0"
33 | }
34 | },
35 | {
36 | "name": "doctrine/instantiator",
37 | "version": "1.0.5",
38 | "version_normalized": "1.0.5.0",
39 | "require": {
40 | "php": ">=5.3,<8.0-DEV"
41 | }
42 | },
43 | {
44 | "name": "doctrine/lexer",
45 | "version": "v1.0.1",
46 | "version_normalized": "1.0.1.0",
47 | "require": {
48 | "php": ">=5.3.2"
49 | }
50 | },
51 | {
52 | "name": "egulias/email-validator",
53 | "version": "2.1.2",
54 | "version_normalized": "2.1.2.0",
55 | "require": {
56 | "doctrine/lexer": "^1.0.1",
57 | "php": ">= 5.5"
58 | }
59 | },
60 | {
61 | "name": "erusev/parsedown",
62 | "version": "1.6.3",
63 | "version_normalized": "1.6.3.0",
64 | "require": {
65 | "php": ">=5.3.0"
66 | }
67 | },
68 | {
69 | "name": "fideloper/proxy",
70 | "version": "3.3.4",
71 | "version_normalized": "3.3.4.0",
72 | "require": {
73 | "illuminate/contracts": "~5.0",
74 | "php": ">=5.4.0"
75 | }
76 | },
77 | {
78 | "name": "filp/whoops",
79 | "version": "2.1.10",
80 | "version_normalized": "2.1.10.0",
81 | "require": {
82 | "php": "^5.5.9 || ^7.0",
83 | "psr/log": "^1.0.1"
84 | }
85 | },
86 | {
87 | "name": "fzaninotto/faker",
88 | "version": "v1.7.1",
89 | "version_normalized": "1.7.1.0",
90 | "require": {
91 | "php": "^5.3.3 || ^7.0"
92 | }
93 | },
94 | {
95 | "name": "hamcrest/hamcrest-php",
96 | "version": "v1.2.2",
97 | "version_normalized": "1.2.2.0",
98 | "require": {
99 | "php": ">=5.3.2"
100 | }
101 | },
102 | {
103 | "name": "jakub-onderka/php-console-color",
104 | "version": "0.1",
105 | "version_normalized": "0.1.0.0",
106 | "require": {
107 | "php": ">=5.3.2"
108 | }
109 | },
110 | {
111 | "name": "jakub-onderka/php-console-highlighter",
112 | "version": "v0.3.2",
113 | "version_normalized": "0.3.2.0",
114 | "require": {
115 | "jakub-onderka/php-console-color": "~0.1",
116 | "php": ">=5.3.0"
117 | }
118 | },
119 | {
120 | "name": "laravel/framework",
121 | "version": "v5.5.14",
122 | "version_normalized": "5.5.14.0",
123 | "require": {
124 | "doctrine/inflector": "~1.1",
125 | "erusev/parsedown": "~1.6",
126 | "ext-mbstring": "*",
127 | "ext-openssl": "*",
128 | "league/flysystem": "~1.0",
129 | "monolog/monolog": "~1.12",
130 | "mtdowling/cron-expression": "~1.0",
131 | "nesbot/carbon": "~1.20",
132 | "php": ">=7.0",
133 | "psr/container": "~1.0",
134 | "psr/simple-cache": "^1.0",
135 | "ramsey/uuid": "~3.0",
136 | "swiftmailer/swiftmailer": "~6.0",
137 | "symfony/console": "~3.3",
138 | "symfony/debug": "~3.3",
139 | "symfony/finder": "~3.3",
140 | "symfony/http-foundation": "~3.3",
141 | "symfony/http-kernel": "~3.3",
142 | "symfony/process": "~3.3",
143 | "symfony/routing": "~3.3",
144 | "symfony/var-dumper": "~3.3",
145 | "tijsverkoyen/css-to-inline-styles": "~2.2",
146 | "vlucas/phpdotenv": "~2.2"
147 | }
148 | },
149 | {
150 | "name": "laravel/tinker",
151 | "version": "v1.0.2",
152 | "version_normalized": "1.0.2.0",
153 | "require": {
154 | "illuminate/console": "~5.1",
155 | "illuminate/contracts": "~5.1",
156 | "illuminate/support": "~5.1",
157 | "php": ">=5.5.9",
158 | "psy/psysh": "0.7.*|0.8.*",
159 | "symfony/var-dumper": "~3.0"
160 | }
161 | },
162 | {
163 | "name": "league/flysystem",
164 | "version": "1.0.41",
165 | "version_normalized": "1.0.41.0",
166 | "require": {
167 | "php": ">=5.5.9"
168 | }
169 | },
170 | {
171 | "name": "mockery/mockery",
172 | "version": "0.9.9",
173 | "version_normalized": "0.9.9.0",
174 | "require": {
175 | "hamcrest/hamcrest-php": "~1.1",
176 | "lib-pcre": ">=7.0",
177 | "php": ">=5.3.2"
178 | }
179 | },
180 | {
181 | "name": "monolog/monolog",
182 | "version": "1.23.0",
183 | "version_normalized": "1.23.0.0",
184 | "require": {
185 | "php": ">=5.3.0",
186 | "psr/log": "~1.0"
187 | }
188 | },
189 | {
190 | "name": "mtdowling/cron-expression",
191 | "version": "v1.2.0",
192 | "version_normalized": "1.2.0.0",
193 | "require": {
194 | "php": ">=5.3.2"
195 | }
196 | },
197 | {
198 | "name": "myclabs/deep-copy",
199 | "version": "1.6.1",
200 | "version_normalized": "1.6.1.0",
201 | "require": {
202 | "php": ">=5.4.0"
203 | }
204 | },
205 | {
206 | "name": "nesbot/carbon",
207 | "version": "1.22.1",
208 | "version_normalized": "1.22.1.0",
209 | "require": {
210 | "php": ">=5.3.0",
211 | "symfony/translation": "~2.6 || ~3.0"
212 | }
213 | },
214 | {
215 | "name": "nikic/php-parser",
216 | "version": "v3.1.1",
217 | "version_normalized": "3.1.1.0",
218 | "require": {
219 | "ext-tokenizer": "*",
220 | "php": ">=5.5"
221 | }
222 | },
223 | {
224 | "name": "paragonie/random_compat",
225 | "version": "v2.0.11",
226 | "version_normalized": "2.0.11.0",
227 | "require": {
228 | "php": ">=5.2.0"
229 | }
230 | },
231 | {
232 | "name": "phar-io/manifest",
233 | "version": "1.0.1",
234 | "version_normalized": "1.0.1.0",
235 | "require": {
236 | "ext-dom": "*",
237 | "ext-phar": "*",
238 | "phar-io/version": "^1.0.1",
239 | "php": "^5.6 || ^7.0"
240 | }
241 | },
242 | {
243 | "name": "phar-io/version",
244 | "version": "1.0.1",
245 | "version_normalized": "1.0.1.0",
246 | "require": {
247 | "php": "^5.6 || ^7.0"
248 | }
249 | },
250 | {
251 | "name": "phpdocumentor/reflection-common",
252 | "version": "1.0.1",
253 | "version_normalized": "1.0.1.0",
254 | "require": {
255 | "php": ">=5.5"
256 | }
257 | },
258 | {
259 | "name": "phpdocumentor/reflection-docblock",
260 | "version": "4.1.1",
261 | "version_normalized": "4.1.1.0",
262 | "require": {
263 | "php": "^7.0",
264 | "phpdocumentor/reflection-common": "^1.0@dev",
265 | "phpdocumentor/type-resolver": "^0.4.0",
266 | "webmozart/assert": "^1.0"
267 | }
268 | },
269 | {
270 | "name": "phpdocumentor/type-resolver",
271 | "version": "0.4.0",
272 | "version_normalized": "0.4.0.0",
273 | "require": {
274 | "php": "^5.5 || ^7.0",
275 | "phpdocumentor/reflection-common": "^1.0"
276 | }
277 | },
278 | {
279 | "name": "phpspec/prophecy",
280 | "version": "v1.7.2",
281 | "version_normalized": "1.7.2.0",
282 | "require": {
283 | "doctrine/instantiator": "^1.0.2",
284 | "php": "^5.3|^7.0",
285 | "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
286 | "sebastian/comparator": "^1.1|^2.0",
287 | "sebastian/recursion-context": "^1.0|^2.0|^3.0"
288 | }
289 | },
290 | {
291 | "name": "phpunit/php-code-coverage",
292 | "version": "5.2.2",
293 | "version_normalized": "5.2.2.0",
294 | "require": {
295 | "ext-dom": "*",
296 | "ext-xmlwriter": "*",
297 | "php": "^7.0",
298 | "phpunit/php-file-iterator": "^1.4.2",
299 | "phpunit/php-text-template": "^1.2.1",
300 | "phpunit/php-token-stream": "^1.4.11 || ^2.0",
301 | "sebastian/code-unit-reverse-lookup": "^1.0.1",
302 | "sebastian/environment": "^3.0",
303 | "sebastian/version": "^2.0.1",
304 | "theseer/tokenizer": "^1.1"
305 | }
306 | },
307 | {
308 | "name": "phpunit/php-file-iterator",
309 | "version": "1.4.2",
310 | "version_normalized": "1.4.2.0",
311 | "require": {
312 | "php": ">=5.3.3"
313 | }
314 | },
315 | {
316 | "name": "phpunit/php-text-template",
317 | "version": "1.2.1",
318 | "version_normalized": "1.2.1.0",
319 | "require": {
320 | "php": ">=5.3.3"
321 | }
322 | },
323 | {
324 | "name": "phpunit/php-timer",
325 | "version": "1.0.9",
326 | "version_normalized": "1.0.9.0",
327 | "require": {
328 | "php": "^5.3.3 || ^7.0"
329 | }
330 | },
331 | {
332 | "name": "phpunit/php-token-stream",
333 | "version": "2.0.1",
334 | "version_normalized": "2.0.1.0",
335 | "require": {
336 | "ext-tokenizer": "*",
337 | "php": "^7.0"
338 | }
339 | },
340 | {
341 | "name": "phpunit/phpunit",
342 | "version": "6.4.1",
343 | "version_normalized": "6.4.1.0",
344 | "require": {
345 | "ext-dom": "*",
346 | "ext-json": "*",
347 | "ext-libxml": "*",
348 | "ext-mbstring": "*",
349 | "ext-xml": "*",
350 | "myclabs/deep-copy": "^1.6.1",
351 | "phar-io/manifest": "^1.0.1",
352 | "phar-io/version": "^1.0",
353 | "php": "^7.0",
354 | "phpspec/prophecy": "^1.7",
355 | "phpunit/php-code-coverage": "^5.2.2",
356 | "phpunit/php-file-iterator": "^1.4.2",
357 | "phpunit/php-text-template": "^1.2.1",
358 | "phpunit/php-timer": "^1.0.9",
359 | "phpunit/phpunit-mock-objects": "^4.0.3",
360 | "sebastian/comparator": "^2.0.2",
361 | "sebastian/diff": "^2.0",
362 | "sebastian/environment": "^3.1",
363 | "sebastian/exporter": "^3.1",
364 | "sebastian/global-state": "^2.0",
365 | "sebastian/object-enumerator": "^3.0.3",
366 | "sebastian/resource-operations": "^1.0",
367 | "sebastian/version": "^2.0.1"
368 | }
369 | },
370 | {
371 | "name": "phpunit/phpunit-mock-objects",
372 | "version": "4.0.4",
373 | "version_normalized": "4.0.4.0",
374 | "require": {
375 | "doctrine/instantiator": "^1.0.5",
376 | "php": "^7.0",
377 | "phpunit/php-text-template": "^1.2.1",
378 | "sebastian/exporter": "^3.0"
379 | }
380 | },
381 | {
382 | "name": "psr/container",
383 | "version": "1.0.0",
384 | "version_normalized": "1.0.0.0",
385 | "require": {
386 | "php": ">=5.3.0"
387 | }
388 | },
389 | {
390 | "name": "psr/log",
391 | "version": "1.0.2",
392 | "version_normalized": "1.0.2.0",
393 | "require": {
394 | "php": ">=5.3.0"
395 | }
396 | },
397 | {
398 | "name": "psr/simple-cache",
399 | "version": "1.0.0",
400 | "version_normalized": "1.0.0.0",
401 | "require": {
402 | "php": ">=5.3.0"
403 | }
404 | },
405 | {
406 | "name": "psy/psysh",
407 | "version": "v0.8.11",
408 | "version_normalized": "0.8.11.0",
409 | "require": {
410 | "dnoegel/php-xdg-base-dir": "0.1",
411 | "jakub-onderka/php-console-highlighter": "0.3.*",
412 | "nikic/php-parser": "~1.3|~2.0|~3.0",
413 | "php": ">=5.3.9",
414 | "symfony/console": "~2.3.10|^2.4.2|~3.0",
415 | "symfony/var-dumper": "~2.7|~3.0"
416 | }
417 | },
418 | {
419 | "name": "ramsey/uuid",
420 | "version": "3.7.1",
421 | "version_normalized": "3.7.1.0",
422 | "require": {
423 | "paragonie/random_compat": "^1.0|^2.0",
424 | "php": "^5.4 || ^7.0"
425 | }
426 | },
427 | {
428 | "name": "sebastian/code-unit-reverse-lookup",
429 | "version": "1.0.1",
430 | "version_normalized": "1.0.1.0",
431 | "require": {
432 | "php": "^5.6 || ^7.0"
433 | }
434 | },
435 | {
436 | "name": "sebastian/comparator",
437 | "version": "2.0.2",
438 | "version_normalized": "2.0.2.0",
439 | "require": {
440 | "php": "^7.0",
441 | "sebastian/diff": "^2.0",
442 | "sebastian/exporter": "^3.0"
443 | }
444 | },
445 | {
446 | "name": "sebastian/diff",
447 | "version": "2.0.1",
448 | "version_normalized": "2.0.1.0",
449 | "require": {
450 | "php": "^7.0"
451 | }
452 | },
453 | {
454 | "name": "sebastian/environment",
455 | "version": "3.1.0",
456 | "version_normalized": "3.1.0.0",
457 | "require": {
458 | "php": "^7.0"
459 | }
460 | },
461 | {
462 | "name": "sebastian/exporter",
463 | "version": "3.1.0",
464 | "version_normalized": "3.1.0.0",
465 | "require": {
466 | "php": "^7.0",
467 | "sebastian/recursion-context": "^3.0"
468 | }
469 | },
470 | {
471 | "name": "sebastian/global-state",
472 | "version": "2.0.0",
473 | "version_normalized": "2.0.0.0",
474 | "require": {
475 | "php": "^7.0"
476 | }
477 | },
478 | {
479 | "name": "sebastian/object-enumerator",
480 | "version": "3.0.3",
481 | "version_normalized": "3.0.3.0",
482 | "require": {
483 | "php": "^7.0",
484 | "sebastian/object-reflector": "^1.1.1",
485 | "sebastian/recursion-context": "^3.0"
486 | }
487 | },
488 | {
489 | "name": "sebastian/object-reflector",
490 | "version": "1.1.1",
491 | "version_normalized": "1.1.1.0",
492 | "require": {
493 | "php": "^7.0"
494 | }
495 | },
496 | {
497 | "name": "sebastian/recursion-context",
498 | "version": "3.0.0",
499 | "version_normalized": "3.0.0.0",
500 | "require": {
501 | "php": "^7.0"
502 | }
503 | },
504 | {
505 | "name": "sebastian/resource-operations",
506 | "version": "1.0.0",
507 | "version_normalized": "1.0.0.0",
508 | "require": {
509 | "php": ">=5.6.0"
510 | }
511 | },
512 | {
513 | "name": "sebastian/version",
514 | "version": "2.0.1",
515 | "version_normalized": "2.0.1.0",
516 | "require": {
517 | "php": ">=5.6"
518 | }
519 | },
520 | {
521 | "name": "swiftmailer/swiftmailer",
522 | "version": "v6.0.2",
523 | "version_normalized": "6.0.2.0",
524 | "require": {
525 | "egulias/email-validator": "~2.0",
526 | "php": ">=7.0.0"
527 | }
528 | },
529 | {
530 | "name": "symfony/console",
531 | "version": "v3.3.10",
532 | "version_normalized": "3.3.10.0",
533 | "require": {
534 | "php": "^5.5.9|>=7.0.8",
535 | "symfony/debug": "~2.8|~3.0",
536 | "symfony/polyfill-mbstring": "~1.0"
537 | }
538 | },
539 | {
540 | "name": "symfony/css-selector",
541 | "version": "v3.3.10",
542 | "version_normalized": "3.3.10.0",
543 | "require": {
544 | "php": "^5.5.9|>=7.0.8"
545 | }
546 | },
547 | {
548 | "name": "symfony/debug",
549 | "version": "v3.3.10",
550 | "version_normalized": "3.3.10.0",
551 | "require": {
552 | "php": "^5.5.9|>=7.0.8",
553 | "psr/log": "~1.0"
554 | }
555 | },
556 | {
557 | "name": "symfony/event-dispatcher",
558 | "version": "v3.3.10",
559 | "version_normalized": "3.3.10.0",
560 | "require": {
561 | "php": "^5.5.9|>=7.0.8"
562 | }
563 | },
564 | {
565 | "name": "symfony/finder",
566 | "version": "v3.3.10",
567 | "version_normalized": "3.3.10.0",
568 | "require": {
569 | "php": "^5.5.9|>=7.0.8"
570 | }
571 | },
572 | {
573 | "name": "symfony/http-foundation",
574 | "version": "v3.3.10",
575 | "version_normalized": "3.3.10.0",
576 | "require": {
577 | "php": "^5.5.9|>=7.0.8",
578 | "symfony/polyfill-mbstring": "~1.1"
579 | }
580 | },
581 | {
582 | "name": "symfony/http-kernel",
583 | "version": "v3.3.10",
584 | "version_normalized": "3.3.10.0",
585 | "require": {
586 | "php": "^5.5.9|>=7.0.8",
587 | "psr/log": "~1.0",
588 | "symfony/debug": "~2.8|~3.0",
589 | "symfony/event-dispatcher": "~2.8|~3.0",
590 | "symfony/http-foundation": "~3.3"
591 | }
592 | },
593 | {
594 | "name": "symfony/polyfill-mbstring",
595 | "version": "v1.5.0",
596 | "version_normalized": "1.5.0.0",
597 | "require": {
598 | "php": ">=5.3.3"
599 | }
600 | },
601 | {
602 | "name": "symfony/process",
603 | "version": "v3.3.10",
604 | "version_normalized": "3.3.10.0",
605 | "require": {
606 | "php": "^5.5.9|>=7.0.8"
607 | }
608 | },
609 | {
610 | "name": "symfony/routing",
611 | "version": "v3.3.10",
612 | "version_normalized": "3.3.10.0",
613 | "require": {
614 | "php": "^5.5.9|>=7.0.8"
615 | }
616 | },
617 | {
618 | "name": "symfony/translation",
619 | "version": "v3.3.10",
620 | "version_normalized": "3.3.10.0",
621 | "require": {
622 | "php": "^5.5.9|>=7.0.8",
623 | "symfony/polyfill-mbstring": "~1.0"
624 | }
625 | },
626 | {
627 | "name": "symfony/var-dumper",
628 | "version": "v3.3.10",
629 | "version_normalized": "3.3.10.0",
630 | "require": {
631 | "php": "^5.5.9|>=7.0.8",
632 | "symfony/polyfill-mbstring": "~1.0"
633 | }
634 | },
635 | {
636 | "name": "theseer/tokenizer",
637 | "version": "1.1.0",
638 | "version_normalized": "1.1.0.0",
639 | "require": {
640 | "ext-dom": "*",
641 | "ext-tokenizer": "*",
642 | "ext-xmlwriter": "*",
643 | "php": "^7.0"
644 | }
645 | },
646 | {
647 | "name": "tijsverkoyen/css-to-inline-styles",
648 | "version": "2.2.0",
649 | "version_normalized": "2.2.0.0",
650 | "require": {
651 | "php": "^5.5 || ^7",
652 | "symfony/css-selector": "^2.7|~3.0"
653 | }
654 | },
655 | {
656 | "name": "vlucas/phpdotenv",
657 | "version": "v2.4.0",
658 | "version_normalized": "2.4.0.0",
659 | "require": {
660 | "php": ">=5.3.9"
661 | }
662 | },
663 | {
664 | "name": "webmozart/assert",
665 | "version": "1.2.0",
666 | "version_normalized": "1.2.0.0",
667 | "require": {
668 | "php": "^5.3.3 || ^7.0"
669 | }
670 | }
671 | ]
--------------------------------------------------------------------------------