mirror of
https://github.com/stonith404/pingvin-share.git
synced 2024-11-05 07:20:13 +01:00
feat: add ClamAV to scan for malicious files
This commit is contained in:
parent
16b697053a
commit
76088cc76a
@ -30,7 +30,7 @@ RUN npm run build && npm prune --production
|
||||
|
||||
# Stage 5: Final image
|
||||
FROM node:18-slim AS runner
|
||||
ENV NODE_ENV=production
|
||||
ENV NODE_ENV=docker
|
||||
RUN apt-get update && apt-get install -y openssl
|
||||
|
||||
WORKDIR /opt/app/frontend
|
||||
|
15
README.md
15
README.md
@ -4,13 +4,12 @@ Pingvin Share is self-hosted file sharing platform and an alternative for WeTran
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- Spin up your instance within 2 minutes
|
||||
- Create a share with files that you can access with a link
|
||||
- No file size limit, only your disk will be your limit
|
||||
- Set a share expiration
|
||||
- Optionally secure your share with a visitor limit and a password
|
||||
- Email recepients
|
||||
- Light & dark mode
|
||||
- ClamAV integration
|
||||
|
||||
## 🐧 Get to know Pingvin Share
|
||||
|
||||
@ -30,6 +29,18 @@ Pingvin Share is self-hosted file sharing platform and an alternative for WeTran
|
||||
|
||||
The website is now listening available on `http://localhost:3000`, have fun with Pingvin Share 🐧!
|
||||
|
||||
### Integrations
|
||||
|
||||
#### ClamAV
|
||||
|
||||
With ClamAV the shares get scanned for malicious files and get removed if any found.
|
||||
|
||||
1. Add the ClamAV container to the Docker Compose stack (see `docker-compose.yml`) and start the container.
|
||||
2. As soon as the ClamAV container is ready (when ClamAV logs "socket found, clamd started"), restart the Pingvin Share container with `docker compose restart pingvin-share`
|
||||
3. The Pingvin Share logs should now log "ClamAV is active"
|
||||
|
||||
Please note that ClamAV needs a lot of [ressources](https://docs.clamav.net/manual/Installing/Docker.html#memory-ram-requirements).
|
||||
|
||||
### Additional resources
|
||||
|
||||
- [Synology NAS installation](https://mariushosting.com/how-to-install-pingvin-share-on-your-synology-nas/)
|
||||
|
252
backend/package-lock.json
generated
252
backend/package-lock.json
generated
@ -11,7 +11,7 @@
|
||||
"@nestjs/common": "^9.2.1",
|
||||
"@nestjs/config": "^2.2.0",
|
||||
"@nestjs/core": "^9.2.1",
|
||||
"@nestjs/jwt": "^9.0.0",
|
||||
"@nestjs/jwt": "^10.0.1",
|
||||
"@nestjs/mapped-types": "^1.2.0",
|
||||
"@nestjs/passport": "^9.0.0",
|
||||
"@nestjs/platform-express": "^9.2.1",
|
||||
@ -21,6 +21,7 @@
|
||||
"archiver": "^5.3.1",
|
||||
"argon2": "^0.30.2",
|
||||
"body-parser": "^1.20.1",
|
||||
"clamscan": "^2.1.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.13.2",
|
||||
"content-disposition": "^0.5.4",
|
||||
@ -43,6 +44,7 @@
|
||||
"@nestjs/schematics": "^9.0.3",
|
||||
"@nestjs/testing": "^9.2.1",
|
||||
"@types/archiver": "^5.3.1",
|
||||
"@types/clamscan": "^2.0.4",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/cron": "^2.0.0",
|
||||
"@types/express": "^4.17.14",
|
||||
@ -674,12 +676,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/jwt": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-9.0.0.tgz",
|
||||
"integrity": "sha512-ZsXGY/wMYKzEhymw2+dxiwrHTRKIKrGszx6r2EjQqNLypdXMQu0QrujwZJ8k3+XQV4snmuJwwNakQoA2ILfq8w==",
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.0.1.tgz",
|
||||
"integrity": "sha512-LwXBKVYHnFeX6GH/Wt0WDjsWCmNDC6tEdLlwNMAvJgYp+TkiCpEmQLkgRpifdUE29mvYSbjSnVs2kW2ob935NA==",
|
||||
"dependencies": {
|
||||
"@types/jsonwebtoken": "8.5.8",
|
||||
"jsonwebtoken": "8.5.1"
|
||||
"@types/jsonwebtoken": "8.5.9",
|
||||
"jsonwebtoken": "9.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^8.0.0 || ^9.0.0"
|
||||
@ -1143,6 +1145,25 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/clamscan": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/clamscan/-/clamscan-2.0.4.tgz",
|
||||
"integrity": "sha512-NpD+EmE+ZK5WRJOAmeDuSYJIv15BUnc4PxQA+m3QNkutaPBZ7bmLDTvqBu2iDchs7YKQjiEQEwEMvsdwtdtImA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"axios": "^0.24.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/clamscan/node_modules/axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
@ -1243,9 +1264,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/jsonwebtoken": {
|
||||
"version": "8.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz",
|
||||
"integrity": "sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==",
|
||||
"version": "8.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz",
|
||||
"integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@ -2416,6 +2437,14 @@
|
||||
"node": ">=6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/clamscan": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clamscan/-/clamscan-2.1.2.tgz",
|
||||
"integrity": "sha512-pcovgLHcrg3l/mI51Kuk0kN++07pSZdBTskISw0UFvsm8UXda8oNCm0eLeODxFg85Mz+k+TtSS9+XPlriJ8/Fg==",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/class-transformer": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
|
||||
@ -4467,9 +4496,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"json5": "lib/cli.js"
|
||||
@ -4497,32 +4526,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
|
||||
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
|
||||
"integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
|
||||
"dependencies": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^5.6.0"
|
||||
"semver": "^7.3.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4",
|
||||
"npm": ">=1.4.28"
|
||||
}
|
||||
},
|
||||
"node_modules/jsonwebtoken/node_modules/semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
|
||||
"bin": {
|
||||
"semver": "bin/semver"
|
||||
"node": ">=12",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/jsprim": {
|
||||
@ -4647,47 +4662,17 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
|
||||
},
|
||||
"node_modules/lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
||||
},
|
||||
"node_modules/lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"node_modules/lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
||||
},
|
||||
"node_modules/lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
||||
},
|
||||
"node_modules/lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"node_modules/lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"node_modules/lodash.union": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
@ -4737,9 +4722,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz",
|
||||
"integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ==",
|
||||
"version": "1.28.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz",
|
||||
"integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw==",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
@ -5464,11 +5449,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/passport-jwt": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
|
||||
"integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz",
|
||||
"integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==",
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^8.2.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
@ -6307,9 +6292,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
@ -8056,12 +8041,12 @@
|
||||
}
|
||||
},
|
||||
"@nestjs/jwt": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-9.0.0.tgz",
|
||||
"integrity": "sha512-ZsXGY/wMYKzEhymw2+dxiwrHTRKIKrGszx6r2EjQqNLypdXMQu0QrujwZJ8k3+XQV4snmuJwwNakQoA2ILfq8w==",
|
||||
"version": "10.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.0.1.tgz",
|
||||
"integrity": "sha512-LwXBKVYHnFeX6GH/Wt0WDjsWCmNDC6tEdLlwNMAvJgYp+TkiCpEmQLkgRpifdUE29mvYSbjSnVs2kW2ob935NA==",
|
||||
"requires": {
|
||||
"@types/jsonwebtoken": "8.5.8",
|
||||
"jsonwebtoken": "8.5.1"
|
||||
"@types/jsonwebtoken": "8.5.9",
|
||||
"jsonwebtoken": "9.0.0"
|
||||
}
|
||||
},
|
||||
"@nestjs/mapped-types": {
|
||||
@ -8408,6 +8393,27 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/clamscan": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/clamscan/-/clamscan-2.0.4.tgz",
|
||||
"integrity": "sha512-NpD+EmE+ZK5WRJOAmeDuSYJIv15BUnc4PxQA+m3QNkutaPBZ7bmLDTvqBu2iDchs7YKQjiEQEwEMvsdwtdtImA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"axios": "^0.24.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": {
|
||||
"version": "0.24.0",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
|
||||
"integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"follow-redirects": "^1.14.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz",
|
||||
@ -8508,9 +8514,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/jsonwebtoken": {
|
||||
"version": "8.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.8.tgz",
|
||||
"integrity": "sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==",
|
||||
"version": "8.5.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-8.5.9.tgz",
|
||||
"integrity": "sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
@ -9408,6 +9414,11 @@
|
||||
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
|
||||
"dev": true
|
||||
},
|
||||
"clamscan": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/clamscan/-/clamscan-2.1.2.tgz",
|
||||
"integrity": "sha512-pcovgLHcrg3l/mI51Kuk0kN++07pSZdBTskISw0UFvsm8UXda8oNCm0eLeODxFg85Mz+k+TtSS9+XPlriJ8/Fg=="
|
||||
},
|
||||
"class-transformer": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz",
|
||||
@ -10973,9 +10984,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz",
|
||||
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
|
||||
"dev": true
|
||||
},
|
||||
"jsonc-parser": {
|
||||
@ -10995,27 +11006,14 @@
|
||||
}
|
||||
},
|
||||
"jsonwebtoken": {
|
||||
"version": "8.5.1",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz",
|
||||
"integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==",
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz",
|
||||
"integrity": "sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==",
|
||||
"requires": {
|
||||
"jws": "^3.2.2",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"ms": "^2.1.1",
|
||||
"semver": "^5.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
}
|
||||
"semver": "^7.3.8"
|
||||
}
|
||||
},
|
||||
"jsprim": {
|
||||
@ -11119,47 +11117,17 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
|
||||
"integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g=="
|
||||
},
|
||||
"lodash.includes": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
|
||||
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
|
||||
},
|
||||
"lodash.isboolean": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
|
||||
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
|
||||
},
|
||||
"lodash.isinteger": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
|
||||
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
|
||||
},
|
||||
"lodash.isnumber": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
|
||||
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
|
||||
},
|
||||
"lodash.isstring": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
|
||||
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.once": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
|
||||
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
|
||||
},
|
||||
"lodash.union": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
|
||||
@ -11196,9 +11164,9 @@
|
||||
}
|
||||
},
|
||||
"luxon": {
|
||||
"version": "1.28.0",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.0.tgz",
|
||||
"integrity": "sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ=="
|
||||
"version": "1.28.1",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-1.28.1.tgz",
|
||||
"integrity": "sha512-gYHAa180mKrNIUJCbwpmD0aTu9kV0dREDrwNnuyFAsO1Wt0EVYSZelPnJlbj9HplzXX/YWXHFTL45kvZ53M0pw=="
|
||||
},
|
||||
"macos-release": {
|
||||
"version": "2.5.0",
|
||||
@ -11729,11 +11697,11 @@
|
||||
}
|
||||
},
|
||||
"passport-jwt": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
|
||||
"integrity": "sha512-BwC0n2GP/1hMVjR4QpnvqA61TxenUMlmfNjYNgK0ZAs0HK4SOQkHcSv4L328blNTLtHq7DbmvyNJiH+bn6C5Mg==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz",
|
||||
"integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==",
|
||||
"requires": {
|
||||
"jsonwebtoken": "^8.2.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
"passport-strategy": "^1.0.0"
|
||||
}
|
||||
},
|
||||
@ -12344,9 +12312,9 @@
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.7",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
|
||||
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
|
||||
"version": "7.3.8",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
||||
"integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
"@nestjs/common": "^9.2.1",
|
||||
"@nestjs/config": "^2.2.0",
|
||||
"@nestjs/core": "^9.2.1",
|
||||
"@nestjs/jwt": "^9.0.0",
|
||||
"@nestjs/jwt": "^10.0.1",
|
||||
"@nestjs/mapped-types": "^1.2.0",
|
||||
"@nestjs/passport": "^9.0.0",
|
||||
"@nestjs/platform-express": "^9.2.1",
|
||||
@ -26,6 +26,7 @@
|
||||
"archiver": "^5.3.1",
|
||||
"argon2": "^0.30.2",
|
||||
"body-parser": "^1.20.1",
|
||||
"clamscan": "^2.1.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.13.2",
|
||||
"content-disposition": "^0.5.4",
|
||||
@ -48,6 +49,7 @@
|
||||
"@nestjs/schematics": "^9.0.3",
|
||||
"@nestjs/testing": "^9.2.1",
|
||||
"@types/archiver": "^5.3.1",
|
||||
"@types/clamscan": "^2.0.4",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/cron": "^2.0.0",
|
||||
"@types/express": "^4.17.14",
|
||||
|
@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Share" ADD COLUMN "removedReason" TEXT;
|
@ -57,6 +57,7 @@ model Share {
|
||||
views Int @default(0)
|
||||
expiration DateTime
|
||||
description String?
|
||||
removedReason String?
|
||||
|
||||
creatorId String?
|
||||
creator User? @relation(fields: [creatorId], references: [id], onDelete: Cascade)
|
||||
|
@ -12,6 +12,7 @@ import { JobsModule } from "./jobs/jobs.module";
|
||||
import { PrismaModule } from "./prisma/prisma.module";
|
||||
import { ShareModule } from "./share/share.module";
|
||||
import { UserModule } from "./user/user.module";
|
||||
import { ClamscanModule } from "./clamscan/clamscan.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -28,6 +29,7 @@ import { UserModule } from "./user/user.module";
|
||||
limit: 100,
|
||||
}),
|
||||
ScheduleModule.forRoot(),
|
||||
ClamscanModule,
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
|
10
backend/src/clamscan/clamscan.module.ts
Normal file
10
backend/src/clamscan/clamscan.module.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { forwardRef, Module } from "@nestjs/common";
|
||||
import { FileModule } from "src/file/file.module";
|
||||
import { ClamScanService } from "./clamscan.service";
|
||||
|
||||
@Module({
|
||||
imports: [forwardRef(() => FileModule)],
|
||||
providers: [ClamScanService],
|
||||
exports: [ClamScanService],
|
||||
})
|
||||
export class ClamscanModule {}
|
86
backend/src/clamscan/clamscan.service.ts
Normal file
86
backend/src/clamscan/clamscan.service.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import * as NodeClam from "clamscan";
|
||||
import * as fs from "fs";
|
||||
import { FileService } from "src/file/file.service";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
|
||||
const clamscanConfig = {
|
||||
clamdscan: {
|
||||
host: process.env.NODE_ENV == "docker" ? "clamav" : "127.0.0.1",
|
||||
port: 3310,
|
||||
localFallback: false,
|
||||
},
|
||||
preference: "clamdscan",
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class ClamScanService {
|
||||
constructor(
|
||||
private fileService: FileService,
|
||||
private prisma: PrismaService
|
||||
) {}
|
||||
|
||||
private ClamScan: Promise<NodeClam | null> = new NodeClam()
|
||||
.init(clamscanConfig)
|
||||
.then((res) => {
|
||||
console.log("ClamAV is active");
|
||||
return res;
|
||||
})
|
||||
.catch(() => {
|
||||
console.log("ClamAV is not active");
|
||||
return null;
|
||||
});
|
||||
|
||||
async check(shareId: string) {
|
||||
const clamScan = await this.ClamScan;
|
||||
|
||||
if (!clamScan) return [];
|
||||
|
||||
const infectedFiles = [];
|
||||
|
||||
const files = fs
|
||||
.readdirSync(`./data/uploads/shares/${shareId}`)
|
||||
.filter((file) => file != "archive.zip");
|
||||
|
||||
for (const fileId of files) {
|
||||
const { isInfected } = await clamScan
|
||||
.isInfected(`./data/uploads/shares/${shareId}/${fileId}`)
|
||||
.catch(() => {
|
||||
console.log("ClamAV is not active");
|
||||
return { isInfected: false };
|
||||
});
|
||||
|
||||
const fileName = (
|
||||
await this.prisma.file.findUnique({ where: { id: fileId } })
|
||||
).name;
|
||||
|
||||
if (isInfected) {
|
||||
infectedFiles.push({ id: fileId, name: fileName });
|
||||
}
|
||||
}
|
||||
|
||||
return infectedFiles;
|
||||
}
|
||||
|
||||
async checkAndRemove(shareId: string) {
|
||||
const infectedFiles = await this.check(shareId);
|
||||
|
||||
if (infectedFiles.length > 0) {
|
||||
await this.fileService.deleteAllFiles(shareId);
|
||||
await this.prisma.file.deleteMany({ where: { shareId } });
|
||||
|
||||
const fileNames = infectedFiles.map((file) => file.name).join(", ");
|
||||
|
||||
await this.prisma.share.update({
|
||||
where: { id: shareId },
|
||||
data: {
|
||||
removedReason: `Your share got removed because the file(s) ${fileNames} are malicious.`,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(
|
||||
`Share ${shareId} deleted because it contained ${infectedFiles.length} malicious file(s)`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ async function bootstrap() {
|
||||
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
|
||||
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
|
||||
|
||||
app.use(bodyParser.raw({type:'application/octet-stream', limit:'20mb'}));
|
||||
app.use(bodyParser.raw({ type: "application/octet-stream", limit: "20mb" }));
|
||||
app.use(cookieParser());
|
||||
app.set("trust proxy", true);
|
||||
|
||||
|
@ -1,12 +1,18 @@
|
||||
import { forwardRef, Module } from "@nestjs/common";
|
||||
import { JwtModule } from "@nestjs/jwt";
|
||||
import { ClamscanModule } from "src/clamscan/clamscan.module";
|
||||
import { EmailModule } from "src/email/email.module";
|
||||
import { FileModule } from "src/file/file.module";
|
||||
import { ShareController } from "./share.controller";
|
||||
import { ShareService } from "./share.service";
|
||||
|
||||
@Module({
|
||||
imports: [JwtModule.register({}), EmailModule, forwardRef(() => FileModule)],
|
||||
imports: [
|
||||
JwtModule.register({}),
|
||||
EmailModule,
|
||||
ClamscanModule,
|
||||
forwardRef(() => FileModule),
|
||||
],
|
||||
controllers: [ShareController],
|
||||
providers: [ShareService],
|
||||
exports: [ShareService],
|
||||
|
@ -10,6 +10,7 @@ import * as archiver from "archiver";
|
||||
import * as argon from "argon2";
|
||||
import * as fs from "fs";
|
||||
import * as moment from "moment";
|
||||
import { ClamScanService } from "src/clamscan/clamscan.service";
|
||||
import { ConfigService } from "src/config/config.service";
|
||||
import { EmailService } from "src/email/email.service";
|
||||
import { FileService } from "src/file/file.service";
|
||||
@ -23,7 +24,8 @@ export class ShareService {
|
||||
private fileService: FileService,
|
||||
private emailService: EmailService,
|
||||
private config: ConfigService,
|
||||
private jwtService: JwtService
|
||||
private jwtService: JwtService,
|
||||
private clasmScanService: ClamScanService
|
||||
) {}
|
||||
|
||||
async create(share: CreateShareDTO, user?: User) {
|
||||
@ -123,6 +125,9 @@ export class ShareService {
|
||||
);
|
||||
}
|
||||
|
||||
// Check if any file is malicious with ClamAV
|
||||
this.clasmScanService.checkAndRemove(share.id);
|
||||
|
||||
return await this.prisma.share.update({
|
||||
where: { id },
|
||||
data: { uploadLocked: true },
|
||||
@ -157,7 +162,7 @@ export class ShareService {
|
||||
}
|
||||
|
||||
async get(id: string) {
|
||||
const share: any = await this.prisma.share.findUnique({
|
||||
const share = await this.prisma.share.findUnique({
|
||||
where: { id },
|
||||
include: {
|
||||
files: true,
|
||||
@ -165,10 +170,13 @@ export class ShareService {
|
||||
},
|
||||
});
|
||||
|
||||
if (share.removedReason)
|
||||
throw new NotFoundException(share.removedReason, "share_removed");
|
||||
|
||||
if (!share || !share.uploadLocked)
|
||||
throw new NotFoundException("Share not found");
|
||||
|
||||
return share;
|
||||
return share as any;
|
||||
}
|
||||
|
||||
async getMetaData(id: string) {
|
||||
|
7
docker-compose-dev.yml
Normal file
7
docker-compose-dev.yml
Normal file
@ -0,0 +1,7 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
clamav:
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 3310:3310
|
||||
image: clamav/clamav
|
@ -6,4 +6,9 @@ services:
|
||||
ports:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- "${PWD}/data:/opt/app/backend/data"
|
||||
- "./data:/opt/app/backend/data"
|
||||
# Optional: Add ClamAV (see README.md)
|
||||
# ClamAV is currently only available for AMD64 see https://github.com/Cisco-Talos/clamav/issues/482
|
||||
# clamav:
|
||||
# restart: unless-stopped
|
||||
# image: clamav/clamav
|
@ -1,7 +1,11 @@
|
||||
import { Stack, TextInput } from "@mantine/core";
|
||||
import { ModalsContextProps } from "@mantine/modals/lib/context";
|
||||
|
||||
const showShareLinkModal = (modals: ModalsContextProps, shareId: string, appUrl : string) => {
|
||||
const showShareLinkModal = (
|
||||
modals: ModalsContextProps,
|
||||
shareId: string,
|
||||
appUrl: string
|
||||
) => {
|
||||
const link = `${appUrl}/share/${shareId}`;
|
||||
return modals.openModal({
|
||||
title: "Share link",
|
||||
|
@ -53,7 +53,10 @@ const Dropzone = ({
|
||||
disabled={isUploading}
|
||||
openRef={openRef as ForwardedRef<() => void>}
|
||||
onDrop={(newFiles: FileUpload[]) => {
|
||||
const fileSizeSum = [...newFiles, ...files].reduce((n, { size }) => n + size, 0);
|
||||
const fileSizeSum = [...newFiles, ...files].reduce(
|
||||
(n, { size }) => n + size,
|
||||
0
|
||||
);
|
||||
|
||||
if (fileSizeSum > config.get("MAX_SHARE_SIZE")) {
|
||||
toast.error(
|
||||
|
@ -47,21 +47,19 @@ const Share = ({ shareId }: { shareId: string }) => {
|
||||
.catch((e) => {
|
||||
const { error } = e.response.data;
|
||||
if (e.response.status == 404) {
|
||||
if (error == "share_removed") {
|
||||
showErrorModal(modals, "Share removed", e.response.data.message);
|
||||
} else {
|
||||
showErrorModal(
|
||||
modals,
|
||||
"Not found",
|
||||
"This share can't be found. Please check your link."
|
||||
);
|
||||
}
|
||||
} else if (error == "share_password_required") {
|
||||
showEnterPasswordModal(modals, getShareToken);
|
||||
} else if (error == "share_token_required") {
|
||||
getShareToken();
|
||||
} else if (error == "forbidden") {
|
||||
showErrorModal(
|
||||
modals,
|
||||
"Forbidden",
|
||||
"You're not allowed to see this share. Are you logged in with the correct account?"
|
||||
);
|
||||
} else {
|
||||
showErrorModal(modals, "Error", "An unknown error occurred.");
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
export type FileUpload = File & { uploadingProgress: number };
|
||||
|
||||
export type FileUploadResponse = {id: string, name: string}
|
||||
export type FileUploadResponse = { id: string; name: string };
|
||||
|
Loading…
Reference in New Issue
Block a user