diff --git a/Dockerfile b/Dockerfile
index 6468418f..2f1ae547 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -11,4 +11,6 @@ COPY ./vapi /var/www/html/vapi
RUN rm /var/www/html/vapi/.env
+RUN echo "flag{ssrf_e0pgt3az9zeqdd4fhatc}" > /flag.txt
+
CMD ["php","/var/www/html/vapi/artisan","serve","--host=0.0.0.0"]
diff --git a/composer.json b/composer.json
new file mode 100644
index 00000000..a39e3134
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,5 @@
+{
+ "require": {
+ "spatie/array-to-xml": "^2.16"
+ }
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 00000000..0705a50b
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,79 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "c80c49a31654f45ba700c4180ee11b35",
+ "packages": [
+ {
+ "name": "spatie/array-to-xml",
+ "version": "2.16.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/array-to-xml.git",
+ "reference": "db39308c5236b69b89cadc3f44f191704814eae2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/db39308c5236b69b89cadc3f44f191704814eae2",
+ "reference": "db39308c5236b69b89cadc3f44f191704814eae2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": "^7.4|^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.2",
+ "phpunit/phpunit": "^9.0",
+ "spatie/phpunit-snapshot-assertions": "^4.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\ArrayToXml\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://freek.dev",
+ "role": "Developer"
+ }
+ ],
+ "description": "Convert an array to xml",
+ "homepage": "https://github.com/spatie/array-to-xml",
+ "keywords": [
+ "array",
+ "convert",
+ "xml"
+ ],
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ],
+ "time": "2020-11-18T22:03:17+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "plugin-api-version": "1.1.0"
+}
diff --git a/database/vapi.sql b/database/vapi.sql
index 378cdcb7..be961e79 100644
--- a/database/vapi.sql
+++ b/database/vapi.sql
@@ -1,11 +1,11 @@
-- phpMyAdmin SQL Dump
--- version 5.0.2
+-- version 5.2.0
-- https://www.phpmyadmin.net/
--
--- Host: 127.0.0.1
--- Generation Time: Sep 09, 2021 at 04:08 PM
--- Server version: 10.4.13-MariaDB
--- PHP Version: 7.4.7
+-- Host: db
+-- Generation Time: Nov 12, 2022 at 01:38 AM
+-- Server version: 8.0.31
+-- PHP Version: 8.0.19
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
START TRANSACTION;
@@ -28,11 +28,11 @@ SET time_zone = "+00:00";
--
CREATE TABLE `a_p_i1_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `course` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `course` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -52,14 +52,14 @@ INSERT INTO `a_p_i1_users` (`id`, `username`, `name`, `course`, `password`) VALU
--
CREATE TABLE `a_p_i2_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `address` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `city` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `country` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `city` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `country` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -78,13 +78,13 @@ INSERT INTO `a_p_i2_users` (`id`, `email`, `password`, `name`, `token`, `address
--
CREATE TABLE `a_p_i3_comments` (
- `id` int(10) UNSIGNED NOT NULL,
- `postid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `deviceid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `latitude` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `longitude` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `commenttext` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `postid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `deviceid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `latitude` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `longitude` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `commenttext` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -101,10 +101,10 @@ INSERT INTO `a_p_i3_comments` (`id`, `postid`, `deviceid`, `latitude`, `longitud
--
CREATE TABLE `a_p_i3_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -121,10 +121,10 @@ INSERT INTO `a_p_i3_users` (`id`, `username`, `name`, `password`) VALUES
--
CREATE TABLE `a_p_i4_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `apikey` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `firstname` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `lastname` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `apikey` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `firstname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `lastname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -141,12 +141,12 @@ INSERT INTO `a_p_i4_users` (`id`, `apikey`, `firstname`, `lastname`) VALUES
--
CREATE TABLE `a_p_i5_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `address` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `mobileno` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `mobileno` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -163,11 +163,11 @@ INSERT INTO `a_p_i5_users` (`id`, `username`, `password`, `name`, `address`, `mo
--
CREATE TABLE `a_p_i6_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `credit` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0'
+ `id` int UNSIGNED NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `credit` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -184,10 +184,10 @@ INSERT INTO `a_p_i6_users` (`id`, `name`, `username`, `password`, `credit`) VALU
--
CREATE TABLE `a_p_i7_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `authkey` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `authkey` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -204,11 +204,11 @@ INSERT INTO `a_p_i7_users` (`id`, `username`, `password`, `authkey`) VALUES
--
CREATE TABLE `a_p_i8_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `authkey` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `secret` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `authkey` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -225,10 +225,10 @@ INSERT INTO `a_p_i8_users` (`id`, `username`, `password`, `authkey`, `secret`) V
--
CREATE TABLE `a_p_i9_users` (
- `id` int(10) UNSIGNED NOT NULL,
- `username` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `pin` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `accbalance` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `pin` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `accbalance` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -245,25 +245,48 @@ INSERT INTO `a_p_i9_users` (`id`, `username`, `pin`, `accbalance`) VALUES
--
CREATE TABLE `failed_jobs` (
- `id` bigint(20) UNSIGNED NOT NULL,
- `uuid` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `connection` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `queue` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `payload` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `exception` longtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `failed_at` timestamp NOT NULL DEFAULT current_timestamp()
+ `id` bigint UNSIGNED NOT NULL,
+ `uuid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `connection` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `queue` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `payload` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `exception` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `failed_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
+--
+-- Table structure for table `flags`
+--
+
+CREATE TABLE `flags` (
+ `challengeid` int UNSIGNED NOT NULL,
+ `flag` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- --------------------------------------------------------
+
+--
+-- Table structure for table `justweaktokens`
+--
+
+CREATE TABLE `justweaktokens` (
+ `id` int UNSIGNED NOT NULL,
+ `username` varchar(255) NOT NULL,
+ `password` varchar(255) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- --------------------------------------------------------
+
--
-- Table structure for table `migrations`
--
CREATE TABLE `migrations` (
- `id` int(10) UNSIGNED NOT NULL,
- `migration` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `batch` int(11) NOT NULL
+ `id` int UNSIGNED NOT NULL,
+ `migration` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `batch` int NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -293,8 +316,8 @@ INSERT INTO `migrations` (`id`, `migration`, `batch`) VALUES
--
CREATE TABLE `password_resets` (
- `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `token` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -305,12 +328,12 @@ CREATE TABLE `password_resets` (
--
CREATE TABLE `personal_access_tokens` (
- `id` bigint(20) UNSIGNED NOT NULL,
- `tokenable_type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `tokenable_id` bigint(20) UNSIGNED NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `token` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `abilities` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `id` bigint UNSIGNED NOT NULL,
+ `tokenable_type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `tokenable_id` bigint UNSIGNED NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `token` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `abilities` text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci,
`last_used_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
@@ -318,17 +341,28 @@ CREATE TABLE `personal_access_tokens` (
-- --------------------------------------------------------
+--
+-- Table structure for table `stickynotes`
+--
+
+CREATE TABLE `stickynotes` (
+ `id` int NOT NULL,
+ `note` varchar(255) NOT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- --------------------------------------------------------
+
--
-- Table structure for table `users`
--
CREATE TABLE `users` (
- `id` bigint(20) UNSIGNED NOT NULL,
- `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `id` bigint UNSIGNED NOT NULL,
+ `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`email_verified_at` timestamp NULL DEFAULT NULL,
- `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
+ `remember_token` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -405,6 +439,19 @@ ALTER TABLE `failed_jobs`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `failed_jobs_uuid_unique` (`uuid`);
+--
+-- Indexes for table `flags`
+--
+ALTER TABLE `flags`
+ ADD PRIMARY KEY (`challengeid`);
+
+--
+-- Indexes for table `justweaktokens`
+--
+ALTER TABLE `justweaktokens`
+ ADD PRIMARY KEY (`id`),
+ ADD UNIQUE KEY `username` (`username`);
+
--
-- Indexes for table `migrations`
--
@@ -425,6 +472,12 @@ ALTER TABLE `personal_access_tokens`
ADD UNIQUE KEY `personal_access_tokens_token_unique` (`token`),
ADD KEY `personal_access_tokens_tokenable_type_tokenable_id_index` (`tokenable_type`,`tokenable_id`);
+--
+-- Indexes for table `stickynotes`
+--
+ALTER TABLE `stickynotes`
+ ADD PRIMARY KEY (`id`);
+
--
-- Indexes for table `users`
--
@@ -440,85 +493,103 @@ ALTER TABLE `users`
-- AUTO_INCREMENT for table `a_p_i1_users`
--
ALTER TABLE `a_p_i1_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
--
-- AUTO_INCREMENT for table `a_p_i2_users`
--
ALTER TABLE `a_p_i2_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- AUTO_INCREMENT for table `a_p_i3_comments`
--
ALTER TABLE `a_p_i3_comments`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `a_p_i3_users`
--
ALTER TABLE `a_p_i3_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `a_p_i4_users`
--
ALTER TABLE `a_p_i4_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `a_p_i5_users`
--
ALTER TABLE `a_p_i5_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `a_p_i6_users`
--
ALTER TABLE `a_p_i6_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `a_p_i7_users`
--
ALTER TABLE `a_p_i7_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `a_p_i8_users`
--
ALTER TABLE `a_p_i8_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `a_p_i9_users`
--
ALTER TABLE `a_p_i9_users`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT for table `failed_jobs`
--
ALTER TABLE `failed_jobs`
- MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+ MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT for table `flags`
+--
+ALTER TABLE `flags`
+ MODIFY `challengeid` int UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT for table `justweaktokens`
+--
+ALTER TABLE `justweaktokens`
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `migrations`
--
ALTER TABLE `migrations`
- MODIFY `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=28;
+ MODIFY `id` int UNSIGNED NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=28;
--
-- AUTO_INCREMENT for table `personal_access_tokens`
--
ALTER TABLE `personal_access_tokens`
- MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+ MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
+
+--
+-- AUTO_INCREMENT for table `stickynotes`
+--
+ALTER TABLE `stickynotes`
+ MODIFY `id` int NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT for table `users`
--
ALTER TABLE `users`
- MODIFY `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT;
+ MODIFY `id` bigint UNSIGNED NOT NULL AUTO_INCREMENT;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
diff --git a/postman/vAPI.postman_collection.json b/postman/vAPI.postman_collection.json
index 44c49dca..07a9da04 100644
--- a/postman/vAPI.postman_collection.json
+++ b/postman/vAPI.postman_collection.json
@@ -1,6 +1,6 @@
{
"info": {
- "_postman_id": "679f51bd-d932-4d3f-8fab-c4180cdeccd0",
+ "_postman_id": "054ba5ba-7ee9-48a7-9dc6-09ad497404e6",
"name": "vAPI",
"description": "vAPI is Vulnerable Adversely Programmed Interface which is Self-Hostable API Interface that mimics OWASP API Top 10 scenarios in the means of Exercises.",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
@@ -1060,7 +1060,186 @@
},
{
"name": "Arena",
- "item": []
+ "item": [
+ {
+ "name": "JustWeakToken",
+ "item": [
+ {
+ "name": "Create User",
+ "event": [
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "var jsonData = JSON.parse(responseBody);\r",
+ "var token = jsonData.token;\r",
+ "console.log(\"JustWeakToken : \"+token);\r",
+ "postman.setEnvironmentVariable(\"justweaktoken\",token);"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\r\n \"username\":\"\",\r\n \"password\":\"\"\r\n}"
+ },
+ "url": {
+ "raw": "{{host}}/vapi/jwt/user",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "vapi",
+ "jwt",
+ "user"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get User",
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "text"
+ },
+ {
+ "key": "Authorization-Token",
+ "value": "{{justweaktoken}}",
+ "type": "text"
+ }
+ ],
+ "url": {
+ "raw": "{{host}}/vapi/jwt/user",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "vapi",
+ "jwt",
+ "user"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "ServerSurfer",
+ "item": [
+ {
+ "name": "Get Data",
+ "protocolProfileBehavior": {
+ "disableBodyPruning": true
+ },
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "",
+ "value": "",
+ "type": "text",
+ "disabled": true
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": ""
+ },
+ "url": {
+ "raw": "{{host}}/vapi/serversurfer?url=https://roottusk.com",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "vapi",
+ "serversurfer"
+ ],
+ "query": [
+ {
+ "key": "url",
+ "value": "https://roottusk.com"
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "StickyNotes",
+ "item": [
+ {
+ "name": "Store a Note",
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json",
+ "type": "default"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "{\n \"note\":\"Hello I am Tushar\"\n}"
+ },
+ "url": {
+ "raw": "{{host}}/vapi/stickynotes",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "vapi",
+ "stickynotes"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Get Notes",
+ "request": {
+ "method": "GET",
+ "header": [],
+ "url": {
+ "raw": "{{host}}/vapi/stickynotes?format=html",
+ "host": [
+ "{{host}}"
+ ],
+ "path": [
+ "vapi",
+ "stickynotes"
+ ],
+ "query": [
+ {
+ "key": "format",
+ "value": "html"
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ }
+ ]
}
],
"event": [
diff --git a/vapi/app/CustomClasses/Variables.php b/vapi/app/CustomClasses/Variables.php
index 03cb141d..17f1d131 100644
--- a/vapi/app/CustomClasses/Variables.php
+++ b/vapi/app/CustomClasses/Variables.php
@@ -33,4 +33,12 @@ public static function getAPI10Flag()
{
return "ZmxhZ3thcGkxMF81ZGI2MTFmN2MxZmZkNzQ3OTcxZn0=";
}
+ public static function getJWTFlag()
+ {
+ return "ZmxhZ3tqd3RfcW5vcGtrdDNvd3N4bmRrMDgzdjF9";
+ }
+ public static function getXSSFlag()
+ {
+ return "ZmxhZ3t4c3NfZTJyZ2h4ZTQxNjRjYzI0NzJ4d2J9";
+ }
}
diff --git a/vapi/app/Http/Controllers/JustWeakTokenController.php b/vapi/app/Http/Controllers/JustWeakTokenController.php
new file mode 100644
index 00000000..1bd192e3
--- /dev/null
+++ b/vapi/app/Http/Controllers/JustWeakTokenController.php
@@ -0,0 +1,89 @@
+hasHeader('Authorization-Token') && $request->header('Authorization-Token')!="" )
+ {
+ $token = $request->header('Authorization-Token');
+ JWT::$leeway = 60;
+ $key="YouNeverGonnaGetThisOne";
+
+ try{
+
+ $decoded = (array) JWT::decode($token, $key, array('HS256','none'));
+
+
+ if(time()>$decoded["exp"])
+ {
+ return response(json_encode(array("success"=>"false","cause"=>"Token Expired")), 403)
+ ->header('Content-Type', 'application/json');
+
+ }
+ else
+ {
+ if($decoded["role"]=="user")
+ {
+ return response(json_encode(array("success"=>"true","text"=>"Just Your Usual Text")), 200)
+ ->header('Content-Type', 'application/json');
+
+ }
+ if($decoded["role"]=="admin")
+ {
+
+ return response(json_encode(array("success"=>"true","text"=>base64_decode(Variables::getJWTFlag()))), 200)
+ ->header('Content-Type', 'application/json');
+ }
+
+ }
+
+ }
+ catch(Exception $e)
+ {
+ return response(json_encode(array("success"=>"false","cause"=>"JWT Exception")), 403)
+ ->header('Content-Type', 'application/json');
+ }
+
+ }
+
+ return response(json_encode(array("success"=>"false","cause"=>"authHeaderNotSet")), 403)
+ ->header('Content-Type', 'application/json');
+ }
+
+ public function store(Request $request)
+ {
+
+
+ $key = "YouNeverGonnaGetThisOne";
+ $payload = array(
+ "iss" => "vapi",
+ "aud" => "vapi",
+ "role"=> "user",
+ "iat" => time(),
+ "exp" => time()+1800
+ );
+ $jwt = JWT::encode($payload, $key);
+
+ if(JustWeakTokens::create(json_decode($request->getContent(), true)))
+ {
+ return response(json_encode(array("success"=>"true","token"=>$jwt)), 200)
+ ->header('Content-Type', 'application/json');
+ }
+ else
+ {
+ return response(json_encode(array("success"=>"false")), 400)
+ ->header('Content-Type', 'application/json');
+ }
+ }
+}
diff --git a/vapi/app/Http/Controllers/StickyNotesController.php b/vapi/app/Http/Controllers/StickyNotesController.php
new file mode 100644
index 00000000..f9646b4f
--- /dev/null
+++ b/vapi/app/Http/Controllers/StickyNotesController.php
@@ -0,0 +1,71 @@
+getContent(), true));
+ }
+
+ public function show(Request $request)
+ {
+ if($request->input('format')!="")
+ {
+ $format = $request->input('format');
+
+ if($format == "json")
+ {
+ return response(json_encode(StickyNotes::all()), 200)
+ ->header('Content-Type', 'application/json');
+ }
+ else
+ {
+ $sticky_notes= json_decode(json_encode(StickyNotes::all()),true);
+
+ $potential_xss_payload_flag = false;
+ // Lazy XML Conversions Needs to be fixed
+ $xml_output = "";
+ foreach($sticky_notes as $note)
+ {
+ $xml_output = $xml_output."".$note['note']." ";
+
+ if (Str::contains($note['note'],"<"))
+ {
+ $potential_xss_payload_flag=true;
+ }
+ }
+
+ if($format == "html" and $potential_xss_payload_flag)
+ {
+ $xml_output = $xml_output."".base64_decode(Variables::getXSSFlag())." ";
+ }
+
+ $xml_output = $xml_output." ";
+
+
+
+ return response($xml_output, 200)
+ ->header('Content-Type', 'text/'.$format);
+ }
+
+ }
+ else
+ {
+ return response(json_encode(array("success"=>"false","cause"=>"format param not set properly")), 403)
+ ->header('Content-Type', 'application/json');
+ }
+ }
+
+}
diff --git a/vapi/app/Http/Controllers/SurferController.php b/vapi/app/Http/Controllers/SurferController.php
new file mode 100644
index 00000000..2d338695
--- /dev/null
+++ b/vapi/app/Http/Controllers/SurferController.php
@@ -0,0 +1,33 @@
+input('url')!="")
+ {
+ $url = $request->input('url');
+ if(!strpos($url,"127.0.0.1") && !strpos($url,"localhost"))
+ {
+ $data=file_get_contents($url);
+ return response(json_encode(array("success"=>"true","data"=>base64_encode($data))), 200)
+ ->header('Content-Type', 'application/json');
+ }
+ else
+ {
+ return response(json_encode(array("success"=>"false","msg"=>"Whoa!!! Not Allowed!!")), 403)
+ ->header('Content-Type', 'application/json');
+ }
+ }
+ else
+ {
+ return response(json_encode(array("success"=>"false","cause"=>"url param not set")), 403)
+ ->header('Content-Type', 'application/json');
+ }
+ }
+}
diff --git a/vapi/app/Models/JustWeakTokens.php b/vapi/app/Models/JustWeakTokens.php
new file mode 100644
index 00000000..1d1a7e44
--- /dev/null
+++ b/vapi/app/Models/JustWeakTokens.php
@@ -0,0 +1,15 @@
+=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=4.8 <=9"
+ },
+ "suggest": {
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "jwt",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/firebase/php-jwt/issues",
+ "source": "https://github.com/firebase/php-jwt/tree/v6.0.0"
+ },
+ "time": "2022-01-24T15:18:34+00:00"
+ },
{
"name": "fruitcake/laravel-cors",
"version": "v2.0.4",
diff --git a/vapi/resources/views/welcome.blade.php b/vapi/resources/views/welcome.blade.php
deleted file mode 100644
index b1905c92..00000000
--- a/vapi/resources/views/welcome.blade.php
+++ /dev/null
@@ -1,132 +0,0 @@
-
-
-
-
-
-
- Laravel
-
-
-
-
-
-
-
-
-
-
-
- @if (Route::has('login'))
-
- @auth
-
Home
- @else
-
Log in
-
- @if (Route::has('register'))
-
Register
- @endif
- @endauth
-
- @endif
-
-
-
-
-
-
-
-
-
-
-
- Laravel has wonderful, thorough documentation covering every aspect of the framework. Whether you are new to the framework or have previous experience with Laravel, we recommend reading all of the documentation from beginning to end.
-
-
-
-
-
-
-
-
-
- Laracasts offers thousands of video tutorials on Laravel, PHP, and JavaScript development. Check them out, see for yourself, and massively level up your development skills in the process.
-
-
-
-
-
-
-
-
-
- Laravel News is a community driven portal and newsletter aggregating all of the latest and most important news in the Laravel ecosystem, including new package releases and tutorials.
-
-
-
-
-
-
-
-
-
- Laravel's robust library of first-party tools and libraries, such as
Forge ,
Vapor ,
Nova , and
Envoyer help you take your projects to the next level. Pair them with powerful open source libraries like
Cashier ,
Dusk ,
Echo ,
Horizon ,
Sanctum ,
Telescope , and more.
-
-
-
-
-
-
-
-
-
-
- Laravel v{{ Illuminate\Foundation\Application::VERSION }} (PHP v{{ PHP_VERSION }})
-
-
-
-
-
-
diff --git a/vapi/resources/views/welcome.html b/vapi/resources/views/welcome.html
new file mode 100644
index 00000000..7ecc5cff
--- /dev/null
+++ b/vapi/resources/views/welcome.html
@@ -0,0 +1,278 @@
+
+
+
+ vAPI
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
vAPI is Vulnerable Adversely Programmed Interface which is Self-Hostable API that mimics OWASP API Top 10 scenarios in the means of Exercises.
+
Requirements
+
+PHP
+MySQL
+PostMan
+MITM Proxy
+
+
Installation (Docker)
+
docker-compose up -d
+
+
Installation (Manual)
+
Copying the Code
+
cd <your-hosting-directory>
+
+
git clone https://github.com/roottusk/vapi.git
+
+
Setting up the Database
+
Import vapi.sql
into MySQL Database
+
Configure the DB Credentials in the vapi/.env
+
Starting MySQL service
+
Run following command (Linux)
+
service mysqld start
+
+
Starting Laravel Server
+
Go to vapi
directory and Run
+
php artisan serve
+
+
Setting Up Postman
+
+Import vAPI.postman_collection.json
in Postman
+Import vAPI_ENV.postman_environment.json
in Postman
+
+
OR
+
Use Public Workspace
+
https://www.postman.com/roottusk/workspace/vapi/
+
Usage
+
Browse http://localhost/vapi/
for Documentation
+
After Sending requests, refer to the Postman Tests or Environment for Generated Tokens
+
Deployment
+
Helm can be used to deploy to a Kubernetes namespace. The chart is in the vapi-chart
folder. The chart requires one secret named vapi
with the following values:
+
DB_PASSWORD: <database password to use>
+DB_USERNAME: <database username to use>
+
+
Sample Helm Install Command: helm upgrade --install vapi ./vapi-chart --values=./vapi-chart/values.yaml
+
*** Important ***
+
The MYSQLROOT PASSWORD on line 232 in the values.yaml
must match that on line 184 in order to work.
+
Presented At
+
OWASP 20th Anniversary
+
Blackhat Europe 2021 Arsenal
+
HITB Cyberweek 2021, Abu Dhabi, UAE
+
@Hack, Riyadh, KSA
+
APISecure.co
+
Upcoming
+
Mentions and References
+
[1] https://apisecurity.io/issue-132-experian-api-leak-breaches-digitalocean-geico-burp-plugins-vapi-lab/
+
[2] https://dsopas.github.io/MindAPI/references/
+
[3] https://dzone.com/articles/api-security-weekly-issue-132
+
[4] https://owasp.org/www-project-vulnerable-web-applications-directory/
+
[5] https://github.com/arainho/awesome-api-security
+
[6] https://portswigger.net/daily-swig/introducing-vapi-an-open-source-lab-environment-to-learn-about-api-security
+
[7] https://apisecurity.io/issue-169-insecure-api-wordpress-plugin-tesla-3rd-party-vulnerability-introducing-vapi/
+
Walkthroughs/Writeups/Videos
+
[1] https://cyc0rpion.medium.com/exploiting-owasp-top-10-api-vulnerabilities-fb9d4b1dd471 (vAPI 1.0 Writeup)
+
[2] https://www.youtube.com/watch?v=0F5opL_c5-4&list=PLT1Gj1RmR7vqHK60qS5bpNUeivz4yhmbS (Turkish Language) (vAPI 1.1 Walkthrough)
+
[3] https://medium.com/@jyotiagarwal3190/roottusk-vapi-writeup-341ec99879c (vAPI 1.1 Writeup)
+
Acknowledgements
+
+The icon and banner uses image from Flaticon
+
+
+
+
+
+
diff --git a/vapi/routes/api.php b/vapi/routes/api.php
index d0520e0a..56658ed5 100644
--- a/vapi/routes/api.php
+++ b/vapi/routes/api.php
@@ -1,6 +1,7 @@
$baseDir . '/app/Console/Kernel.php',
'App\\CustomClasses\\CustomHeaderAuth' => $baseDir . '/app/CustomClasses/CustomHeaderAuth.php',
+ 'App\\CustomClasses\\DatabaseMigration' => $baseDir . '/app/CustomClasses/DatabaseMigration.php',
'App\\CustomClasses\\Variables' => $baseDir . '/app/CustomClasses/Variables.php',
'App\\Exceptions\\Handler' => $baseDir . '/app/Exceptions/Handler.php',
'App\\Http\\Controllers\\API10UsersController' => $baseDir . '/app/Http/Controllers/API10UsersController.php',
@@ -44,6 +45,7 @@
'App\\Models\\API8Users' => $baseDir . '/app/Models/API8Users.php',
'App\\Models\\API9Users' => $baseDir . '/app/Models/API9Users.php',
'App\\Models\\Flags' => $baseDir . '/app/Models/Flags.php',
+ 'App\\Models\\JustWeakToken' => $baseDir . '/app/Models/JustWeakToken.php',
'App\\Models\\User' => $baseDir . '/app/Models/User.php',
'App\\Providers\\AppServiceProvider' => $baseDir . '/app/Providers/AppServiceProvider.php',
'App\\Providers\\AuthServiceProvider' => $baseDir . '/app/Providers/AuthServiceProvider.php',
@@ -947,6 +949,11 @@
'Faker\\Provider\\zh_TW\\Text' => $vendorDir . '/fakerphp/faker/src/Faker/Provider/zh_TW/Text.php',
'Faker\\UniqueGenerator' => $vendorDir . '/fakerphp/faker/src/Faker/UniqueGenerator.php',
'Faker\\ValidGenerator' => $vendorDir . '/fakerphp/faker/src/Faker/ValidGenerator.php',
+ 'Firebase\\JWT\\BeforeValidException' => $vendorDir . '/firebase/php-jwt/src/BeforeValidException.php',
+ 'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php',
+ 'Firebase\\JWT\\JWK' => $vendorDir . '/firebase/php-jwt/src/JWK.php',
+ 'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php',
+ 'Firebase\\JWT\\SignatureInvalidException' => $vendorDir . '/firebase/php-jwt/src/SignatureInvalidException.php',
'Fruitcake\\Cors\\CorsServiceProvider' => $vendorDir . '/fruitcake/laravel-cors/src/CorsServiceProvider.php',
'Fruitcake\\Cors\\HandleCors' => $vendorDir . '/fruitcake/laravel-cors/src/HandleCors.php',
'GrahamCampbell\\ResultType\\Error' => $vendorDir . '/graham-campbell/result-type/src/Error.php',
diff --git a/vapi/vendor/composer/autoload_psr4.php b/vapi/vendor/composer/autoload_psr4.php
index c3c259ea..64aaf8b2 100644
--- a/vapi/vendor/composer/autoload_psr4.php
+++ b/vapi/vendor/composer/autoload_psr4.php
@@ -68,6 +68,7 @@
'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'),
'GrahamCampbell\\ResultType\\' => array($vendorDir . '/graham-campbell/result-type/src'),
'Fruitcake\\Cors\\' => array($vendorDir . '/fruitcake/laravel-cors/src'),
+ 'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'),
'Faker\\' => array($vendorDir . '/fakerphp/faker/src/Faker'),
'Facade\\Ignition\\' => array($vendorDir . '/facade/ignition/src'),
'Facade\\IgnitionContracts\\' => array($vendorDir . '/facade/ignition-contracts/src'),
diff --git a/vapi/vendor/composer/autoload_static.php b/vapi/vendor/composer/autoload_static.php
index e823bd2c..d8432dec 100644
--- a/vapi/vendor/composer/autoload_static.php
+++ b/vapi/vendor/composer/autoload_static.php
@@ -142,6 +142,7 @@ class ComposerStaticInit65d9fc829c98efd576e88f35a789e11a
'F' =>
array (
'Fruitcake\\Cors\\' => 15,
+ 'Firebase\\JWT\\' => 13,
'Faker\\' => 6,
'Facade\\Ignition\\' => 16,
'Facade\\IgnitionContracts\\' => 25,
@@ -431,6 +432,10 @@ class ComposerStaticInit65d9fc829c98efd576e88f35a789e11a
array (
0 => __DIR__ . '/..' . '/fruitcake/laravel-cors/src',
),
+ 'Firebase\\JWT\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/firebase/php-jwt/src',
+ ),
'Faker\\' =>
array (
0 => __DIR__ . '/..' . '/fakerphp/faker/src/Faker',
@@ -518,6 +523,7 @@ class ComposerStaticInit65d9fc829c98efd576e88f35a789e11a
public static $classMap = array (
'App\\Console\\Kernel' => __DIR__ . '/../..' . '/app/Console/Kernel.php',
'App\\CustomClasses\\CustomHeaderAuth' => __DIR__ . '/../..' . '/app/CustomClasses/CustomHeaderAuth.php',
+ 'App\\CustomClasses\\DatabaseMigration' => __DIR__ . '/../..' . '/app/CustomClasses/DatabaseMigration.php',
'App\\CustomClasses\\Variables' => __DIR__ . '/../..' . '/app/CustomClasses/Variables.php',
'App\\Exceptions\\Handler' => __DIR__ . '/../..' . '/app/Exceptions/Handler.php',
'App\\Http\\Controllers\\API10UsersController' => __DIR__ . '/../..' . '/app/Http/Controllers/API10UsersController.php',
@@ -554,6 +560,7 @@ class ComposerStaticInit65d9fc829c98efd576e88f35a789e11a
'App\\Models\\API8Users' => __DIR__ . '/../..' . '/app/Models/API8Users.php',
'App\\Models\\API9Users' => __DIR__ . '/../..' . '/app/Models/API9Users.php',
'App\\Models\\Flags' => __DIR__ . '/../..' . '/app/Models/Flags.php',
+ 'App\\Models\\JustWeakToken' => __DIR__ . '/../..' . '/app/Models/JustWeakToken.php',
'App\\Models\\User' => __DIR__ . '/../..' . '/app/Models/User.php',
'App\\Providers\\AppServiceProvider' => __DIR__ . '/../..' . '/app/Providers/AppServiceProvider.php',
'App\\Providers\\AuthServiceProvider' => __DIR__ . '/../..' . '/app/Providers/AuthServiceProvider.php',
@@ -1457,6 +1464,11 @@ class ComposerStaticInit65d9fc829c98efd576e88f35a789e11a
'Faker\\Provider\\zh_TW\\Text' => __DIR__ . '/..' . '/fakerphp/faker/src/Faker/Provider/zh_TW/Text.php',
'Faker\\UniqueGenerator' => __DIR__ . '/..' . '/fakerphp/faker/src/Faker/UniqueGenerator.php',
'Faker\\ValidGenerator' => __DIR__ . '/..' . '/fakerphp/faker/src/Faker/ValidGenerator.php',
+ 'Firebase\\JWT\\BeforeValidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/BeforeValidException.php',
+ 'Firebase\\JWT\\ExpiredException' => __DIR__ . '/..' . '/firebase/php-jwt/src/ExpiredException.php',
+ 'Firebase\\JWT\\JWK' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWK.php',
+ 'Firebase\\JWT\\JWT' => __DIR__ . '/..' . '/firebase/php-jwt/src/JWT.php',
+ 'Firebase\\JWT\\SignatureInvalidException' => __DIR__ . '/..' . '/firebase/php-jwt/src/SignatureInvalidException.php',
'Fruitcake\\Cors\\CorsServiceProvider' => __DIR__ . '/..' . '/fruitcake/laravel-cors/src/CorsServiceProvider.php',
'Fruitcake\\Cors\\HandleCors' => __DIR__ . '/..' . '/fruitcake/laravel-cors/src/HandleCors.php',
'GrahamCampbell\\ResultType\\Error' => __DIR__ . '/..' . '/graham-campbell/result-type/src/Error.php',
diff --git a/vapi/vendor/composer/installed.json b/vapi/vendor/composer/installed.json
index e0bc46f4..a921a0cd 100644
--- a/vapi/vendor/composer/installed.json
+++ b/vapi/vendor/composer/installed.json
@@ -933,6 +933,66 @@
],
"install-path": "../filp/whoops"
},
+ {
+ "name": "firebase/php-jwt",
+ "version": "v6.0.0",
+ "version_normalized": "6.0.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/firebase/php-jwt.git",
+ "reference": "0541cba75ab108ef901985e68055a92646c73534"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/firebase/php-jwt/zipball/0541cba75ab108ef901985e68055a92646c73534",
+ "reference": "0541cba75ab108ef901985e68055a92646c73534",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=4.8 <=9"
+ },
+ "suggest": {
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+ },
+ "time": "2022-01-24T15:18:34+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "jwt",
+ "php"
+ ],
+ "support": {
+ "issues": "https://github.com/firebase/php-jwt/issues",
+ "source": "https://github.com/firebase/php-jwt/tree/v6.0.0"
+ },
+ "install-path": "../firebase/php-jwt"
+ },
{
"name": "fruitcake/laravel-cors",
"version": "v2.0.4",
diff --git a/vapi/vendor/composer/installed.php b/vapi/vendor/composer/installed.php
index e111b37d..1b58801b 100644
--- a/vapi/vendor/composer/installed.php
+++ b/vapi/vendor/composer/installed.php
@@ -1,11 +1,11 @@
array(
- 'pretty_version' => 'v8.6.1',
- 'version' => '8.6.1.0',
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
- 'reference' => NULL,
+ 'reference' => 'db01baef23b379bb9fc8e9af1ad0913079f7e8c7',
'name' => 'laravel/laravel',
'dev' => true,
),
@@ -139,6 +139,15 @@
'reference' => 'fdf92f03e150ed84d5967a833ae93abffac0315b',
'dev_requirement' => true,
),
+ 'firebase/php-jwt' => array(
+ 'pretty_version' => 'v6.0.0',
+ 'version' => '6.0.0.0',
+ 'type' => 'library',
+ 'install_path' => __DIR__ . '/../firebase/php-jwt',
+ 'aliases' => array(),
+ 'reference' => '0541cba75ab108ef901985e68055a92646c73534',
+ 'dev_requirement' => false,
+ ),
'fruitcake/laravel-cors' => array(
'pretty_version' => 'v2.0.4',
'version' => '2.0.4.0',
@@ -395,12 +404,12 @@
'dev_requirement' => false,
),
'laravel/laravel' => array(
- 'pretty_version' => 'v8.6.1',
- 'version' => '8.6.1.0',
+ 'pretty_version' => 'dev-master',
+ 'version' => 'dev-master',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
- 'reference' => NULL,
+ 'reference' => 'db01baef23b379bb9fc8e9af1ad0913079f7e8c7',
'dev_requirement' => false,
),
'laravel/sail' => array(
@@ -760,8 +769,8 @@
'psr/log-implementation' => array(
'dev_requirement' => false,
'provided' => array(
- 0 => '1.0|2.0',
- 1 => '1.0.0',
+ 0 => '1.0.0',
+ 1 => '1.0|2.0',
),
),
'psr/simple-cache' => array(
diff --git a/vapi/vendor/firebase/php-jwt/LICENSE b/vapi/vendor/firebase/php-jwt/LICENSE
new file mode 100644
index 00000000..11c01466
--- /dev/null
+++ b/vapi/vendor/firebase/php-jwt/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2011, Neuman Vong
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * 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.
+
+ * Neither the name of the copyright holder nor the names of other
+ 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
+OWNER 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.
diff --git a/vapi/vendor/firebase/php-jwt/README.md b/vapi/vendor/firebase/php-jwt/README.md
new file mode 100644
index 00000000..7839af60
--- /dev/null
+++ b/vapi/vendor/firebase/php-jwt/README.md
@@ -0,0 +1,297 @@
+![Build Status](https://github.com/firebase/php-jwt/actions/workflows/tests.yml/badge.svg)
+[![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt)
+[![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt)
+[![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt)
+
+PHP-JWT
+=======
+A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519).
+
+Installation
+------------
+
+Use composer to manage your dependencies and download PHP-JWT:
+
+```bash
+composer require firebase/php-jwt
+```
+
+Optionally, install the `paragonie/sodium_compat` package from composer if your
+php is < 7.2 or does not have libsodium installed:
+
+```bash
+composer require paragonie/sodium_compat
+```
+
+Example
+-------
+```php
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+
+$key = "example_key";
+$payload = array(
+ "iss" => "http://example.org",
+ "aud" => "http://example.com",
+ "iat" => 1356999524,
+ "nbf" => 1357000000
+);
+
+/**
+ * IMPORTANT:
+ * You must specify supported algorithms for your application. See
+ * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40
+ * for a list of spec-compliant algorithms.
+ */
+$jwt = JWT::encode($payload, $key, 'HS256');
+$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
+
+print_r($decoded);
+
+/*
+ NOTE: This will now be an object instead of an associative array. To get
+ an associative array, you will need to cast it as such:
+*/
+
+$decoded_array = (array) $decoded;
+
+/**
+ * You can add a leeway to account for when there is a clock skew times between
+ * the signing and verifying servers. It is recommended that this leeway should
+ * not be bigger than a few minutes.
+ *
+ * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef
+ */
+JWT::$leeway = 60; // $leeway in seconds
+$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
+```
+Example with RS256 (openssl)
+----------------------------
+```php
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+
+$privateKey = << "example.org",
+ "aud" => "example.com",
+ "iat" => 1356999524,
+ "nbf" => 1357000000
+);
+
+$jwt = JWT::encode($payload, $privateKey, 'RS256');
+echo "Encode:\n" . print_r($jwt, true) . "\n";
+
+$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
+
+/*
+ NOTE: This will now be an object instead of an associative array. To get
+ an associative array, you will need to cast it as such:
+*/
+
+$decoded_array = (array) $decoded;
+echo "Decode:\n" . print_r($decoded_array, true) . "\n";
+```
+
+Example with a passphrase
+-------------------------
+
+```php
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+
+// Your passphrase
+$passphrase = '[YOUR_PASSPHRASE]';
+
+// Your private key file with passphrase
+// Can be generated with "ssh-keygen -t rsa -m pem"
+$privateKeyFile = '/path/to/key-with-passphrase.pem';
+
+// Create a private key of type "resource"
+$privateKey = openssl_pkey_get_private(
+ file_get_contents($privateKeyFile),
+ $passphrase
+);
+
+$payload = array(
+ "iss" => "example.org",
+ "aud" => "example.com",
+ "iat" => 1356999524,
+ "nbf" => 1357000000
+);
+
+$jwt = JWT::encode($payload, $privateKey, 'RS256');
+echo "Encode:\n" . print_r($jwt, true) . "\n";
+
+// Get public key from the private key, or pull from from a file.
+$publicKey = openssl_pkey_get_details($privateKey)['key'];
+
+$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
+echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
+```
+
+Example with EdDSA (libsodium and Ed25519 signature)
+----------------------------
+```php
+use Firebase\JWT\JWT;
+use Firebase\JWT\Key;
+
+// Public and private keys are expected to be Base64 encoded. The last
+// non-empty line is used so that keys can be generated with
+// sodium_crypto_sign_keypair(). The secret keys generated by other tools may
+// need to be adjusted to match the input expected by libsodium.
+
+$keyPair = sodium_crypto_sign_keypair();
+
+$privateKey = base64_encode(sodium_crypto_sign_secretkey($keyPair));
+
+$publicKey = base64_encode(sodium_crypto_sign_publickey($keyPair));
+
+$payload = array(
+ "iss" => "example.org",
+ "aud" => "example.com",
+ "iat" => 1356999524,
+ "nbf" => 1357000000
+);
+
+$jwt = JWT::encode($payload, $privateKey, 'EdDSA');
+echo "Encode:\n" . print_r($jwt, true) . "\n";
+
+$decoded = JWT::decode($jwt, new Key($publicKey, 'EdDSA'));
+echo "Decode:\n" . print_r((array) $decoded, true) . "\n";
+````
+
+Using JWKs
+----------
+
+```php
+use Firebase\JWT\JWK;
+use Firebase\JWT\JWT;
+
+// Set of keys. The "keys" key is required. For example, the JSON response to
+// this endpoint: https://www.gstatic.com/iap/verify/public_key-jwk
+$jwks = ['keys' => []];
+
+// JWK::parseKeySet($jwks) returns an associative array of **kid** to Firebase\JWT\Key
+// objects. Pass this as the second parameter to JWT::decode.
+JWT::decode($payload, JWK::parseKeySet($jwks));
+```
+
+Changelog
+---------
+
+#### 6.0.0 / 2022-01-24
+
+ - **Backwards-Compatibility Breaking Changes**: See the [Release Notes](https://github.com/firebase/php-jwt/releases/tag/v5.5.1) for more information.
+ - New Key object to prevent key/algorithm type confusion (#365)
+ - Add JWK support (#273)
+ - Add ES256 support (#256)
+ - Add ES384 support (#324)
+ - Add Ed25519 support (#343)
+
+#### 5.0.0 / 2017-06-26
+- Support RS384 and RS512.
+ See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)!
+- Add an example for RS256 openssl.
+ See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)!
+- Detect invalid Base64 encoding in signature.
+ See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)!
+- Update `JWT::verify` to handle OpenSSL errors.
+ See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)!
+- Add `array` type hinting to `decode` method
+ See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)!
+- Add all JSON error types.
+ See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)!
+- Bugfix 'kid' not in given key list.
+ See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)!
+- Miscellaneous cleanup, documentation and test fixes.
+ See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115),
+ [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and
+ [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman),
+ [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)!
+
+#### 4.0.0 / 2016-07-17
+- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)!
+- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)!
+- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)!
+- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)!
+
+#### 3.0.0 / 2015-07-22
+- Minimum PHP version updated from `5.2.0` to `5.3.0`.
+- Add `\Firebase\JWT` namespace. See
+[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to
+[@Dashron](https://github.com/Dashron)!
+- Require a non-empty key to decode and verify a JWT. See
+[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to
+[@sjones608](https://github.com/sjones608)!
+- Cleaner documentation blocks in the code. See
+[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to
+[@johanderuijter](https://github.com/johanderuijter)!
+
+#### 2.2.0 / 2015-06-22
+- Add support for adding custom, optional JWT headers to `JWT::encode()`. See
+[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to
+[@mcocaro](https://github.com/mcocaro)!
+
+#### 2.1.0 / 2015-05-20
+- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew
+between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)!
+- Add support for passing an object implementing the `ArrayAccess` interface for
+`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)!
+
+#### 2.0.0 / 2015-04-01
+- **Note**: It is strongly recommended that you update to > v2.0.0 to address
+ known security vulnerabilities in prior versions when both symmetric and
+ asymmetric keys are used together.
+- Update signature for `JWT::decode(...)` to require an array of supported
+ algorithms to use when verifying token signatures.
+
+
+Tests
+-----
+Run the tests using phpunit:
+
+```bash
+$ pear install PHPUnit
+$ phpunit --configuration phpunit.xml.dist
+PHPUnit 3.7.10 by Sebastian Bergmann.
+.....
+Time: 0 seconds, Memory: 2.50Mb
+OK (5 tests, 5 assertions)
+```
+
+New Lines in private keys
+-----
+
+If your private key contains `\n` characters, be sure to wrap it in double quotes `""`
+and not single quotes `''` in order to properly interpret the escaped characters.
+
+License
+-------
+[3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause).
diff --git a/vapi/vendor/firebase/php-jwt/composer.json b/vapi/vendor/firebase/php-jwt/composer.json
new file mode 100644
index 00000000..6146e2dc
--- /dev/null
+++ b/vapi/vendor/firebase/php-jwt/composer.json
@@ -0,0 +1,36 @@
+{
+ "name": "firebase/php-jwt",
+ "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.",
+ "homepage": "https://github.com/firebase/php-jwt",
+ "keywords": [
+ "php",
+ "jwt"
+ ],
+ "authors": [
+ {
+ "name": "Neuman Vong",
+ "email": "neuman+pear@twilio.com",
+ "role": "Developer"
+ },
+ {
+ "name": "Anant Narayanan",
+ "email": "anant@php.net",
+ "role": "Developer"
+ }
+ ],
+ "license": "BSD-3-Clause",
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "suggest": {
+ "paragonie/sodium_compat": "Support EdDSA (Ed25519) signatures when libsodium is not present"
+ },
+ "autoload": {
+ "psr-4": {
+ "Firebase\\JWT\\": "src"
+ }
+ },
+ "require-dev": {
+ "phpunit/phpunit": ">=4.8 <=9"
+ }
+}
diff --git a/vapi/vendor/firebase/php-jwt/src/BeforeValidException.php b/vapi/vendor/firebase/php-jwt/src/BeforeValidException.php
new file mode 100644
index 00000000..5085b04c
--- /dev/null
+++ b/vapi/vendor/firebase/php-jwt/src/BeforeValidException.php
@@ -0,0 +1,6 @@
+
+ * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
+ * @link https://github.com/firebase/php-jwt
+ */
+class JWK
+{
+ /**
+ * Parse a set of JWK keys
+ *
+ * @param array $jwks The JSON Web Key Set as an associative array
+ *
+ * @return array An associative array that represents the set of keys
+ *
+ * @throws InvalidArgumentException Provided JWK Set is empty
+ * @throws UnexpectedValueException Provided JWK Set was invalid
+ * @throws DomainException OpenSSL failure
+ *
+ * @uses parseKey
+ */
+ public static function parseKeySet(array $jwks)
+ {
+ $keys = array();
+
+ if (!isset($jwks['keys'])) {
+ throw new UnexpectedValueException('"keys" member must exist in the JWK Set');
+ }
+ if (empty($jwks['keys'])) {
+ throw new InvalidArgumentException('JWK Set did not contain any keys');
+ }
+
+ foreach ($jwks['keys'] as $k => $v) {
+ $kid = isset($v['kid']) ? $v['kid'] : $k;
+ if ($key = self::parseKey($v)) {
+ $keys[$kid] = $key;
+ }
+ }
+
+ if (0 === \count($keys)) {
+ throw new UnexpectedValueException('No supported algorithms found in JWK Set');
+ }
+
+ return $keys;
+ }
+
+ /**
+ * Parse a JWK key
+ *
+ * @param array $jwk An individual JWK
+ *
+ * @return resource|array An associative array that represents the key
+ *
+ * @throws InvalidArgumentException Provided JWK is empty
+ * @throws UnexpectedValueException Provided JWK was invalid
+ * @throws DomainException OpenSSL failure
+ *
+ * @uses createPemFromModulusAndExponent
+ */
+ private static function parseKey(array $jwk)
+ {
+ if (empty($jwk)) {
+ throw new InvalidArgumentException('JWK must not be empty');
+ }
+ if (!isset($jwk['kty'])) {
+ throw new UnexpectedValueException('JWK must contain a "kty" parameter');
+ }
+
+ switch ($jwk['kty']) {
+ case 'RSA':
+ if (\array_key_exists('d', $jwk)) {
+ throw new UnexpectedValueException('RSA private keys are not supported');
+ }
+ if (!isset($jwk['n']) || !isset($jwk['e'])) {
+ throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"');
+ }
+
+ $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']);
+ $publicKey = \openssl_pkey_get_public($pem);
+ if (false === $publicKey) {
+ throw new DomainException(
+ 'OpenSSL error: ' . \openssl_error_string()
+ );
+ }
+ return $publicKey;
+ default:
+ // Currently only RSA is supported
+ break;
+ }
+ }
+
+ /**
+ * Create a public key represented in PEM format from RSA modulus and exponent information
+ *
+ * @param string $n The RSA modulus encoded in Base64
+ * @param string $e The RSA exponent encoded in Base64
+ *
+ * @return string The RSA public key represented in PEM format
+ *
+ * @uses encodeLength
+ */
+ private static function createPemFromModulusAndExponent($n, $e)
+ {
+ $modulus = JWT::urlsafeB64Decode($n);
+ $publicExponent = JWT::urlsafeB64Decode($e);
+
+ $components = array(
+ 'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus),
+ 'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent)
+ );
+
+ $rsaPublicKey = \pack(
+ 'Ca*a*a*',
+ 48,
+ self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])),
+ $components['modulus'],
+ $components['publicExponent']
+ );
+
+ // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
+ $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
+ $rsaPublicKey = \chr(0) . $rsaPublicKey;
+ $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey;
+
+ $rsaPublicKey = \pack(
+ 'Ca*a*',
+ 48,
+ self::encodeLength(\strlen($rsaOID . $rsaPublicKey)),
+ $rsaOID . $rsaPublicKey
+ );
+
+ $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
+ \chunk_split(\base64_encode($rsaPublicKey), 64) .
+ '-----END PUBLIC KEY-----';
+
+ return $rsaPublicKey;
+ }
+
+ /**
+ * DER-encode the length
+ *
+ * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
+ * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
+ *
+ * @param int $length
+ * @return string
+ */
+ private static function encodeLength($length)
+ {
+ if ($length <= 0x7F) {
+ return \chr($length);
+ }
+
+ $temp = \ltrim(\pack('N', $length), \chr(0));
+
+ return \pack('Ca*', 0x80 | \strlen($temp), $temp);
+ }
+}
\ No newline at end of file
diff --git a/vapi/vendor/firebase/php-jwt/src/JWT.php b/vapi/vendor/firebase/php-jwt/src/JWT.php
new file mode 100644
index 00000000..60cb9757
--- /dev/null
+++ b/vapi/vendor/firebase/php-jwt/src/JWT.php
@@ -0,0 +1,510 @@
+
+ * @author Anant Narayanan
+ * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
+ * @link https://github.com/firebase/php-jwt
+ */
+class JWT
+{
+ const ASN1_INTEGER = 0x02;
+ const ASN1_SEQUENCE = 0x10;
+ const ASN1_BIT_STRING = 0x03;
+
+ /**
+ * When checking nbf, iat or expiration times,
+ * we want to provide some extra leeway time to
+ * account for clock skew.
+ */
+ public static $leeway = 0;
+
+ /**
+ * Allow the current timestamp to be specified.
+ * Useful for fixing a value within unit testing.
+ *
+ * Will default to PHP time() value if null.
+ */
+ public static $timestamp = null;
+
+ public static $supported_algs = array(
+ 'ES256' => array('openssl', 'SHA256'),
+ 'HS256' => array('hash_hmac', 'SHA256'),
+ 'HS384' => array('hash_hmac', 'SHA384'),
+ 'HS512' => array('hash_hmac', 'SHA512'),
+ 'RS256' => array('openssl', 'SHA256'),
+ 'RS384' => array('openssl', 'SHA384'),
+ 'RS512' => array('openssl', 'SHA512'),
+ 'none' => array('hash_hmac','SHA256'),
+ );
+
+ /**
+ * Decodes a JWT string into a PHP object.
+ *
+ * @param string $jwt The JWT
+ * @param string|array|resource $key The key, or map of keys.
+ * If the algorithm used is asymmetric, this is the public key
+ * @param array $allowed_algs List of supported verification algorithms
+ * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
+ *
+ * @return object The JWT's payload as a PHP object
+ *
+ * @throws UnexpectedValueException Provided JWT was invalid
+ * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
+ * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
+ * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
+ * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
+ *
+ * @uses jsonDecode
+ * @uses urlsafeB64Decode
+ */
+ public static function decode($jwt, $key, array $allowed_algs = array())
+ {
+ $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
+
+ if (empty($key)) {
+ throw new InvalidArgumentException('Key may not be empty');
+ }
+ $tks = \explode('.', $jwt);
+ if (\count($tks) != 3) {
+ throw new UnexpectedValueException('Wrong number of segments');
+ }
+ list($headb64, $bodyb64, $cryptob64) = $tks;
+ if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
+ throw new UnexpectedValueException('Invalid header encoding');
+ }
+ if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
+ throw new UnexpectedValueException('Invalid claims encoding');
+ }
+ if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
+ throw new UnexpectedValueException('Invalid signature encoding');
+ }
+ if (empty($header->alg)) {
+ throw new UnexpectedValueException('Empty algorithm');
+ }
+ if (empty(static::$supported_algs[$header->alg])) {
+ throw new UnexpectedValueException('Algorithm not supported');
+ }
+ if (!\in_array($header->alg, $allowed_algs)) {
+ throw new UnexpectedValueException('Algorithm not allowed');
+ }
+ if ($header->alg === 'ES256') {
+ // OpenSSL expects an ASN.1 DER sequence for ES256 signatures
+ $sig = self::signatureToDER($sig);
+ }
+
+ if (\is_array($key) || $key instanceof \ArrayAccess) {
+ if (isset($header->kid)) {
+ if (!isset($key[$header->kid])) {
+ throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
+ }
+ $key = $key[$header->kid];
+ } else {
+ throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
+ }
+ }
+
+
+
+ // Check the nbf if it is defined. This is the time that the
+ // token can actually be used. If it's not yet that time, abort.
+ if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
+ throw new BeforeValidException(
+ 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
+ );
+ }
+
+ // Check that this token has been created before 'now'. This prevents
+ // using tokens that have been created for later use (and haven't
+ // correctly used the nbf claim).
+ if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
+ throw new BeforeValidException(
+ 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
+ );
+ }
+
+ // Check if this token has expired.
+ if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
+ throw new ExpiredException('Expired token');
+ }
+
+ return $payload;
+ }
+
+ /**
+ * Converts and signs a PHP object or array into a JWT string.
+ *
+ * @param object|array $payload PHP object or array
+ * @param string $key The secret key.
+ * If the algorithm used is asymmetric, this is the private key
+ * @param string $alg The signing algorithm.
+ * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
+ * @param mixed $keyId
+ * @param array $head An array with header elements to attach
+ *
+ * @return string A signed JWT
+ *
+ * @uses jsonEncode
+ * @uses urlsafeB64Encode
+ */
+ public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
+ {
+ $header = array('typ' => 'JWT', 'alg' => $alg);
+ if ($keyId !== null) {
+ $header['kid'] = $keyId;
+ }
+ if (isset($head) && \is_array($head)) {
+ $header = \array_merge($head, $header);
+ }
+ $segments = array();
+ $segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
+ $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
+ $signing_input = \implode('.', $segments);
+
+ $signature = static::sign($signing_input, $key, $alg);
+ $segments[] = static::urlsafeB64Encode($signature);
+
+ return \implode('.', $segments);
+ }
+
+ /**
+ * Sign a string with a given key and algorithm.
+ *
+ * @param string $msg The message to sign
+ * @param string|resource $key The secret key
+ * @param string $alg The signing algorithm.
+ * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
+ *
+ * @return string An encrypted message
+ *
+ * @throws DomainException Unsupported algorithm was specified
+ */
+ public static function sign($msg, $key, $alg = 'HS256')
+ {
+ if (empty(static::$supported_algs[$alg])) {
+ throw new DomainException('Algorithm not supported');
+ }
+ list($function, $algorithm) = static::$supported_algs[$alg];
+ switch ($function) {
+ case 'hash_hmac':
+ return \hash_hmac($algorithm, $msg, $key, true);
+ case 'openssl':
+ $signature = '';
+ $success = \openssl_sign($msg, $signature, $key, $algorithm);
+ if (!$success) {
+ throw new DomainException("OpenSSL unable to sign data");
+ } else {
+ if ($alg === 'ES256') {
+ $signature = self::signatureFromDER($signature, 256);
+ }
+ return $signature;
+ }
+ }
+ }
+
+ /**
+ * Verify a signature with the message, key and method. Not all methods
+ * are symmetric, so we must have a separate verify and sign method.
+ *
+ * @param string $msg The original message (header and body)
+ * @param string $signature The original signature
+ * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
+ * @param string $alg The algorithm
+ *
+ * @return bool
+ *
+ * @throws DomainException Invalid Algorithm or OpenSSL failure
+ */
+ private static function verify($msg, $signature, $key, $alg)
+ {
+ if (empty(static::$supported_algs[$alg])) {
+ throw new DomainException('Algorithm not supported');
+ }
+
+ list($function, $algorithm) = static::$supported_algs[$alg];
+ switch ($function) {
+ case 'openssl':
+ $success = \openssl_verify($msg, $signature, $key, $algorithm);
+ if ($success === 1) {
+ return true;
+ } elseif ($success === 0) {
+ return false;
+ }
+ // returns 1 on success, 0 on failure, -1 on error.
+ throw new DomainException(
+ 'OpenSSL error: ' . \openssl_error_string()
+ );
+ case 'hash_hmac':
+ default:
+ $hash = \hash_hmac($algorithm, $msg, $key, true);
+ if (\function_exists('hash_equals')) {
+ return \hash_equals($signature, $hash);
+ }
+ $len = \min(static::safeStrlen($signature), static::safeStrlen($hash));
+
+ $status = 0;
+ for ($i = 0; $i < $len; $i++) {
+ $status |= (\ord($signature[$i]) ^ \ord($hash[$i]));
+ }
+ $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
+
+ return ($status === 0);
+ }
+ }
+
+ /**
+ * Decode a JSON string into a PHP object.
+ *
+ * @param string $input JSON string
+ *
+ * @return object Object representation of JSON string
+ *
+ * @throws DomainException Provided string was invalid JSON
+ */
+ public static function jsonDecode($input)
+ {
+ if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
+ /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
+ * to specify that large ints (like Steam Transaction IDs) should be treated as
+ * strings, rather than the PHP default behaviour of converting them to floats.
+ */
+ $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
+ } else {
+ /** Not all servers will support that, however, so for older versions we must
+ * manually detect large ints in the JSON string and quote them (thus converting
+ *them to strings) before decoding, hence the preg_replace() call.
+ */
+ $max_int_length = \strlen((string) PHP_INT_MAX) - 1;
+ $json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
+ $obj = \json_decode($json_without_bigints);
+ }
+
+ if ($errno = \json_last_error()) {
+ static::handleJsonError($errno);
+ } elseif ($obj === null && $input !== 'null') {
+ throw new DomainException('Null result with non-null input');
+ }
+ return $obj;
+ }
+
+ /**
+ * Encode a PHP object into a JSON string.
+ *
+ * @param object|array $input A PHP object or array
+ *
+ * @return string JSON representation of the PHP object or array
+ *
+ * @throws DomainException Provided object could not be encoded to valid JSON
+ */
+ public static function jsonEncode($input)
+ {
+ $json = \json_encode($input);
+ if ($errno = \json_last_error()) {
+ static::handleJsonError($errno);
+ } elseif ($json === 'null' && $input !== null) {
+ throw new DomainException('Null result with non-null input');
+ }
+ return $json;
+ }
+
+ /**
+ * Decode a string with URL-safe Base64.
+ *
+ * @param string $input A Base64 encoded string
+ *
+ * @return string A decoded string
+ */
+ public static function urlsafeB64Decode($input)
+ {
+ $remainder = \strlen($input) % 4;
+ if ($remainder) {
+ $padlen = 4 - $remainder;
+ $input .= \str_repeat('=', $padlen);
+ }
+ return \base64_decode(\strtr($input, '-_', '+/'));
+ }
+
+ /**
+ * Encode a string with URL-safe Base64.
+ *
+ * @param string $input The string you want encoded
+ *
+ * @return string The base64 encode of what you passed in
+ */
+ public static function urlsafeB64Encode($input)
+ {
+ return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
+ }
+
+ /**
+ * Helper method to create a JSON error.
+ *
+ * @param int $errno An error number from json_last_error()
+ *
+ * @return void
+ */
+ private static function handleJsonError($errno)
+ {
+ $messages = array(
+ JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
+ JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
+ JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
+ JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
+ JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
+ );
+ throw new DomainException(
+ isset($messages[$errno])
+ ? $messages[$errno]
+ : 'Unknown JSON error: ' . $errno
+ );
+ }
+
+ /**
+ * Get the number of bytes in cryptographic strings.
+ *
+ * @param string $str
+ *
+ * @return int
+ */
+ private static function safeStrlen($str)
+ {
+ if (\function_exists('mb_strlen')) {
+ return \mb_strlen($str, '8bit');
+ }
+ return \strlen($str);
+ }
+
+ /**
+ * Convert an ECDSA signature to an ASN.1 DER sequence
+ *
+ * @param string $sig The ECDSA signature to convert
+ * @return string The encoded DER object
+ */
+ private static function signatureToDER($sig)
+ {
+ // Separate the signature into r-value and s-value
+ list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
+
+ // Trim leading zeros
+ $r = \ltrim($r, "\x00");
+ $s = \ltrim($s, "\x00");
+
+ // Convert r-value and s-value from unsigned big-endian integers to
+ // signed two's complement
+ if (\ord($r[0]) > 0x7f) {
+ $r = "\x00" . $r;
+ }
+ if (\ord($s[0]) > 0x7f) {
+ $s = "\x00" . $s;
+ }
+
+ return self::encodeDER(
+ self::ASN1_SEQUENCE,
+ self::encodeDER(self::ASN1_INTEGER, $r) .
+ self::encodeDER(self::ASN1_INTEGER, $s)
+ );
+ }
+
+ /**
+ * Encodes a value into a DER object.
+ *
+ * @param int $type DER tag
+ * @param string $value the value to encode
+ * @return string the encoded object
+ */
+ private static function encodeDER($type, $value)
+ {
+ $tag_header = 0;
+ if ($type === self::ASN1_SEQUENCE) {
+ $tag_header |= 0x20;
+ }
+
+ // Type
+ $der = \chr($tag_header | $type);
+
+ // Length
+ $der .= \chr(\strlen($value));
+
+ return $der . $value;
+ }
+
+ /**
+ * Encodes signature from a DER object.
+ *
+ * @param string $der binary signature in DER format
+ * @param int $keySize the number of bits in the key
+ * @return string the signature
+ */
+ private static function signatureFromDER($der, $keySize)
+ {
+ // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
+ list($offset, $_) = self::readDER($der);
+ list($offset, $r) = self::readDER($der, $offset);
+ list($offset, $s) = self::readDER($der, $offset);
+
+ // Convert r-value and s-value from signed two's compliment to unsigned
+ // big-endian integers
+ $r = \ltrim($r, "\x00");
+ $s = \ltrim($s, "\x00");
+
+ // Pad out r and s so that they are $keySize bits long
+ $r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
+ $s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
+
+ return $r . $s;
+ }
+
+ /**
+ * Reads binary DER-encoded data and decodes into a single object
+ *
+ * @param string $der the binary data in DER format
+ * @param int $offset the offset of the data stream containing the object
+ * to decode
+ * @return array [$offset, $data] the new offset and the decoded object
+ */
+ private static function readDER($der, $offset = 0)
+ {
+ $pos = $offset;
+ $size = \strlen($der);
+ $constructed = (\ord($der[$pos]) >> 5) & 0x01;
+ $type = \ord($der[$pos++]) & 0x1f;
+
+ // Length
+ $len = \ord($der[$pos++]);
+ if ($len & 0x80) {
+ $n = $len & 0x1f;
+ $len = 0;
+ while ($n-- && $pos < $size) {
+ $len = ($len << 8) | \ord($der[$pos++]);
+ }
+ }
+
+ // Value
+ if ($type == self::ASN1_BIT_STRING) {
+ $pos++; // Skip the first contents octet (padding indicator)
+ $data = \substr($der, $pos, $len - 1);
+ $pos += $len - 1;
+ } elseif (!$constructed) {
+ $data = \substr($der, $pos, $len);
+ $pos += $len;
+ } else {
+ $data = null;
+ }
+
+ return array($pos, $data);
+ }
+}
\ No newline at end of file
diff --git a/vapi/vendor/firebase/php-jwt/src/SignatureInvalidException.php b/vapi/vendor/firebase/php-jwt/src/SignatureInvalidException.php
new file mode 100644
index 00000000..fbb7018d
--- /dev/null
+++ b/vapi/vendor/firebase/php-jwt/src/SignatureInvalidException.php
@@ -0,0 +1,6 @@
+
+
+
+
+
+
+
+
+
+## Introduction
+
+Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.
+
+## Official Documentation
+
+Documentation for Sanctum can be found on the [Laravel website](https://laravel.com/docs/sanctum).
+
+## Contributing
+
+Thank you for considering contributing to Sanctum! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
+
+## Code of Conduct
+
+In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
+
+## Security Vulnerabilities
+
+Please review [our security policy](https://github.com/laravel/sanctum/security/policy) on how to report security vulnerabilities.
+
+## License
+
+Laravel Sanctum is open-sourced software licensed under the [MIT license](LICENSE.md).
diff --git a/vapi/vendor/laravel/sanctum_compare/composer.json b/vapi/vendor/laravel/sanctum_compare/composer.json
new file mode 100644
index 00000000..f175e328
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/composer.json
@@ -0,0 +1,53 @@
+{
+ "name": "laravel/sanctum",
+ "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
+ "keywords": ["laravel", "sanctum", "auth"],
+ "license": "MIT",
+ "support": {
+ "issues": "https://github.com/laravel/sanctum/issues",
+ "source": "https://github.com/laravel/sanctum"
+ },
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "require": {
+ "php": "^7.2|^8.0",
+ "ext-json": "*",
+ "illuminate/contracts": "^6.9|^7.0|^8.0",
+ "illuminate/database": "^6.9|^7.0|^8.0",
+ "illuminate/support": "^6.9|^7.0|^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.0",
+ "orchestra/testbench": "^4.0|^5.0|^6.0",
+ "phpunit/phpunit": "^8.0|^9.3"
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Sanctum\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Laravel\\Sanctum\\Tests\\": "tests/"
+ }
+ },
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ },
+ "laravel": {
+ "providers": [
+ "Laravel\\Sanctum\\SanctumServiceProvider"
+ ]
+ }
+ },
+ "config": {
+ "sort-packages": true
+ },
+ "minimum-stability": "dev",
+ "prefer-stable": true
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/config/sanctum.php b/vapi/vendor/laravel/sanctum_compare/config/sanctum.php
new file mode 100644
index 00000000..442726a7
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/config/sanctum.php
@@ -0,0 +1,51 @@
+ explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
+ '%s%s',
+ 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
+ env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : ''
+ ))),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Expiration Minutes
+ |--------------------------------------------------------------------------
+ |
+ | This value controls the number of minutes until an issued token will be
+ | considered expired. If this value is null, personal access tokens do
+ | not expire. This won't tweak the lifetime of first-party sessions.
+ |
+ */
+
+ 'expiration' => null,
+
+ /*
+ |--------------------------------------------------------------------------
+ | Sanctum Middleware
+ |--------------------------------------------------------------------------
+ |
+ | When authenticating your first-party SPA with Sanctum you may need to
+ | customize some of the middleware Sanctum uses while processing the
+ | request. You may change the middleware listed below as required.
+ |
+ */
+
+ 'middleware' => [
+ 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
+ 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
+ ],
+
+];
diff --git a/vapi/vendor/laravel/sanctum_compare/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/vapi/vendor/laravel/sanctum_compare/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php
new file mode 100644
index 00000000..3ce00023
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php
@@ -0,0 +1,36 @@
+bigIncrements('id');
+ $table->morphs('tokenable');
+ $table->string('name');
+ $table->string('token', 64)->unique();
+ $table->text('abilities')->nullable();
+ $table->timestamp('last_used_at')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('personal_access_tokens');
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/Contracts/HasAbilities.php b/vapi/vendor/laravel/sanctum_compare/src/Contracts/HasAbilities.php
new file mode 100644
index 00000000..6012e2e9
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/Contracts/HasAbilities.php
@@ -0,0 +1,22 @@
+auth = $auth;
+ $this->expiration = $expiration;
+ $this->provider = $provider;
+ }
+
+ /**
+ * Retrieve the authenticated user for the incoming request.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return mixed
+ */
+ public function __invoke(Request $request)
+ {
+ foreach (Arr::wrap(config('sanctum.guard', 'web')) as $guard) {
+ if ($user = $this->auth->guard($guard)->user()) {
+ return $this->supportsTokens($user)
+ ? $user->withAccessToken(new TransientToken)
+ : $user;
+ }
+ }
+
+ if ($token = $request->bearerToken()) {
+ $model = Sanctum::$personalAccessTokenModel;
+
+ $accessToken = $model::findToken($token);
+
+ if (! $this->isValidAccessToken($accessToken) ||
+ ! $this->supportsTokens($accessToken->tokenable)) {
+ return;
+ }
+
+ if (method_exists($accessToken->getConnection(), 'hasModifiedRecords') &&
+ method_exists($accessToken->getConnection(), 'setRecordModificationState')) {
+ tap($accessToken->getConnection()->hasModifiedRecords(), function ($hasModifiedRecords) use ($accessToken) {
+ $accessToken->forceFill(['last_used_at' => now()])->save();
+
+ $accessToken->getConnection()->setRecordModificationState($hasModifiedRecords);
+ });
+ } else {
+ $accessToken->forceFill(['last_used_at' => now()])->save();
+ }
+
+ return $accessToken->tokenable->withAccessToken(
+ $accessToken
+ );
+ }
+ }
+
+ /**
+ * Determine if the tokenable model supports API tokens.
+ *
+ * @param mixed $tokenable
+ * @return bool
+ */
+ protected function supportsTokens($tokenable = null)
+ {
+ return $tokenable && in_array(HasApiTokens::class, class_uses_recursive(
+ get_class($tokenable)
+ ));
+ }
+
+ /**
+ * Determine if the provided access token is valid.
+ *
+ * @param mixed $accessToken
+ * @return bool
+ */
+ protected function isValidAccessToken($accessToken): bool
+ {
+ if (! $accessToken) {
+ return false;
+ }
+
+ $isValid =
+ (! $this->expiration || $accessToken->created_at->gt(now()->subMinutes($this->expiration)))
+ && $this->hasValidProvider($accessToken->tokenable);
+
+ if (is_callable(Sanctum::$accessTokenAuthenticationCallback)) {
+ $isValid = (bool) (Sanctum::$accessTokenAuthenticationCallback)($accessToken, $isValid);
+ }
+
+ return $isValid;
+ }
+
+ /**
+ * Determine if the tokenable model matches the provider's model type.
+ *
+ * @param \Illuminate\Database\Eloquent\Model $tokenable
+ * @return bool
+ */
+ protected function hasValidProvider($tokenable)
+ {
+ if (is_null($this->provider)) {
+ return true;
+ }
+
+ $model = config("auth.providers.{$this->provider}.model");
+
+ return $tokenable instanceof $model;
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/HasApiTokens.php b/vapi/vendor/laravel/sanctum_compare/src/HasApiTokens.php
new file mode 100644
index 00000000..c3d5c805
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/HasApiTokens.php
@@ -0,0 +1,77 @@
+morphMany(Sanctum::$personalAccessTokenModel, 'tokenable');
+ }
+
+ /**
+ * Determine if the current API token has a given scope.
+ *
+ * @param string $ability
+ * @return bool
+ */
+ public function tokenCan(string $ability)
+ {
+ return $this->accessToken && $this->accessToken->can($ability);
+ }
+
+ /**
+ * Create a new personal access token for the user.
+ *
+ * @param string $name
+ * @param array $abilities
+ * @return \Laravel\Sanctum\NewAccessToken
+ */
+ public function createToken(string $name, array $abilities = ['*'])
+ {
+ $token = $this->tokens()->create([
+ 'name' => $name,
+ 'token' => hash('sha256', $plainTextToken = Str::random(40)),
+ 'abilities' => $abilities,
+ ]);
+
+ return new NewAccessToken($token, $token->getKey().'|'.$plainTextToken);
+ }
+
+ /**
+ * Get the access token currently associated with the user.
+ *
+ * @return \Laravel\Sanctum\Contracts\HasAbilities
+ */
+ public function currentAccessToken()
+ {
+ return $this->accessToken;
+ }
+
+ /**
+ * Set the current access token for the user.
+ *
+ * @param \Laravel\Sanctum\Contracts\HasAbilities $accessToken
+ * @return $this
+ */
+ public function withAccessToken($accessToken)
+ {
+ $this->accessToken = $accessToken;
+
+ return $this;
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/Http/Controllers/CsrfCookieController.php b/vapi/vendor/laravel/sanctum_compare/src/Http/Controllers/CsrfCookieController.php
new file mode 100644
index 00000000..2cb9dfe8
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/Http/Controllers/CsrfCookieController.php
@@ -0,0 +1,25 @@
+expectsJson()) {
+ return new JsonResponse(null, 204);
+ }
+
+ return new Response('', 204);
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php b/vapi/vendor/laravel/sanctum_compare/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php
new file mode 100644
index 00000000..f1e34237
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/Http/Middleware/EnsureFrontendRequestsAreStateful.php
@@ -0,0 +1,74 @@
+configureSecureCookieSessions();
+
+ return (new Pipeline(app()))->send($request)->through(static::fromFrontend($request) ? [
+ function ($request, $next) {
+ $request->attributes->set('sanctum', true);
+
+ return $next($request);
+ },
+ config('sanctum.middleware.encrypt_cookies', \Illuminate\Cookie\Middleware\EncryptCookies::class),
+ \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
+ \Illuminate\Session\Middleware\StartSession::class,
+ config('sanctum.middleware.verify_csrf_token', \Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class),
+ ] : [])->then(function ($request) use ($next) {
+ return $next($request);
+ });
+ }
+
+ /**
+ * Configure secure cookie sessions.
+ *
+ * @return void
+ */
+ protected function configureSecureCookieSessions()
+ {
+ config([
+ 'session.http_only' => true,
+ 'session.same_site' => 'lax',
+ ]);
+ }
+
+ /**
+ * Determine if the given request is from the first-party application frontend.
+ *
+ * @param \Illuminate\Http\Request $request
+ * @return bool
+ */
+ public static function fromFrontend($request)
+ {
+ $domain = $request->headers->get('referer') ?: $request->headers->get('origin');
+
+ if (is_null($domain)) {
+ return false;
+ }
+
+ $domain = Str::replaceFirst('https://', '', $domain);
+ $domain = Str::replaceFirst('http://', '', $domain);
+ $domain = Str::endsWith($domain, '/') ? $domain : "{$domain}/";
+
+ $stateful = array_filter(config('sanctum.stateful', []));
+
+ return Str::is(Collection::make($stateful)->map(function ($uri) {
+ return trim($uri).'/*';
+ })->all(), $domain);
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/NewAccessToken.php b/vapi/vendor/laravel/sanctum_compare/src/NewAccessToken.php
new file mode 100644
index 00000000..489fae6d
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/NewAccessToken.php
@@ -0,0 +1,60 @@
+accessToken = $accessToken;
+ $this->plainTextToken = $plainTextToken;
+ }
+
+ /**
+ * Get the instance as an array.
+ *
+ * @return array
+ */
+ public function toArray()
+ {
+ return [
+ 'accessToken' => $this->accessToken,
+ 'plainTextToken' => $this->plainTextToken,
+ ];
+ }
+
+ /**
+ * Convert the object to its JSON representation.
+ *
+ * @param int $options
+ * @return string
+ */
+ public function toJson($options = 0)
+ {
+ return json_encode($this->toArray(), $options);
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/PersonalAccessToken.php b/vapi/vendor/laravel/sanctum_compare/src/PersonalAccessToken.php
new file mode 100644
index 00000000..0d5df213
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/PersonalAccessToken.php
@@ -0,0 +1,91 @@
+ 'json',
+ 'last_used_at' => 'datetime',
+ ];
+
+ /**
+ * The attributes that are mass assignable.
+ *
+ * @var array
+ */
+ protected $fillable = [
+ 'name',
+ 'token',
+ 'abilities',
+ ];
+
+ /**
+ * The attributes that should be hidden for serialization.
+ *
+ * @var array
+ */
+ protected $hidden = [
+ 'token',
+ ];
+
+ /**
+ * Get the tokenable model that the access token belongs to.
+ *
+ * @return \Illuminate\Database\Eloquent\Relations\MorphTo
+ */
+ public function tokenable()
+ {
+ return $this->morphTo('tokenable');
+ }
+
+ /**
+ * Find the token instance matching the given token.
+ *
+ * @param string $token
+ * @return static|null
+ */
+ public static function findToken($token)
+ {
+ if (strpos($token, '|') === false) {
+ return static::where('token', hash('sha256', $token))->first();
+ }
+
+ [$id, $token] = explode('|', $token, 2);
+
+ if ($instance = static::find($id)) {
+ return hash_equals($instance->token, hash('sha256', $token)) ? $instance : null;
+ }
+ }
+
+ /**
+ * Determine if the token has a given ability.
+ *
+ * @param string $ability
+ * @return bool
+ */
+ public function can($ability)
+ {
+ return in_array('*', $this->abilities) ||
+ array_key_exists($ability, array_flip($this->abilities));
+ }
+
+ /**
+ * Determine if the token is missing a given ability.
+ *
+ * @param string $ability
+ * @return bool
+ */
+ public function cant($ability)
+ {
+ return ! $this->can($ability);
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/Sanctum.php b/vapi/vendor/laravel/sanctum_compare/src/Sanctum.php
new file mode 100644
index 00000000..826293c1
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/Sanctum.php
@@ -0,0 +1,116 @@
+shouldIgnoreMissing(false);
+
+ if (in_array('*', $abilities)) {
+ $token->shouldReceive('can')->withAnyArgs()->andReturn(true);
+ } else {
+ foreach ($abilities as $ability) {
+ $token->shouldReceive('can')->with($ability)->andReturn(true);
+ }
+ }
+
+ $user->withAccessToken($token);
+
+ if (isset($user->wasRecentlyCreated) && $user->wasRecentlyCreated) {
+ $user->wasRecentlyCreated = false;
+ }
+
+ app('auth')->guard($guard)->setUser($user);
+
+ app('auth')->shouldUse($guard);
+
+ return $user;
+ }
+
+ /**
+ * Set the personal access token model name.
+ *
+ * @param string $model
+ * @return void
+ */
+ public static function usePersonalAccessTokenModel($model)
+ {
+ static::$personalAccessTokenModel = $model;
+ }
+
+ /**
+ * Specify a callback that should be used to authenticate access tokens.
+ *
+ * @param callable $callback
+ * @return void
+ */
+ public static function authenticateAccessTokensUsing(callable $callback)
+ {
+ static::$accessTokenAuthenticationCallback = $callback;
+ }
+
+ /**
+ * Determine if Sanctum's migrations should be run.
+ *
+ * @return bool
+ */
+ public static function shouldRunMigrations()
+ {
+ return static::$runsMigrations;
+ }
+
+ /**
+ * Configure Sanctum to not register its migrations.
+ *
+ * @return static
+ */
+ public static function ignoreMigrations()
+ {
+ static::$runsMigrations = false;
+
+ return new static;
+ }
+
+ /**
+ * Get the token model class name.
+ *
+ * @return string
+ */
+ public static function personalAccessTokenModel()
+ {
+ return static::$personalAccessTokenModel;
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/SanctumServiceProvider.php b/vapi/vendor/laravel/sanctum_compare/src/SanctumServiceProvider.php
new file mode 100644
index 00000000..7f8fb949
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/SanctumServiceProvider.php
@@ -0,0 +1,132 @@
+ array_merge([
+ 'driver' => 'sanctum',
+ 'provider' => null,
+ ], config('auth.guards.sanctum', [])),
+ ]);
+
+ if (! app()->configurationIsCached()) {
+ $this->mergeConfigFrom(__DIR__.'/../config/sanctum.php', 'sanctum');
+ }
+ }
+
+ /**
+ * Bootstrap any application services.
+ *
+ * @return void
+ */
+ public function boot()
+ {
+ if (app()->runningInConsole()) {
+ $this->registerMigrations();
+
+ $this->publishes([
+ __DIR__.'/../database/migrations' => database_path('migrations'),
+ ], 'sanctum-migrations');
+
+ $this->publishes([
+ __DIR__.'/../config/sanctum.php' => config_path('sanctum.php'),
+ ], 'sanctum-config');
+ }
+
+ $this->defineRoutes();
+ $this->configureGuard();
+ $this->configureMiddleware();
+ }
+
+ /**
+ * Register Sanctum's migration files.
+ *
+ * @return void
+ */
+ protected function registerMigrations()
+ {
+ if (Sanctum::shouldRunMigrations()) {
+ return $this->loadMigrationsFrom(__DIR__.'/../database/migrations');
+ }
+ }
+
+ /**
+ * Define the Sanctum routes.
+ *
+ * @return void
+ */
+ protected function defineRoutes()
+ {
+ if (app()->routesAreCached() || config('sanctum.routes') === false) {
+ return;
+ }
+
+ Route::group(['prefix' => config('sanctum.prefix', 'sanctum')], function () {
+ Route::get(
+ '/csrf-cookie',
+ CsrfCookieController::class.'@show'
+ )->middleware('web');
+ });
+ }
+
+ /**
+ * Configure the Sanctum authentication guard.
+ *
+ * @return void
+ */
+ protected function configureGuard()
+ {
+ Auth::resolved(function ($auth) {
+ $auth->extend('sanctum', function ($app, $name, array $config) use ($auth) {
+ return tap($this->createGuard($auth, $config), function ($guard) {
+ app()->refresh('request', $guard, 'setRequest');
+ });
+ });
+ });
+ }
+
+ /**
+ * Register the guard.
+ *
+ * @param \Illuminate\Contracts\Auth\Factory $auth
+ * @param array $config
+ * @return RequestGuard
+ */
+ protected function createGuard($auth, $config)
+ {
+ return new RequestGuard(
+ new Guard($auth, config('sanctum.expiration'), $config['provider']),
+ request(),
+ $auth->createUserProvider($config['provider'] ?? null)
+ );
+ }
+
+ /**
+ * Configure the Sanctum middleware and priority.
+ *
+ * @return void
+ */
+ protected function configureMiddleware()
+ {
+ $kernel = app()->make(Kernel::class);
+
+ $kernel->prependToMiddlewarePriority(EnsureFrontendRequestsAreStateful::class);
+ }
+}
diff --git a/vapi/vendor/laravel/sanctum_compare/src/TransientToken.php b/vapi/vendor/laravel/sanctum_compare/src/TransientToken.php
new file mode 100644
index 00000000..6987b5d5
--- /dev/null
+++ b/vapi/vendor/laravel/sanctum_compare/src/TransientToken.php
@@ -0,0 +1,30 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+ private $classMapAuthoritative = false;
+ private $missingClasses = array();
+ private $apcuPrefix;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
+ *
+ * @param string|null $apcuPrefix
+ */
+ public function setApcuPrefix($apcuPrefix)
+ {
+ $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
+ }
+
+ /**
+ * The APCu prefix in use, or null if APCu caching is not enabled.
+ *
+ * @return string|null
+ */
+ public function getApcuPrefix()
+ {
+ return $this->apcuPrefix;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
+ return false;
+ }
+ if (null !== $this->apcuPrefix) {
+ $file = apcu_fetch($this->apcuPrefix.$class, $hit);
+ if ($hit) {
+ return $file;
+ }
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if (false === $file && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if (null !== $this->apcuPrefix) {
+ apcu_add($this->apcuPrefix.$class, $file);
+ }
+
+ if (false === $file) {
+ // Remember that this class does not exist.
+ $this->missingClasses[$class] = true;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ $subPath = $class;
+ while (false !== $lastPos = strrpos($subPath, '\\')) {
+ $subPath = substr($subPath, 0, $lastPos);
+ $search = $subPath . '\\';
+ if (isset($this->prefixDirsPsr4[$search])) {
+ $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
+ foreach ($this->prefixDirsPsr4[$search] as $dir) {
+ if (file_exists($file = $dir . $pathEnd)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+
+ return false;
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE
new file mode 100644
index 00000000..62ecfd8d
--- /dev/null
+++ b/vendor/composer/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php
new file mode 100644
index 00000000..7a91153b
--- /dev/null
+++ b/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/spatie/array-to-xml/src'),
+);
diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php
new file mode 100644
index 00000000..c2286715
--- /dev/null
+++ b/vendor/composer/autoload_real.php
@@ -0,0 +1,55 @@
+= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
+ if ($useStaticLoader) {
+ require_once __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInit79dd318e929d105a149570c2a277ec88::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php
new file mode 100644
index 00000000..27dd96e8
--- /dev/null
+++ b/vendor/composer/autoload_static.php
@@ -0,0 +1,31 @@
+
+ array (
+ 'Spatie\\ArrayToXml\\' => 18,
+ ),
+ );
+
+ public static $prefixDirsPsr4 = array (
+ 'Spatie\\ArrayToXml\\' =>
+ array (
+ 0 => __DIR__ . '/..' . '/spatie/array-to-xml/src',
+ ),
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixLengthsPsr4 = ComposerStaticInit79dd318e929d105a149570c2a277ec88::$prefixLengthsPsr4;
+ $loader->prefixDirsPsr4 = ComposerStaticInit79dd318e929d105a149570c2a277ec88::$prefixDirsPsr4;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json
new file mode 100644
index 00000000..7328915a
--- /dev/null
+++ b/vendor/composer/installed.json
@@ -0,0 +1,64 @@
+[
+ {
+ "name": "spatie/array-to-xml",
+ "version": "2.16.0",
+ "version_normalized": "2.16.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spatie/array-to-xml.git",
+ "reference": "db39308c5236b69b89cadc3f44f191704814eae2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/db39308c5236b69b89cadc3f44f191704814eae2",
+ "reference": "db39308c5236b69b89cadc3f44f191704814eae2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "php": "^7.4|^8.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.2",
+ "phpunit/phpunit": "^9.0",
+ "spatie/phpunit-snapshot-assertions": "^4.2"
+ },
+ "time": "2020-11-18T22:03:17+00:00",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "Spatie\\ArrayToXml\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://freek.dev",
+ "role": "Developer"
+ }
+ ],
+ "description": "Convert an array to xml",
+ "homepage": "https://github.com/spatie/array-to-xml",
+ "keywords": [
+ "array",
+ "convert",
+ "xml"
+ ],
+ "funding": [
+ {
+ "url": "https://spatie.be/open-source/support-us",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/spatie",
+ "type": "github"
+ }
+ ]
+ }
+]
diff --git a/vendor/spatie/array-to-xml/.github/CONTRIBUTING.md b/vendor/spatie/array-to-xml/.github/CONTRIBUTING.md
new file mode 100644
index 00000000..b4ae1c4a
--- /dev/null
+++ b/vendor/spatie/array-to-xml/.github/CONTRIBUTING.md
@@ -0,0 +1,55 @@
+# Contributing
+
+Contributions are **welcome** and will be fully **credited**.
+
+Please read and understand the contribution guide before creating an issue or pull request.
+
+## Etiquette
+
+This project is open source, and as such, the maintainers give their free time to build and maintain the source code
+held within. They make the code freely available in the hope that it will be of use to other developers. It would be
+extremely unfair for them to suffer abuse or anger for their hard work.
+
+Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the
+world that developers are civilized and selfless people.
+
+It's the duty of the maintainer to ensure that all submissions to the project are of sufficient
+quality to benefit the project. Many developers have different skillsets, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used.
+
+## Viability
+
+When requesting or submitting new features, first consider whether it might be useful to others. Open
+source projects are used by many developers, who may have entirely different needs to your own. Think about
+whether or not your feature is likely to be used by other users of the project.
+
+## Procedure
+
+Before filing an issue:
+
+- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident.
+- Check to make sure your feature suggestion isn't already present within the project.
+- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
+- Check the pull requests tab to ensure that the feature isn't already in progress.
+
+Before submitting a pull request:
+
+- Check the codebase to ensure that your feature doesn't already exist.
+- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
+
+## Requirements
+
+If the project maintainer has any additional requirements, you will find them listed here.
+
+- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer).
+
+- **Add tests!** - Your patch won't be accepted if it doesn't have tests.
+
+- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date.
+
+- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option.
+
+- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests.
+
+- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting.
+
+**Happy coding**!
diff --git a/vendor/spatie/array-to-xml/.github/FUNDING.yml b/vendor/spatie/array-to-xml/.github/FUNDING.yml
new file mode 100644
index 00000000..fe5143b5
--- /dev/null
+++ b/vendor/spatie/array-to-xml/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+github: spatie
+custom: https://spatie.be/open-source/support-us
diff --git a/vendor/spatie/array-to-xml/.github/SECURITY.md b/vendor/spatie/array-to-xml/.github/SECURITY.md
new file mode 100644
index 00000000..ca913434
--- /dev/null
+++ b/vendor/spatie/array-to-xml/.github/SECURITY.md
@@ -0,0 +1,3 @@
+# Security Policy
+
+If you discover any security related issues, please email freek@spatie.be instead of using the issue tracker.
diff --git a/vendor/spatie/array-to-xml/.github/workflows/php-cs-fixer.yml b/vendor/spatie/array-to-xml/.github/workflows/php-cs-fixer.yml
new file mode 100644
index 00000000..4cf285f0
--- /dev/null
+++ b/vendor/spatie/array-to-xml/.github/workflows/php-cs-fixer.yml
@@ -0,0 +1,23 @@
+name: Check & fix styling
+
+on: [push]
+
+jobs:
+ php-cs-fixer:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+ with:
+ ref: ${{ github.head_ref }}
+
+ - name: Run PHP CS Fixer
+ uses: docker://oskarstark/php-cs-fixer-ga
+ with:
+ args: --config=.php_cs.dist --allow-risky=yes
+
+ - name: Commit changes
+ uses: stefanzweifel/git-auto-commit-action@v4
+ with:
+ commit_message: Fix styling
diff --git a/vendor/spatie/array-to-xml/.github/workflows/run-tests.yml b/vendor/spatie/array-to-xml/.github/workflows/run-tests.yml
new file mode 100644
index 00000000..77743d66
--- /dev/null
+++ b/vendor/spatie/array-to-xml/.github/workflows/run-tests.yml
@@ -0,0 +1,37 @@
+name: Tests
+
+on: [push, pull_request]
+
+jobs:
+ test:
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: true
+ matrix:
+ os: [ubuntu-latest]
+ php: [8.0, 7.4]
+ stability: [prefer-lowest, prefer-stable]
+
+ name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
+ coverage: none
+
+ - name: Setup problem matchers
+ run: |
+ echo "::add-matcher::${{ runner.tool_cache }}/php.json"
+ echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
+
+ - name: Install dependencies
+ run: composer update --${{ matrix.stability }} --prefer-dist --no-interaction
+
+ - name: Execute tests
+ run: vendor/bin/phpunit
diff --git a/vendor/spatie/array-to-xml/.php_cs.dist b/vendor/spatie/array-to-xml/.php_cs.dist
new file mode 100644
index 00000000..ac127a7d
--- /dev/null
+++ b/vendor/spatie/array-to-xml/.php_cs.dist
@@ -0,0 +1,40 @@
+in([
+ __DIR__ . '/src',
+ __DIR__ . '/tests',
+ ])
+ ->name('*.php')
+ ->notName('*.blade.php')
+ ->ignoreDotFiles(true)
+ ->ignoreVCS(true);
+
+return PhpCsFixer\Config::create()
+ ->setRules([
+ '@PSR2' => true,
+ 'array_syntax' => ['syntax' => 'short'],
+ 'ordered_imports' => ['sortAlgorithm' => 'alpha'],
+ 'no_unused_imports' => true,
+ 'not_operator_with_successor_space' => true,
+ 'trailing_comma_in_multiline_array' => true,
+ 'phpdoc_scalar' => true,
+ 'unary_operator_spaces' => true,
+ 'binary_operator_spaces' => true,
+ 'blank_line_before_statement' => [
+ 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'],
+ ],
+ 'phpdoc_single_line_var_spacing' => true,
+ 'phpdoc_var_without_name' => true,
+ 'class_attributes_separation' => [
+ 'elements' => [
+ 'method',
+ ],
+ ],
+ 'method_argument_space' => [
+ 'on_multiline' => 'ensure_fully_multiline',
+ 'keep_multiple_spaces_after_comma' => true,
+ ],
+ 'single_trait_insert_per_statement' => true,
+ ])
+ ->setFinder($finder);
diff --git a/vendor/spatie/array-to-xml/CHANGELOG.md b/vendor/spatie/array-to-xml/CHANGELOG.md
new file mode 100755
index 00000000..f7e55541
--- /dev/null
+++ b/vendor/spatie/array-to-xml/CHANGELOG.md
@@ -0,0 +1,114 @@
+# Changelog
+
+All notable changes to `array-to-xml` will be documented in this file
+
+## 2.16.0 - 2020-11-18
+
+- add escapable colons in custom keys (#151)
+
+## 2.15.1 - 2020-11-12
+
+- add support for PHP 8
+
+## 2.15.0 - 2020-10-29
+
+- add $xmlStandalone as a new parameter (#148)
+
+## 2.14.0 - 2020-09-14
+
+- add support for dropping XML declaration (#145)
+
+## 2.13.0 - 2020-08-24
+
+- add support for custom keys (#140)
+
+## 2.12.1 - 2020-06-17
+
+- add XML prettification (#136)
+
+## 2.11.2 - 2019-08-21
+
+- fix XML structure when using numeric keys
+
+## 2.11.1 - 2019-07-25
+
+- do not interpret "0" as a non-empty value
+
+## 2.11.0 - 2019-09-26
+
+- drop support for PHP 7.1
+
+## 2.10.0 - 2019-09-26
+
+- add `setDomProperties`
+
+## 2.9.0 - 2019-05-06
+
+- add support for numeric keys
+
+## 2.8.1 - 2019-03-15
+
+- fix tests
+- drop support for PHP 7.0
+
+## 2.8.0 - 2018-11-29
+
+- added support for mixed content
+
+## 2.7.3 - 2018-10-30
+- fix for `DomExeception`s being thrown
+
+## 2.7.2 - 2018-09-17
+- remove control characters
+
+## 2.7.1 - 2018-02-02
+- fix setting attributes
+
+## 2.7.0 - 2017-09-07
+- allow wrapping data in a CDATA section
+
+## 2.6.1- 2017-08-29
+- add fix for multiple empty/self-closing child elements
+
+## 2.6.0 - 2017-08-25
+- add support for naming a root element and adding properties to it
+
+## 2.5.2 - 2017-08-03
+- avoid pulling in the snapshot package on install
+
+## 2.5.1 - 2017-05-30
+- PHP 7 is now required
+
+## 2.5.0 - 2017-05-22
+- allow encoding and version to be set
+
+## 2.4.0 - 2017-02-18
+- attributes and value can be set in SimpleXMLElement style
+
+## 2.3.0 - 2017-02-18
+- attributes and value can be set in SimpleXMLElement style
+
+## 2.2.1 - 2016-12-08
+- fixed an error when there is a special character to the value set in _value
+
+## 2.2.0 - 2016-06-04
+- added `toDom` method
+
+## 2.1.1 - 2016-02-23
+- Fixed typo in the name of the `addSequentialNode`-function
+
+## 2.1.0 - 2015-10-08
+- Add ability to use attributes
+
+## 2.0.0 - 2015-10-08
+- Add support to collection arrays and dynamically XML convertion when keys are numeric
+
+## 1.0.3 - 2015-10-03
+- handle values with special characters
+
+## 1.0.1 - 2015-03-18
+- use DOMDocument for better validation
+- added an option to opt out of the automatic space replacement
+
+## 1.0.0 - 2015-03-17
+- initial release
diff --git a/vendor/spatie/array-to-xml/LICENSE.md b/vendor/spatie/array-to-xml/LICENSE.md
new file mode 100755
index 00000000..59e5ec59
--- /dev/null
+++ b/vendor/spatie/array-to-xml/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) Spatie bvba
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/spatie/array-to-xml/README.md b/vendor/spatie/array-to-xml/README.md
new file mode 100755
index 00000000..f5813034
--- /dev/null
+++ b/vendor/spatie/array-to-xml/README.md
@@ -0,0 +1,513 @@
+# Convert an array to xml
+
+[![Latest Version](https://img.shields.io/github/release/spatie/array-to-xml.svg?style=flat-square)](https://github.com/spatie/array-to-xml/releases)
+[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
+![Tests](https://github.com/spatie/array-to-xml/workflows/Tests/badge.svg)
+[![Total Downloads](https://img.shields.io/packagist/dt/spatie/array-to-xml.svg?style=flat-square)](https://packagist.org/packages/spatie/array-to-xml)
+
+This package provides a very simple class to convert an array to an xml string.
+
+## Support us
+
+[ ](https://spatie.be/github-ad-click/array-to-xml)
+
+We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
+
+We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
+
+## Install
+
+You can install this package via composer.
+
+``` bash
+composer require spatie/array-to-xml
+```
+
+## Usage
+
+```php
+use Spatie\ArrayToXml\ArrayToXml;
+...
+$array = [
+ 'Good guy' => [
+ 'name' => 'Luke Skywalker',
+ 'weapon' => 'Lightsaber'
+ ],
+ 'Bad guy' => [
+ 'name' => 'Sauron',
+ 'weapon' => 'Evil Eye'
+ ]
+];
+
+$result = ArrayToXml::convert($array);
+```
+After running this piece of code `$result` will contain:
+
+```xml
+
+
+
+ Luke Skywalker
+ Lightsaber
+
+
+ Sauron
+ Evil Eye
+
+
+```
+
+
+### Setting the name of the root element
+
+Optionally you can set the name of the rootElement by passing it as the second argument. If you don't specify
+this argument (or set it to an empty string) "root" will be used.
+```
+$result = ArrayToXml::convert($array, 'customrootname');
+```
+
+### Handling key names
+
+By default all spaces in the key names of your array will be converted to underscores. If you want to opt out of
+this behaviour you can set the third argument to false. We'll leave all keynames alone.
+```
+$result = ArrayToXml::convert($array, 'customrootname', false);
+```
+
+### Adding attributes
+
+You can use a key named `_attributes` to add attributes to a node, and `_value` to specify the value.
+
+```php
+$array = [
+ 'Good guy' => [
+ '_attributes' => ['attr1' => 'value'],
+ 'name' => 'Luke Skywalker',
+ 'weapon' => 'Lightsaber'
+ ],
+ 'Bad guy' => [
+ 'name' => 'Sauron',
+ 'weapon' => 'Evil Eye'
+ ],
+ 'The survivor' => [
+ '_attributes' => ['house'=>'Hogwarts'],
+ '_value' => 'Harry Potter'
+ ]
+];
+
+$result = ArrayToXml::convert($array);
+```
+
+This code will result in:
+
+```xml
+
+
+
+ Luke Skywalker
+ Lightsaber
+
+
+ Sauron
+ Evil Eye
+
+
+ Harry Potter
+
+
+```
+
+*Note, that the value of the `_value` field must be a string. [(More)](https://github.com/spatie/array-to-xml/issues/75#issuecomment-413726065)*
+
+### Using reserved characters
+
+It is also possible to wrap the value of a node into a CDATA section. This allows you to use reserved characters.
+
+```php
+$array = [
+ 'Good guy' => [
+ 'name' => [
+ '_cdata' => 'Luke Skywalker '
+ ],
+ 'weapon' => 'Lightsaber'
+ ],
+ 'Bad guy' => [
+ 'name' => 'Sauron ',
+ 'weapon' => 'Evil Eye'
+ ]
+];
+
+$result = ArrayToXml::convert($array);
+```
+
+This code will result in:
+
+```xml
+
+
+
+ Luke Skywalker]]>
+ Lightsaber
+
+
+ <h1>Sauron</h1>
+ Evil Eye
+
+
+```
+
+If your input contains something that cannot be parsed a `DOMException` will be thrown.
+
+
+### Customize the XML declaration
+
+You could specify specific values in for:
+ - encoding as the fourth argument (string)
+ - version as the fifth argument (string)
+ - standalone as sixth argument (boolean)
+
+```php
+$result = ArrayToXml::convert($array, [], true, 'UTF-8', '1.1', [], true);
+```
+
+This will result in:
+
+```xml
+
+```
+
+
+### Adding attributes to the root element
+
+To add attributes to the root element provide an array with an `_attributes` key as the second argument.
+The root element name can then be set using the `rootElementName` key.
+
+```php
+$result = ArrayToXml::convert($array, [
+ 'rootElementName' => 'helloyouluckypeople',
+ '_attributes' => [
+ 'xmlns' => 'https://github.com/spatie/array-to-xml',
+ ],
+], true, 'UTF-8');
+```
+
+### Using a multi-dimensional array
+
+Use a multi-dimensional array to create a collection of elements.
+```php
+$array = [
+ 'Good guys' => [
+ 'Guy' => [
+ ['name' => 'Luke Skywalker', 'weapon' => 'Lightsaber'],
+ ['name' => 'Captain America', 'weapon' => 'Shield'],
+ ],
+ ],
+ 'Bad guys' => [
+ 'Guy' => [
+ ['name' => 'Sauron', 'weapon' => 'Evil Eye'],
+ ['name' => 'Darth Vader', 'weapon' => 'Lightsaber'],
+ ],
+ ],
+];
+```
+
+This will result in:
+
+```xml
+
+
+
+
+ Luke Skywalker
+ Lightsaber
+
+
+ Captain America
+ Shield
+
+
+
+
+ Sauron
+ Evil Eye
+
+
+ Darth Vader
+ Lightsaber
+
+
+
+```
+
+### Handling numeric keys
+
+The package can also can handle numeric keys:
+
+```php
+$array = [
+ 100 => [
+ 'name' => 'Vladimir',
+ 'nickname' => 'greeflas',
+ ],
+ 200 => [
+ 'name' => 'Marina',
+ 'nickname' => 'estacet',
+ ],
+];
+
+$result = ArrayToXml::convert(['__numeric' => $array]);
+```
+
+This will result in:
+
+```xml
+
+
+
+ Vladimir
+ greeflas
+
+
+ Marina
+ estacet
+
+
+```
+
+You can change key prefix with setter method called `setNumericTagNamePrefix()`.
+
+### Using custom keys
+
+The package can also can handle custom keys:
+
+```php
+$array = [
+ '__custom:custom-key:1' => [
+ 'name' => 'Vladimir',
+ 'nickname' => 'greeflas',
+ ],
+ '__custom:custom-key:2' => [
+ 'name' => 'Marina',
+ 'nickname' => 'estacet',
+ 'tags' => [
+ '__custom:tag:1' => 'first-tag',
+ '__custom:tag:2' => 'second-tag',
+ ]
+ ],
+];
+
+$result = ArrayToXml::convert($array);
+```
+
+This will result in:
+
+```xml
+
+
+
+ Vladimir
+ greeflas
+
+
+ Marina
+ estacet
+
+ first-tag
+ second-tag
+
+
+
+```
+
+A custom key contains three, colon-separated parts: "__custom:[custom-tag]:[unique-string]".
+
+- "__custom"
+ - The key always starts with "__custom".
+- [custom-tag]
+ - The string to be rendered as the XML tag.
+- [unique-string]
+ - A unique string that avoids overwriting of duplicate keys in PHP arrays.
+
+a colon character can be included within the custom-tag portion by escaping it with a backslash:
+
+```php
+$array = [
+ '__custom:ns\\:custom-key:1' => [
+ 'name' => 'Vladimir',
+ 'nickname' => 'greeflas',
+ ],
+ '__custom:ns\\:custom-key:2' => [
+ 'name' => 'Marina',
+ 'nickname' => 'estacet',
+ 'tags' => [
+ '__custom:ns\\:tag:1' => 'first-tag',
+ '__custom:ns\\:tag:2' => 'second-tag',
+ ]
+ ],
+];
+```
+This will result in:
+
+```xml
+
+
+
+ Vladimir
+ greeflas
+
+
+ Marina
+ estacet
+
+ first-tag
+ second-tag
+
+
+
+```
+
+### Setting DOMDocument properties
+
+To set properties of the internal DOMDocument object just pass an array consisting of keys and values. For a full list of valid properties consult https://www.php.net/manual/en/class.domdocument.php.
+
+You can use the constructor to set DOMDocument properties.
+
+```php
+$result = ArrayToXml::convert(
+ $array,
+ $rootElement,
+ $replaceSpacesByUnderScoresInKeyNames,
+ $xmlEncoding,
+ $xmlVersion,
+ ['formatOutput' => true]
+);
+
+```
+
+Alternatively you can use `setDomProperties`
+
+```php
+$arrayToXml = new ArrayToXml($array);
+$arrayToXml->setDomProperties(['formatOutput' => true]);
+$result = $arrayToXml->toXml();
+```
+
+### XML Prettification
+
+Call `$arrayToXml->prettify()` method on ArrayToXml to set XML in pretty form.
+
+Example:
+
+```php
+$array = [
+ 'Good guy' => [
+ 'name' => 'Luke Skywalker',
+ 'weapon' => 'Lightsaber'
+ ],
+ 'Bad guy' => [
+ 'name' => 'Sauron',
+ 'weapon' => 'Evil Eye'
+ ]
+];
+$arrayToXml = new ArrayToXml($array);
+```
+
+With prettification:
+
+```php
+$arrayToXml->prettify()->toXml();
+```
+
+will result in:
+
+```xml
+
+
+
+ Luke Skywalker
+ Lightsaber
+
+
+ Sauron
+ Evil Eye
+
+
+```
+
+Without prettification:
+
+```php
+$arrayToXml->toXml();
+```
+
+will result in:
+
+```xml
+
+Luke Skywalker Lightsaber Sauron Evil Eye
+```
+
+### Dropping XML declaration
+
+Call `$arrayToXml->dropXmlDeclaration()` method on ArrayToXml object to omit default XML declaration on top of the generated XML.
+
+Example:
+
+```php
+$root = [
+ 'rootElementName' => 'soap:Envelope',
+ '_attributes' => [
+ 'xmlns:soap' => 'http://www.w3.org/2003/05/soap-envelope/',
+ ],
+];
+$array = [
+ 'soap:Header' => [],
+ 'soap:Body' => [
+ 'soap:key' => 'soap:value',
+ ],
+];
+$arrayToXml = new ArrayToXml($array, $root);
+
+$result = $arrayToXml->dropXmlDeclaration()->toXml();
+```
+
+This will result in:
+
+```xml
+soap:value
+```
+
+## Testing
+
+```bash
+vendor/bin/phpunit
+```
+
+### Changelog
+
+Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently.
+
+## Contributing
+
+Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
+
+## Security
+
+If you discover any security related issues, please email freek@spatie.be instead of using the issue tracker.
+
+## Postcardware
+
+You're free to use this package, but if it makes it to your production environment we highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using.
+
+Our address is: Spatie, Kruikstraat 22, 2018 Antwerp, Belgium.
+
+We publish all received postcards [on our company website](https://spatie.be/en/opensource/postcards).
+
+## Credits
+
+- [Freek Van der Herten](https://github.com/freekmurze)
+- [All Contributors](../../contributors)
+
+## License
+
+The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
diff --git a/vendor/spatie/array-to-xml/composer.json b/vendor/spatie/array-to-xml/composer.json
new file mode 100755
index 00000000..90520ab8
--- /dev/null
+++ b/vendor/spatie/array-to-xml/composer.json
@@ -0,0 +1,38 @@
+{
+ "name": "spatie/array-to-xml",
+ "description": "Convert an array to xml",
+ "keywords": [
+ "convert",
+ "array",
+ "xml"
+ ],
+ "homepage": "https://github.com/spatie/array-to-xml",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Freek Van der Herten",
+ "email": "freek@spatie.be",
+ "homepage": "https://freek.dev",
+ "role": "Developer"
+ }
+ ],
+ "require": {
+ "php" : "^7.4|^8.0",
+ "ext-dom": "*"
+ },
+ "require-dev": {
+ "phpunit/phpunit" : "^9.0",
+ "mockery/mockery": "^1.2",
+ "spatie/phpunit-snapshot-assertions": "^4.2"
+ },
+ "autoload": {
+ "psr-4": {
+ "Spatie\\ArrayToXml\\": "src"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Spatie\\ArrayToXml\\Test\\": "tests"
+ }
+ }
+}
diff --git a/vendor/spatie/array-to-xml/phpunit.xml b/vendor/spatie/array-to-xml/phpunit.xml
new file mode 100644
index 00000000..b2daa31b
--- /dev/null
+++ b/vendor/spatie/array-to-xml/phpunit.xml
@@ -0,0 +1,13 @@
+
+
+
+
+ src/
+
+
+
+
+ ./tests/
+
+
+
diff --git a/vendor/spatie/array-to-xml/src/ArrayToXml.php b/vendor/spatie/array-to-xml/src/ArrayToXml.php
new file mode 100644
index 00000000..66eb49e3
--- /dev/null
+++ b/vendor/spatie/array-to-xml/src/ArrayToXml.php
@@ -0,0 +1,264 @@
+document = new DOMDocument($xmlVersion, $xmlEncoding);
+
+ if (! is_null($xmlStandalone)) {
+ $this->document->xmlStandalone = $xmlStandalone;
+ }
+
+ if (! empty($domProperties)) {
+ $this->setDomProperties($domProperties);
+ }
+
+ $this->replaceSpacesByUnderScoresInKeyNames = $replaceSpacesByUnderScoresInKeyNames;
+
+ if ($this->isArrayAllKeySequential($array) && ! empty($array)) {
+ throw new DOMException('Invalid Character Error');
+ }
+
+ $root = $this->createRootElement($rootElement);
+
+ $this->document->appendChild($root);
+
+ $this->convertElement($root, $array);
+ }
+
+ public function setNumericTagNamePrefix(string $prefix)
+ {
+ $this->numericTagNamePrefix = $prefix;
+ }
+
+ public static function convert(
+ array $array,
+ $rootElement = '',
+ bool $replaceSpacesByUnderScoresInKeyNames = true,
+ string $xmlEncoding = null,
+ string $xmlVersion = '1.0',
+ array $domProperties = [],
+ bool $xmlStandalone = null
+ ) {
+ $converter = new static(
+ $array,
+ $rootElement,
+ $replaceSpacesByUnderScoresInKeyNames,
+ $xmlEncoding,
+ $xmlVersion,
+ $domProperties,
+ $xmlStandalone
+ );
+
+ return $converter->toXml();
+ }
+
+ public function toXml(): string
+ {
+ if ($this->addXmlDeclaration === false) {
+ return $this->document->saveXml($this->document->documentElement);
+ }
+
+ return $this->document->saveXML();
+ }
+
+ public function toDom(): DOMDocument
+ {
+ return $this->document;
+ }
+
+ protected function ensureValidDomProperties(array $domProperties)
+ {
+ foreach ($domProperties as $key => $value) {
+ if (! property_exists($this->document, $key)) {
+ throw new Exception($key.' is not a valid property of DOMDocument');
+ }
+ }
+ }
+
+ public function setDomProperties(array $domProperties)
+ {
+ $this->ensureValidDomProperties($domProperties);
+
+ foreach ($domProperties as $key => $value) {
+ $this->document->{$key} = $value;
+ }
+
+ return $this;
+ }
+
+ public function prettify()
+ {
+ $this->document->preserveWhiteSpace = false;
+ $this->document->formatOutput = true;
+
+ return $this;
+ }
+
+ public function dropXmlDeclaration()
+ {
+ $this->addXmlDeclaration = false;
+
+ return $this;
+ }
+
+ private function convertElement(DOMElement $element, $value)
+ {
+ $sequential = $this->isArrayAllKeySequential($value);
+
+ if (! is_array($value)) {
+ $value = htmlspecialchars($value);
+
+ $value = $this->removeControlCharacters($value);
+
+ $element->nodeValue = $value;
+
+ return;
+ }
+
+ foreach ($value as $key => $data) {
+ if (! $sequential) {
+ if (($key === '_attributes') || ($key === '@attributes')) {
+ $this->addAttributes($element, $data);
+ } elseif ((($key === '_value') || ($key === '@value')) && is_string($data)) {
+ $element->nodeValue = htmlspecialchars($data);
+ } elseif ((($key === '_cdata') || ($key === '@cdata')) && is_string($data)) {
+ $element->appendChild($this->document->createCDATASection($data));
+ } elseif ((($key === '_mixed') || ($key === '@mixed')) && is_string($data)) {
+ $fragment = $this->document->createDocumentFragment();
+ $fragment->appendXML($data);
+ $element->appendChild($fragment);
+ } elseif ($key === '__numeric') {
+ $this->addNumericNode($element, $data);
+ } elseif (substr($key, 0, 9) === '__custom:') {
+ $this->addNode($element, str_replace('\:', ':', preg_split('/(?addNode($element, $key, $data);
+ }
+ } elseif (is_array($data)) {
+ $this->addCollectionNode($element, $data);
+ } else {
+ $this->addSequentialNode($element, $data);
+ }
+ }
+ }
+
+ protected function addNumericNode(DOMElement $element, $value)
+ {
+ foreach ($value as $key => $item) {
+ $this->convertElement($element, [$this->numericTagNamePrefix.$key => $item]);
+ }
+ }
+
+ protected function addNode(DOMElement $element, $key, $value)
+ {
+ if ($this->replaceSpacesByUnderScoresInKeyNames) {
+ $key = str_replace(' ', '_', $key);
+ }
+
+ $child = $this->document->createElement($key);
+ $element->appendChild($child);
+ $this->convertElement($child, $value);
+ }
+
+ protected function addCollectionNode(DOMElement $element, $value)
+ {
+ if ($element->childNodes->length === 0 && $element->attributes->length === 0) {
+ $this->convertElement($element, $value);
+
+ return;
+ }
+
+ $child = $this->document->createElement($element->tagName);
+ $element->parentNode->appendChild($child);
+ $this->convertElement($child, $value);
+ }
+
+ protected function addSequentialNode(DOMElement $element, $value)
+ {
+ if (empty($element->nodeValue) && ! is_numeric($element->nodeValue)) {
+ $element->nodeValue = htmlspecialchars($value);
+
+ return;
+ }
+
+ $child = new DOMElement($element->tagName);
+ $child->nodeValue = htmlspecialchars($value);
+ $element->parentNode->appendChild($child);
+ }
+
+ protected function isArrayAllKeySequential($value)
+ {
+ if (! is_array($value)) {
+ return false;
+ }
+
+ if (count($value) <= 0) {
+ return true;
+ }
+
+ if (\key($value) === '__numeric') {
+ return false;
+ }
+
+ return array_unique(array_map('is_int', array_keys($value))) === [true];
+ }
+
+ protected function addAttributes(DOMElement $element, array $data)
+ {
+ foreach ($data as $attrKey => $attrVal) {
+ $element->setAttribute($attrKey, $attrVal);
+ }
+ }
+
+ protected function createRootElement($rootElement): DOMElement
+ {
+ if (is_string($rootElement)) {
+ $rootElementName = $rootElement ?: 'root';
+
+ return $this->document->createElement($rootElementName);
+ }
+
+ $rootElementName = $rootElement['rootElementName'] ?? 'root';
+
+ $element = $this->document->createElement($rootElementName);
+
+ foreach ($rootElement as $key => $value) {
+ if ($key !== '_attributes' && $key !== '@attributes') {
+ continue;
+ }
+
+ $this->addAttributes($element, $rootElement[$key]);
+ }
+
+ return $element;
+ }
+
+ protected function removeControlCharacters(string $value): string
+ {
+ return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/', '', $value);
+ }
+}