Compare commits

...

5 Commits

Author SHA1 Message Date
jayssj11
336f30b56c auto capture of url and tab name, need to refactor code again (#3) 2024-10-29 08:04:16 +01:00
jayssj11
31a531fd07 Chat component (#2)
* request capture when server responds with Sample Requests and Responses but do not capture response yet

* break down request and response in separate entities, still need to capture response properly

* response capture works

* code refactor
2024-10-22 10:34:39 +02:00
jayssj11
4ad394419b chat window added to the extension (#1) 2024-10-02 16:47:38 +02:00
Tanner
25f35d0051 feat: moving requestbuilder.tsx from utils to it's own component (#92) 2024-08-28 04:10:28 -04:00
tsukino
5ccdd9b06a feat: add default plugins to extension bundle (#93) 2024-08-28 04:05:56 -04:00
21 changed files with 607 additions and 62 deletions

161
package-lock.json generated
View File

@@ -11,13 +11,14 @@
"dependencies": {
"@extism/extism": "^1.0.2",
"@fortawesome/fontawesome-free": "^6.4.2",
"@types/axios": "^0.14.0",
"async-mutex": "^0.4.0",
"axios": "^1.7.7",
"buffer": "^6.0.3",
"charwise": "^3.0.1",
"classnames": "^2.3.2",
"comlink": "^4.4.1",
"copy-to-clipboard": "^3.3.3",
"dayjs": "^1.11.12",
"fast-deep-equal": "^3.1.3",
"fuse.js": "^6.6.2",
"level": "^8.0.0",
@@ -33,7 +34,7 @@
"redux-thunk": "^2.4.2",
"tailwindcss": "^3.3.3",
"tlsn-js": "0.1.0-alpha.6.2",
"tlsn-jsV5.3": "npm:tlsn-js@0.1.0-alpha.5.3"
"tlsn-js-v5": "npm:tlsn-js@0.1.0-alpha.5.4"
},
"devDependencies": {
"@babel/core": "^7.20.12",
@@ -3550,6 +3551,16 @@
"integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==",
"dev": true
},
"node_modules/@types/axios": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
"integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==",
"deprecated": "This is a stub types definition for axios (https://github.com/mzabriskie/axios). axios provides its own type definitions, so you don't need @types/axios installed!",
"license": "MIT",
"dependencies": {
"axios": "*"
}
},
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@@ -4836,6 +4847,12 @@
"tslib": "^2.4.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/autoprefixer": {
"version": "10.4.19",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
@@ -4897,6 +4914,17 @@
"node": ">=4"
}
},
"node_modules/axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -5561,6 +5589,18 @@
"node": ">=0.1.90"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/comlink": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.1.tgz",
@@ -6095,11 +6135,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/dayjs": {
"version": "1.11.12",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz",
"integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -6238,6 +6273,15 @@
"node": ">=0.10.0"
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -7712,7 +7756,6 @@
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true,
"funding": [
{
"type": "individual",
@@ -7763,6 +7806,20 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -9594,7 +9651,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
@@ -9603,7 +9659,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
@@ -11449,6 +11504,12 @@
"node": ">= 0.10"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
"node_modules/pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@@ -13009,11 +13070,12 @@
"node": ">= 16.20.2"
}
},
"node_modules/tlsn-jsV5.3": {
"node_modules/tlsn-js-v5": {
"name": "tlsn-js",
"version": "0.1.0-alpha.5.3",
"resolved": "https://registry.npmjs.org/tlsn-js/-/tlsn-js-0.1.0-alpha.5.3.tgz",
"integrity": "sha512-eTSCQ6MaH8mN2oCfLsQDe/mdfTlIq74MbKVesV6M01C2SRE0FJcVlTWMsnT3L+wOpvOlvsiBeCWV7T9m/YMzew==",
"version": "0.1.0-alpha.5.4",
"resolved": "https://registry.npmjs.org/tlsn-js/-/tlsn-js-0.1.0-alpha.5.4.tgz",
"integrity": "sha512-qbqaDjApXarohn/XMJrxMsNHYTCzy+pw0Fc8gtPPN17PLE+1DwwLTXAAhnxYqYQyo3+Hmy+ODd4C02+KCwRWmg==",
"license": "ISC",
"dependencies": {
"comlink": "^4.4.1"
},
@@ -16331,6 +16393,14 @@
"integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==",
"dev": true
},
"@types/axios": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz",
"integrity": "sha512-KqQnQbdYE54D7oa/UmYVMZKq7CO4l8DEENzOKc4aBRwxCXSlJXGz83flFx5L7AWrOQnmuN3kVsRdt+GZPPjiVQ==",
"requires": {
"axios": "*"
}
},
"@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
@@ -17380,6 +17450,11 @@
"tslib": "^2.4.0"
}
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"autoprefixer": {
"version": "10.4.19",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz",
@@ -17409,6 +17484,16 @@
"integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==",
"dev": true
},
"axios": {
"version": "1.7.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
"requires": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -17893,6 +17978,14 @@
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"comlink": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.1.tgz",
@@ -18249,11 +18342,6 @@
"is-data-view": "^1.0.1"
}
},
"dayjs": {
"version": "1.11.12",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.12.tgz",
"integrity": "sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@@ -18358,6 +18446,11 @@
}
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
@@ -19487,8 +19580,7 @@
"follow-redirects": {
"version": "1.15.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"dev": true
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA=="
},
"for-each": {
"version": "0.3.3",
@@ -19515,6 +19607,16 @@
}
}
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
@@ -20783,14 +20885,12 @@
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"requires": {
"mime-db": "1.52.0"
}
@@ -21881,6 +21981,11 @@
}
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
},
"pseudomap": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@@ -22990,10 +23095,10 @@
"resolved": "https://registry.npmjs.org/tlsn-js/-/tlsn-js-0.1.0-alpha.6.2.tgz",
"integrity": "sha512-6PANWQEFw48VFNfDgA017zcNRae2OutzNmE5Xcc/h6lwkZZ8MBZIFAFfz/WHZJ3fcFJumaKrG80gpomP6blZqw=="
},
"tlsn-jsV5.3": {
"version": "npm:tlsn-js@0.1.0-alpha.5.3",
"resolved": "https://registry.npmjs.org/tlsn-js/-/tlsn-js-0.1.0-alpha.5.3.tgz",
"integrity": "sha512-eTSCQ6MaH8mN2oCfLsQDe/mdfTlIq74MbKVesV6M01C2SRE0FJcVlTWMsnT3L+wOpvOlvsiBeCWV7T9m/YMzew==",
"tlsn-js-v5": {
"version": "npm:tlsn-js@0.1.0-alpha.5.4",
"resolved": "https://registry.npmjs.org/tlsn-js/-/tlsn-js-0.1.0-alpha.5.4.tgz",
"integrity": "sha512-qbqaDjApXarohn/XMJrxMsNHYTCzy+pw0Fc8gtPPN17PLE+1DwwLTXAAhnxYqYQyo3+Hmy+ODd4C02+KCwRWmg==",
"requires": {
"comlink": "^4.4.1"
}

View File

@@ -18,7 +18,9 @@
"dependencies": {
"@extism/extism": "^1.0.2",
"@fortawesome/fontawesome-free": "^6.4.2",
"@types/axios": "^0.14.0",
"async-mutex": "^0.4.0",
"axios": "^1.7.7",
"buffer": "^6.0.3",
"charwise": "^3.0.1",
"classnames": "^2.3.2",
@@ -39,7 +41,7 @@
"redux-thunk": "^2.4.2",
"tailwindcss": "^3.3.3",
"tlsn-js": "0.1.0-alpha.6.2",
"tlsn-jsV5.3": "npm:tlsn-js@0.1.0-alpha.5.3"
"tlsn-js-v5": "npm:tlsn-js@0.1.0-alpha.5.4"
},
"devDependencies": {
"@babel/core": "^7.20.12",

View File

@@ -1 +0,0 @@
You can find example plugins at https://github.com/tlsnotary/tlsn-plugin-boilerplate/tree/main/examples

10
pnpm-lock.yaml generated
View File

@@ -74,9 +74,9 @@ dependencies:
tlsn-js:
specifier: 0.1.0-alpha.6.2
version: 0.1.0-alpha.6.2
tlsn-jsV5.3:
specifier: npm:tlsn-js@0.1.0-alpha.5.3
version: /tlsn-js@0.1.0-alpha.5.3
tlsn-js-v5:
specifier: npm:tlsn-js@0.1.0-alpha.5.4
version: /tlsn-js@0.1.0-alpha.5.4
devDependencies:
'@babel/core':
@@ -7974,8 +7974,8 @@ packages:
resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==}
dev: true
/tlsn-js@0.1.0-alpha.5.3:
resolution: {integrity: sha512-eTSCQ6MaH8mN2oCfLsQDe/mdfTlIq74MbKVesV6M01C2SRE0FJcVlTWMsnT3L+wOpvOlvsiBeCWV7T9m/YMzew==}
/tlsn-js@0.1.0-alpha.5.4:
resolution: {integrity: sha512-qbqaDjApXarohn/XMJrxMsNHYTCzy+pw0Fc8gtPPN17PLE+1DwwLTXAAhnxYqYQyo3+Hmy+ODd4C02+KCwRWmg==}
engines: {node: '>= 16.20.2'}
dependencies:
comlink: 4.4.1

Binary file not shown.

Binary file not shown.

View File

@@ -24,6 +24,7 @@ import {
} from '../../utils/plugins';
import { ErrorModal } from '../ErrorModal';
import classNames from 'classnames';
import DefaultPluginIcon from '../../assets/img/default-plugin-icon.png';
export default function PluginUploadInfo(): ReactElement {
const [error, showError] = useState('');
@@ -122,7 +123,7 @@ export function PluginInfoModal(props: {
<ModalHeader className="w-full p-2 border-gray-200 text-gray-500">
{header || (
<div className="flex flex-row items-end justify-start gap-2">
<img className="h-5" src={logo} alt="logo" />
<img className="h-5" src={logo || DefaultPluginIcon} alt="logo" />
<span className="font-semibold">{`Installing ${pluginContent.title}`}</span>
</div>
)}
@@ -132,7 +133,7 @@ export function PluginInfoModal(props: {
<>
<img
className="w-12 h-12"
src={pluginContent.icon}
src={pluginContent.icon || DefaultPluginIcon}
alt="Plugin Icon"
/>
<span className="text-3xl text-center">

View File

@@ -169,7 +169,7 @@ export function Plugin(props: {
<PluginInfoModalContent className="flex flex-col items-center cursor-default">
<img
className="w-12 h-12 mb-2"
src={config.icon}
src={config.icon || DefaultPluginIcon}
alt="Plugin Icon"
/>
<span className="text-3xl text-blue-600 font-semibold">

View File

@@ -28,6 +28,12 @@ const cookiesDb = db.sublevel<string, boolean>('cookies', {
const headersDb = db.sublevel<string, boolean>('headers', {
valueEncoding: 'json',
});
const appDb = db.sublevel<string, any>('app', {
valueEncoding: 'json',
});
enum AppDatabaseKey {
DefaultPluginsInstalled = 'DefaultPluginsInstalled',
}
export async function addNotaryRequest(
now = Date.now(),
@@ -372,3 +378,19 @@ export async function getHeadersByHost(host: string) {
}
return ret;
}
async function getDefaultPluginsInstalled(): Promise<boolean> {
return appDb.get(AppDatabaseKey.DefaultPluginsInstalled).catch(() => false);
}
export async function setDefaultPluginsInstalled(installed = false) {
return mutex.runExclusive(async () => {
await appDb.put(AppDatabaseKey.DefaultPluginsInstalled, installed);
});
}
export async function getAppState() {
return {
defaultPluginsInstalled: await getDefaultPluginsInstalled(),
};
}

View File

@@ -1,6 +1,8 @@
import { onBeforeRequest, onResponseStarted, onSendHeaders } from './handlers';
import { deleteCacheByTabId } from './cache';
import browser from 'webextension-polyfill';
import { getAppState, setDefaultPluginsInstalled } from './db';
import { installPlugin } from './plugins/utils';
(async () => {
browser.webRequest.onSendHeaders.addListener(
@@ -31,6 +33,19 @@ import browser from 'webextension-polyfill';
deleteCacheByTabId(tabId);
});
const { defaultPluginsInstalled } = await getAppState();
if (!defaultPluginsInstalled) {
try {
const twitterProfileUrl = browser.runtime.getURL('twitter_profile.wasm');
const discordDmUrl = browser.runtime.getURL('discord_dm.wasm');
await installPlugin(twitterProfileUrl);
await installPlugin(discordDmUrl);
} finally {
await setDefaultPluginsInstalled(true);
}
}
const { initRPC } = await import('./rpc');
await createOffscreenDocument();
initRPC();

View File

@@ -0,0 +1,29 @@
import { addPlugin, addPluginConfig, addPluginMetadata } from '../db';
import { getPluginConfig } from '../../../utils/misc';
export async function installPlugin(
urlOrBuffer: ArrayBuffer | string,
origin = '',
filePath = '',
metadata: {[key: string]: string} = {},
) {
let arrayBuffer;
if (typeof urlOrBuffer === 'string') {
const resp = await fetch(urlOrBuffer);
arrayBuffer = await resp.arrayBuffer();
} else {
arrayBuffer = urlOrBuffer;
}
const config = await getPluginConfig(arrayBuffer);
const hex = Buffer.from(arrayBuffer).toString('hex');
const hash = await addPlugin(hex);
await addPluginConfig(hash!, config);
await addPluginMetadata(hash!, {
...metadata,
origin,
filePath,
});
return hash;
}

View File

@@ -24,6 +24,8 @@ import {
getPlugins,
getCookiesByHost,
getHeadersByHost,
getAppState,
setDefaultPluginsInstalled,
} from './db';
import { addOnePlugin, removeOnePlugin } from '../../reducers/plugins';
import {
@@ -85,6 +87,8 @@ export enum BackgroundActiontype {
run_plugin_request = 'run_plugin_request',
run_plugin_response = 'run_plugin_response',
get_logging_level = 'get_logging_level',
get_app_state = 'get_app_state',
set_default_plugins_installed = 'set_default_plugins_installed',
}
export type BackgroundAction = {
@@ -192,6 +196,12 @@ export const initRPC = () => {
case BackgroundActiontype.get_logging_level:
getLoggingFilter().then(sendResponse);
return true;
case BackgroundActiontype.get_app_state:
getAppState().then(sendResponse);
return true;
case BackgroundActiontype.set_default_plugins_installed:
setDefaultPluginsInstalled(request.data).then(sendResponse);
return true;
default:
break;
}

View File

@@ -7,7 +7,7 @@ import {
NotarizedSession as _NotarizedSession,
TlsProof as _TlsProof,
} from 'tlsn-js';
import { verify } from 'tlsn-jsV5.3';
import { verify } from 'tlsn-js-v5';
import { urlify } from '../../utils/misc';
import { BackgroundActiontype } from '../Background/rpc';

View File

@@ -12,6 +12,7 @@ import Requests from '../../pages/Requests';
import Options from '../../pages/Options';
import Request from '../../pages/Requests/Request';
import Home from '../../pages/Home';
import Chat from '../../pages/Chat';
import logo from '../../assets/img/icon-128.png';
import RequestBuilder from '../../pages/RequestBuilder';
import Notarize from '../../pages/Notarize';
@@ -104,6 +105,7 @@ const Popup = () => {
<Route path="/custom/*" element={<RequestBuilder />} />
<Route path="/options" element={<Options />} />
<Route path="/home" element={<Home />} />
<Route path="/chat" element={<Chat />} />
<Route path="/plugininfo" element={<PluginUploadInfo />} />
<Route path="/connection-approval" element={<ConnectionApproval />} />
<Route path="/get-history-approval" element={<GetHistoryApproval />} />

View File

@@ -28,7 +28,7 @@
],
"web_accessible_resources": [
{
"resources": ["content.styles.css", "icon-128.png", "icon-34.png", "content.bundle.js"],
"resources": ["content.styles.css", "icon-128.png", "icon-34.png", "content.bundle.js", "discord_dm.wasm", "twitter_profile.wasm"],
"matches": ["http://*/*", "https://*/*", "<all_urls>"]
}
],

63
src/pages/Chat/chat.css Normal file
View File

@@ -0,0 +1,63 @@
.chat-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.chat-window {
height: 400px;
border: 1px solid #ccc;
overflow-y: auto;
padding: 10px;
margin-bottom: 20px;
}
.message {
margin-bottom: 10px;
padding: 8px 12px;
border-radius: 20px;
max-width: 70%;
}
.user {
background-color: #007bff;
color: white;
align-self: flex-end;
margin-left: auto;
}
.bot {
background-color: #f1f0f0;
color: black;
align-self: flex-start;
}
.chat-input {
display: flex;
}
.chat-input input {
flex-grow: 1;
padding: 10px;
font-size: 16px;
}
.chat-input button {
padding: 10px 20px;
font-size: 16px;
background-color: #007bff;
color: white;
border: none;
cursor: pointer;
}
.chat-buttons {
display: flex;
gap: 10px;
}
.clear-button {
background-color: #f44336;
color: white;
border: none;
}

303
src/pages/Chat/index.tsx Normal file
View File

@@ -0,0 +1,303 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import './Chat.css';
import { useRequests } from '../../reducers/requests';
import { extractBodyFromResponse } from '../../utils/misc';
interface Message {
id: number;
text: string;
sender: 'user' | 'bot';
}
interface CapturedData {
request: string;
headers: Record<string, string>;
response: string;
}
interface RequestData {
method: string;
url: string;
headers: Record<string, string>;
body?: string;
}
interface TabInfo {
url: string;
title: string;
favicon: string;
}
const Chat: React.FC = () => {
const [messages, setMessages] = useState<Message[]>(() => {
const savedMessages = localStorage.getItem('chatMessages');
return savedMessages ? JSON.parse(savedMessages) : [];
});
const [inputMessage, setInputMessage] = useState('');
const [allRequests, setAllRequests] = useState<RequestData[]>([]);
const [isConnected, setIsConnected] = useState(false);
const socketRef = useRef<WebSocket | null>(null);
const [chatId, setChatId] = useState<string | null>(null);
const requests = useRequests();
const [capturedData, setCapturedData] = useState<CapturedData[]>([]);
const [hasSetInitialTabInfo, setHasSetInitialTabInfo] = useState(false);
useEffect(() => {
localStorage.setItem('chatMessages', JSON.stringify(messages));
}, [messages]);
const getCurrentTabInfo = async (): Promise<TabInfo> => {
return new Promise((resolve) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const currentTab = tabs[0];
resolve({
url: currentTab.url || '',
title: currentTab.title || '',
favicon: currentTab.favIconUrl || ''
});
});
});
};
const initializeChat = async () => {
const storedChatId = localStorage.getItem('chatId');
if (storedChatId) {
setChatId(storedChatId);
await connectWebSocket(storedChatId);
} else {
await fetchNewChatId();
}
};
useEffect(() => {
initializeChat();
return () => {
if (socketRef.current) {
socketRef.current.close();
}
};
}, []);
// Effect to set initial tab info when connection is established
useEffect(() => {
const setInitialTabInfo = async () => {
if (isConnected && messages.length === 0 && !hasSetInitialTabInfo) {
const tabInfo = await getCurrentTabInfo();
setInputMessage(`Current Page: ${tabInfo.title}\n website URL: ${tabInfo.url}`);
setHasSetInitialTabInfo(true);
// Send initial info to background script
chrome.runtime.sendMessage({
type: 'TAB_INFO',
data: tabInfo
});
}
};
setInitialTabInfo();
}, [isConnected, messages.length, hasSetInitialTabInfo]);
const fetchNewChatId = async () => {
try {
const response = await fetch('http://localhost:8000/get_chat_id');
const data = await response.json();
const newChatId = data.chat_id;
localStorage.setItem('chatId', newChatId);
setChatId(newChatId);
await connectWebSocket(newChatId);
} catch (error) {
console.error('Failed to fetch chat ID:', error);
}
};
const captureRequestAndResponse = useCallback(async (req: RequestData) => {
try {
const response = await fetch(req.url, {
method: req.method,
headers: req.headers,
body: req.body,
});
const responseText = await extractBodyFromResponse(response);
const headers: Record<string, string> = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
setCapturedData(prevData => [...prevData, {
request: `${req.method} ${req.url}`,
headers,
response: responseText,
}]);
} catch (error) {
console.error('Error capturing request and response:', error);
}
}, []);
const fetchMultipleRequests = async (requests: RequestData[]) => {
try {
const fetchPromises = requests.map(async (req) => {
if (req.headers === null || req.headers === undefined) {
req.headers = {};
}
if (req.body === null || req.body === undefined) {
req.body = '';
}
const response = await fetch(req.url, {
method: req.method,
headers: req.headers,
});
const responseText = await response.text();
return {
request: `${req.method} ${req.url}`,
headers: req.headers,
response: responseText,
};
});
const responses = await Promise.all(fetchPromises);
setCapturedData(prevData => [...prevData, ...responses]);
const response_message = responses.map(data => data.response).join('\n');
setInputMessage(response_message);
} catch (error) {
console.error('Error fetching multiple requests:', error);
}
};
const connectWebSocket = async (id: string) => {
return new Promise<void>((resolve, reject) => {
socketRef.current = new WebSocket(`ws://localhost:8000/ws/${id}`);
socketRef.current.onopen = () => {
console.log('WebSocket connection established');
setIsConnected(true);
resolve();
};
socketRef.current.onmessage = (event) => {
const botResponse: Message = {
id: Date.now(),
text: event.data,
sender: 'bot',
};
setMessages((prevMessages) => [...prevMessages, botResponse]);
if (botResponse.text.includes("send_request_function")) {
const updatedRequests = requests.map(req => ({
method: req.method,
url: req.url,
headers: req.requestHeaders.reduce((acc: { [key: string]: string }, h: any) => {
if (h.name && h.value) acc[h.name] = h.value;
return acc;
}, {}),
}));
setAllRequests(updatedRequests);
const requestDetails = updatedRequests.map(req =>
`${req.method} ${req.url}\nHeaders: ${JSON.stringify(req.headers, null, 2)}`
).join('\n\n');
setInputMessage(requestDetails);
}
if (botResponse.text.includes("send_response_function")) {
const regex = /"send_response_function"\s*:\s*(\[.*?\])/s;
const match = botResponse.text.match(regex);
if (match) {
const requestArrayString = match[1];
try {
const requestArray: RequestData[] = JSON.parse(requestArrayString);
fetchMultipleRequests(requestArray);
} catch (error) {
console.error("Error parsing JSON:", error);
}
}
}
};
socketRef.current.onclose = () => {
console.log('WebSocket connection closed');
setIsConnected(false);
};
socketRef.current.onerror = (error) => {
console.error('WebSocket error:', error);
reject(error);
};
});
};
useEffect(() => {
if (capturedData.length > 0 && isConnected) {
const capturedDataMessage = JSON.stringify(capturedData);
socketRef.current?.send(capturedDataMessage);
setCapturedData([]);
}
}, [capturedData, isConnected]);
const sendMessage = () => {
if (inputMessage.trim() === '' || !isConnected) return;
const newMessage: Message = {
id: Date.now(),
text: inputMessage,
sender: 'user',
};
setMessages((prevMessages) => [...prevMessages, newMessage]);
setInputMessage('');
if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
socketRef.current.send(inputMessage);
} else {
console.error('WebSocket is not connected');
}
};
const clearChat = () => {
setMessages([]);
setAllRequests([]);
setCapturedData([]);
setHasSetInitialTabInfo(false);
};
return (
<div className="chat-container">
<div className="chat-window">
{messages.map((message) => (
<div
key={message.id}
className={`message ${message.sender === 'user' ? 'user' : 'bot'}`}
>
{message.text}
</div>
))}
</div>
<div className="chat-input">
<input
type="text"
value={inputMessage}
onChange={(e) => setInputMessage(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Type your message..."
className="chat-input-field"
/>
<div className="chat-buttons">
<button
onClick={sendMessage}
className="send-button"
disabled={!isConnected}
>
Send
</button>
<button
onClick={clearChat}
className="clear-button"
style={{ backgroundColor: '#f44336', color: 'white', border: 'none' }}
>
Clear Chat
</button>
</div>
</div>
{!isConnected && <div className="connection-status">Disconnected</div>}
</div>
);
};
export default Chat;

View File

@@ -44,6 +44,9 @@ export default function Home(): ReactElement {
<NavButton fa="fa-solid fa-gear" onClick={() => navigate('/options')}>
Options
</NavButton>
<NavButton fa="fa-solid fa-comment-dots" onClick={() => navigate('/chat')}>
Chat
</NavButton>
</div>
<PluginList className="mx-4" />
</div>

View File

@@ -24,7 +24,7 @@ import {
InputBody,
FormBodyTable,
parseResponse,
} from '../../utils/requestbuilder';
} from '../../components/RequestBuilder';
enum TabType {
Params = 'Params',

View File

@@ -40,7 +40,8 @@ var options = {
mode: process.env.NODE_ENV || "development",
ignoreWarnings: [
/Circular dependency between chunks with runtime/,
/ResizeObserver loop completed with undelivered notifications/
/ResizeObserver loop completed with undelivered notifications/,
/Should not import the named export/,
],
entry: {
@@ -199,31 +200,21 @@ var options = {
}),
new CopyWebpackPlugin({
patterns: [
// {
// from: "node_modules/tlsn-js/build/7.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
// {
// from: "node_modules/tlsn-js/build/250.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
// {
// from: "node_modules/tlsn-js/build/278.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
// {
// from: "node_modules/tlsn-js/build/349.js",
// to: path.join(__dirname, "build"),
// force: true,
// },
{
from: "node_modules/tlsn-js/build",
to: path.join(__dirname, "build"),
force: true,
},
{
from: "src/assets/plugins/discord_dm.wasm",
to: path.join(__dirname, "build"),
force: true,
},
{
from: "src/assets/plugins/twitter_profile.wasm",
to: path.join(__dirname, "build"),
force: true,
},
],
}),
new HtmlWebpackPlugin({