├── src ├── frontend-nextjs │ ├── .npmrc │ ├── constants.ts │ ├── enums │ │ ├── roles.ts │ │ ├── memberships.ts │ │ ├── routes.ts │ │ └── queryKeys.ts │ ├── app │ │ ├── icon1.png │ │ ├── icon2.png │ │ ├── favicon.ico │ │ ├── apple-icon1.png │ │ ├── apple-icon10.png │ │ ├── apple-icon2.png │ │ ├── apple-icon3.png │ │ ├── apple-icon4.png │ │ ├── apple-icon5.png │ │ ├── apple-icon6.png │ │ ├── apple-icon7.png │ │ ├── apple-icon8.png │ │ ├── apple-icon9.png │ │ ├── api-doc │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── users │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── login │ │ │ └── layout.tsx │ │ ├── post │ │ │ └── layout.tsx │ │ ├── ad-manager │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── mytimeline │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── payment │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── user │ │ │ └── [username] │ │ │ │ ├── layout.tsx │ │ │ │ └── page.tsx │ │ ├── membership-plans │ │ │ ├── layout.tsx │ │ │ └── page.tsx │ │ ├── api │ │ │ ├── healthz │ │ │ │ └── route.ts │ │ │ ├── posts │ │ │ │ ├── route.ts │ │ │ │ ├── mytimeline │ │ │ │ │ └── route.ts │ │ │ │ └── [username] │ │ │ │ │ └── route.ts │ │ │ ├── roles │ │ │ │ └── route.ts │ │ │ ├── ads │ │ │ │ └── route.ts │ │ │ ├── auth │ │ │ │ ├── logout │ │ │ │ │ └── route.ts │ │ │ │ ├── ad-manager │ │ │ │ │ └── route.ts │ │ │ │ ├── register │ │ │ │ │ └── route.ts │ │ │ │ ├── jwt-payload │ │ │ │ │ └── route.ts │ │ │ │ └── login │ │ │ │ │ └── route.ts │ │ │ ├── deployment-health │ │ │ │ └── route.ts │ │ │ ├── like │ │ │ │ ├── route.ts │ │ │ │ └── [postId] │ │ │ │ │ └── route.ts │ │ │ ├── post │ │ │ │ ├── [postId] │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ ├── likes │ │ │ │ └── [postId] │ │ │ │ │ └── route.ts │ │ │ ├── ad │ │ │ │ ├── [adName] │ │ │ │ │ └── route.ts │ │ │ │ └── route.ts │ │ │ ├── followers │ │ │ │ └── [username] │ │ │ │ │ └── route.ts │ │ │ └── users │ │ │ │ └── route.ts │ │ ├── page.tsx │ │ ├── error.tsx │ │ └── providers.tsx │ ├── public │ │ ├── mstile-70x70.png │ │ ├── mstile-144x144.png │ │ ├── mstile-150x150.png │ │ ├── mstile-310x150.png │ │ ├── mstile-310x310.png │ │ ├── manifest.json │ │ ├── browserconfig.xml │ │ ├── safari-pinned-tab.svg │ │ ├── unguard_logo.svg │ │ └── unguard_logo_black.svg │ ├── postcss.config.js │ ├── .dockerignore │ ├── .prettierignore │ ├── components │ │ ├── UserProfile │ │ │ ├── BlueCheckmarkIcon.tsx │ │ │ ├── FollowerList.tsx │ │ │ └── UserTimeline.tsx │ │ ├── ErrorCard.tsx │ │ ├── Footer │ │ │ └── Footer.tsx │ │ ├── GlobalTimeline.tsx │ │ ├── SwaggerUIReact.tsx │ │ ├── AdManager │ │ │ └── AdList.tsx │ │ └── Timeline │ │ │ ├── LikeButton.tsx │ │ │ └── Timeline.tsx │ ├── next.config.js │ ├── .eslintignore │ ├── swagger-ui-react.d.ts │ ├── .env │ ├── services │ │ ├── BioService.ts │ │ ├── MembershipService.ts │ │ ├── AdService.ts │ │ ├── FollowService.ts │ │ ├── PostService.ts │ │ ├── LikeService.ts │ │ ├── api │ │ │ ├── MembershipService.ts │ │ │ └── DeploymentHealthService.ts │ │ └── PaymentService.ts │ ├── .prettierrc │ ├── styles │ │ └── globals.css │ ├── .gitignore │ ├── hooks │ │ ├── queries │ │ │ ├── useAd.ts │ │ │ ├── useAdVisibility.ts │ │ │ ├── useBio.ts │ │ │ ├── useRoles.ts │ │ │ ├── useAdList.ts │ │ │ ├── useCheckLogin.ts │ │ │ ├── useJwtPayload.ts │ │ │ ├── useMembership.ts │ │ │ ├── usePost.ts │ │ │ ├── useCheckAdmanager.ts │ │ │ ├── useFollowingStatus.ts │ │ │ ├── useFollowersList.ts │ │ │ ├── useUserList.ts │ │ │ ├── useLikes.ts │ │ │ └── usePaymentInfo.ts │ │ ├── mutations │ │ │ ├── useDeleteAd.ts │ │ │ ├── useUploadAd.ts │ │ │ ├── useUpdateMembership.ts │ │ │ ├── useUpdatePaymentInfo.ts │ │ │ └── useLikeChange.ts │ │ └── useNavigation.ts │ ├── tailwind.config.js │ ├── lib │ │ └── swagger.ts │ ├── tsconfig.json │ ├── config │ │ └── site.ts │ └── middleware.ts ├── malicious-load-generator │ ├── .dockerignore │ ├── requirements.in │ ├── Dockerfile │ └── unguard.malicious-load-generator.iml ├── user-auth-service │ ├── .dockerignore │ ├── unguard.user-auth-service.iml │ ├── Dockerfile │ ├── package.json │ ├── keys │ │ └── jwtRS256.key.pub │ └── routes │ │ ├── index.js │ │ └── jwt.js ├── like-service │ ├── storage │ │ ├── logs │ │ │ └── .gitignore │ │ ├── app │ │ │ ├── public │ │ │ │ └── .gitignore │ │ │ └── .gitignore │ │ └── framework │ │ │ ├── views │ │ │ └── .gitignore │ │ │ ├── cache │ │ │ ├── data │ │ │ │ └── .gitignore │ │ │ └── .gitignore │ │ │ ├── sessions │ │ │ └── .gitignore │ │ │ ├── testing │ │ │ └── .gitignore │ │ │ └── .gitignore │ ├── bootstrap │ │ └── cache │ │ │ └── .gitignore │ ├── public │ │ ├── robots.txt │ │ ├── .htaccess │ │ └── web.config │ ├── database │ │ └── .gitignore │ ├── .gitattributes │ ├── .styleci.yml │ ├── .gitignore │ ├── .editorconfig │ ├── tests │ │ ├── TestCase.php │ │ └── CreatesApplication.php │ ├── app │ │ ├── Http │ │ │ └── Middleware │ │ │ │ ├── EncryptCookies.php │ │ │ │ ├── TrimStrings.php │ │ │ │ ├── TrustHosts.php │ │ │ │ └── TrustProxies.php │ │ └── Providers │ │ │ ├── AppServiceProvider.php │ │ │ └── RouteServiceProvider.php │ ├── .env │ ├── Dockerfile │ ├── phpunit.xml │ ├── resources │ │ └── lang │ │ │ └── en │ │ │ ├── pagination.php │ │ │ └── auth.php │ └── server.php ├── payment-service │ ├── .dockerignore │ ├── unguard.payment-service.iml │ ├── Dockerfile │ ├── payment_service │ │ ├── logger_config.py │ │ └── run.py │ └── pyproject.toml ├── user-simulator │ ├── .dockerignore │ ├── data │ │ ├── imgposts.json │ │ ├── textposts.json │ │ └── biolist.json │ ├── Dockerfile │ ├── unguard.user-simulator.iml │ ├── tsconfig.json │ ├── package.json │ └── types.ts ├── proxy-service │ ├── settings.gradle │ ├── .env │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── .mvn │ │ └── wrapper │ │ │ └── maven-wrapper.properties │ ├── src │ │ └── main │ │ │ ├── resources │ │ │ └── application.properties │ │ │ └── java │ │ │ └── org │ │ │ └── dynatrace │ │ │ └── ssrfservice │ │ │ ├── Application.java │ │ │ └── configuration │ │ │ └── NoopTracerConfiguration.java │ └── Dockerfile ├── microblog-service │ ├── settings.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── .mvn │ │ └── wrapper │ │ │ └── maven-wrapper.properties │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── application.properties │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── dynatrace │ │ │ │ └── microblog │ │ │ │ ├── utils │ │ │ │ └── JwtTokensUtils.java │ │ │ │ ├── dto │ │ │ │ ├── PostId.java │ │ │ │ └── User.java │ │ │ │ ├── form │ │ │ │ └── PostForm.java │ │ │ │ ├── exceptions │ │ │ │ ├── InvalidJwtException.java │ │ │ │ ├── InvalidUserException.java │ │ │ │ ├── NotLoggedInException.java │ │ │ │ ├── UserNotFoundException.java │ │ │ │ ├── UserAlreadyExistsException.java │ │ │ │ └── FollowYourselfException.java │ │ │ │ ├── Application.java │ │ │ │ └── configuration │ │ │ │ └── NoopTracerConfiguration.java │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── dynatrace │ │ │ └── microblog │ │ │ └── utils │ │ │ └── PostSerializerTest.java │ ├── .env │ └── Dockerfile ├── profile-service │ ├── settings.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── src │ │ ├── main │ │ │ ├── resources │ │ │ │ └── application.properties │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── dynatrace │ │ │ │ └── profileservice │ │ │ │ ├── ProfileServiceApplication.java │ │ │ │ ├── exceptions │ │ │ │ └── BioNotFoundException.java │ │ │ │ ├── model │ │ │ │ └── Bio.java │ │ │ │ └── HealthController.java │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── dynatrace │ │ │ └── profileservice │ │ │ └── ProfileServiceApplicationTests.java │ ├── Dockerfile │ ├── .gitignore │ └── build.gradle ├── ad-service │ ├── global.json │ ├── wwwroot │ │ ├── adFolder │ │ │ ├── kingfisher.jpg │ │ │ └── spring-bird.jpg │ │ ├── js │ │ │ └── site.js │ │ └── css │ │ │ └── style.css │ ├── appsettings.json │ ├── unguard.ad-service.iml │ ├── .dockerignore │ ├── Properties │ │ └── launchSettings.json │ ├── Pages │ │ ├── _ViewStart.cshtml │ │ ├── _ViewImports.cshtml │ │ ├── ads.cshtml │ │ ├── ads │ │ │ ├── delete.cshtml │ │ │ └── upload.cshtml │ │ ├── ad.cshtml │ │ └── Shared │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ └── _Layout.cshtml │ ├── Dockerfile │ ├── AdService.sln │ └── docker-compose.yml ├── membership-service │ ├── appsettings.Development.json │ ├── appsettings.json │ ├── Properties │ │ └── launchSettings.json │ ├── Dockerfile │ └── MembershipService.csproj ├── envoy-proxy │ ├── Dockerfile │ ├── config │ │ └── request-logger.lua │ └── README.md └── status-service │ ├── .gitignore │ ├── unguard.status-service.iml │ ├── Dockerfile │ ├── utils │ └── get-env.go │ └── connections │ └── k8s-client.go ├── .github ├── CODEOWNERS ├── workflows │ └── commitlint.yaml └── ISSUE_TEMPLATE │ └── feature_request.md ├── exploit-toolkit ├── .env ├── exploits │ ├── crlf-injection │ │ └── payload │ │ │ └── hackedflag.txt │ ├── xss │ │ └── images │ │ │ ├── xss_update_bio.png │ │ │ ├── xss_click_button.png │ │ │ └── xss_update_bio_result.png │ ├── zip-slip │ │ ├── images │ │ │ └── upload-ad-zip.png │ │ └── payloads │ │ │ ├── exploit-ads.zip │ │ │ └── original-ads.zip │ ├── ssrf │ │ └── images │ │ │ ├── ssrf-set-redis-key.png │ │ │ └── ssrf-set-redis-key-result.png │ ├── log4shell │ │ └── images │ │ │ ├── log4shell-demo-results.png │ │ │ └── unguard-share-url-with-jndi-lookup.png │ ├── cmd-injection │ │ ├── images │ │ │ ├── CMDI_markdown_post_bio.png │ │ │ └── CMDI_markdown_post_bio_result.png │ │ └── README.md │ ├── sql-injection │ │ ├── images │ │ │ ├── SQL_profile_service_update_bio.png │ │ │ └── SQL_profile_service_update_bio_result.png │ │ └── README.md │ └── TEMPLATE.md ├── requirements.txt ├── Dockerfile ├── exploit-toolkit.iml ├── INSTALL.md └── setup.py ├── .gitallowed ├── docs ├── images │ ├── jaeger-ui.png │ ├── unguard-timeline.png │ ├── unguard-architecture.fig │ ├── unguard-user-profile.png │ └── logo │ │ ├── unguard-logo-red-small.png │ │ ├── unguard-logo-fill-black.svg │ │ ├── unguard-logo-fill-white.svg │ │ ├── unguard-logo-fill-red.svg │ │ ├── unguard-logo-fill-blue.svg │ │ ├── unguard-logo-black.svg │ │ └── unguard-logo-white.svg └── jaeger │ └── jaeger-otlp-values.yaml ├── chart ├── templates │ ├── NOTES.txt │ └── tests │ │ └── test-frontend-connection.yaml ├── jaeger.yaml ├── aws.yaml ├── Chart.yaml ├── .helmignore └── tracing.yaml ├── .husky └── commit-msg ├── commitlint.config.js ├── .idea ├── vcs.xml ├── .gitignore ├── unguard.iml ├── misc.xml └── jarRepositories.xml ├── package.json ├── .editorconfig ├── CITATION.cff ├── .gitattributes ├── monaco └── management-zone │ └── management-zone.yaml └── k8s-manifests ├── jaeger └── jaeger.yaml └── localdev └── kind └── cluster-config.yaml /src/frontend-nextjs/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=true -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @dynatrace-oss/unguard-oss -------------------------------------------------------------------------------- /src/malicious-load-generator/.dockerignore: -------------------------------------------------------------------------------- 1 | __pycache__ -------------------------------------------------------------------------------- /src/user-auth-service/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /src/malicious-load-generator/requirements.in: -------------------------------------------------------------------------------- 1 | locust==2.9.0 -------------------------------------------------------------------------------- /src/like-service/storage/logs/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/payment-service/.dockerignore: -------------------------------------------------------------------------------- 1 | *.db 2 | *.iml 3 | *.md 4 | -------------------------------------------------------------------------------- /src/frontend-nextjs/constants.ts: -------------------------------------------------------------------------------- 1 | export const BASE_PATH = '/ui'; 2 | -------------------------------------------------------------------------------- /src/like-service/bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/like-service/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/like-service/storage/app/public/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/like-service/database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite 2 | *.sqlite-journal 3 | -------------------------------------------------------------------------------- /src/like-service/storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/user-simulator/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | README.md 3 | *.iml 4 | -------------------------------------------------------------------------------- /src/like-service/storage/app/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !public/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /src/like-service/storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/like-service/storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /src/like-service/storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /exploit-toolkit/.env: -------------------------------------------------------------------------------- 1 | FRONTEND_BASE_PATH=/ui 2 | AD_SERVICE_BASE_PATH=/ad-service -------------------------------------------------------------------------------- /src/proxy-service/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'unguard.proxy-service' 2 | -------------------------------------------------------------------------------- /.gitallowed: -------------------------------------------------------------------------------- 1 | # Allow RSA keys in user-auth-service/keys 2 | src/user-auth-service/keys/* -------------------------------------------------------------------------------- /src/like-service/storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /src/microblog-service/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'unguard.microblog-service' 2 | -------------------------------------------------------------------------------- /src/profile-service/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'unguard.profile-service' 2 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/crlf-injection/payload/hackedflag.txt: -------------------------------------------------------------------------------- 1 | SET hacked exploitworked 2 | -------------------------------------------------------------------------------- /src/frontend-nextjs/enums/roles.ts: -------------------------------------------------------------------------------- 1 | export enum ROLE { 2 | AD_MANAGER = 'AD_MANAGER', 3 | } 4 | -------------------------------------------------------------------------------- /docs/images/jaeger-ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/docs/images/jaeger-ui.png -------------------------------------------------------------------------------- /src/proxy-service/.env: -------------------------------------------------------------------------------- 1 | SERVER_PORT=8081 2 | JAEGER_SERVICE_NAME=proxy-service 3 | JAEGER_AGENT_HOST=localhost 4 | -------------------------------------------------------------------------------- /chart/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | This is the helm chart for Unguard, an insecure cloud-native microservices demo application. 2 | -------------------------------------------------------------------------------- /docs/images/unguard-timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/docs/images/unguard-timeline.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/icon1.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/icon2.png -------------------------------------------------------------------------------- /src/frontend-nextjs/enums/memberships.ts: -------------------------------------------------------------------------------- 1 | export enum MEMBERSHIP { 2 | FREE = 'FREE', 3 | PRO = 'PRO', 4 | } 5 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/favicon.ico -------------------------------------------------------------------------------- /docs/images/unguard-architecture.fig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/docs/images/unguard-architecture.fig -------------------------------------------------------------------------------- /docs/images/unguard-user-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/docs/images/unguard-user-profile.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon1.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon10.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon2.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon3.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon4.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon5.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon6.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon7.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon8.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/apple-icon9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/app/apple-icon9.png -------------------------------------------------------------------------------- /docs/images/logo/unguard-logo-red-small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/docs/images/logo/unguard-logo-red-small.png -------------------------------------------------------------------------------- /src/frontend-nextjs/public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/public/mstile-70x70.png -------------------------------------------------------------------------------- /src/frontend-nextjs/public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/public/mstile-144x144.png -------------------------------------------------------------------------------- /src/frontend-nextjs/public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/public/mstile-150x150.png -------------------------------------------------------------------------------- /src/frontend-nextjs/public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/public/mstile-310x150.png -------------------------------------------------------------------------------- /src/frontend-nextjs/public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/frontend-nextjs/public/mstile-310x310.png -------------------------------------------------------------------------------- /src/ad-service/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "5.0", 4 | "rollForward": "latestMajor", 5 | "allowPrerelease": true 6 | } 7 | } -------------------------------------------------------------------------------- /src/ad-service/wwwroot/adFolder/kingfisher.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/ad-service/wwwroot/adFolder/kingfisher.jpg -------------------------------------------------------------------------------- /src/ad-service/wwwroot/adFolder/spring-bird.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/ad-service/wwwroot/adFolder/spring-bird.jpg -------------------------------------------------------------------------------- /src/frontend-nextjs/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /src/like-service/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.css linguist-vendored 3 | *.scss linguist-vendored 4 | *.js linguist-vendored 5 | CHANGELOG.md export-ignore 6 | -------------------------------------------------------------------------------- /src/profile-service/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/profile-service/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/proxy-service/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/proxy-service/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exploit-toolkit/exploits/xss/images/xss_update_bio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/xss/images/xss_update_bio.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api-doc/layout.tsx: -------------------------------------------------------------------------------- 1 | export default function ApiDocLayout({ children }: { children: React.ReactNode }) { 2 | return <>{children}; 3 | } 4 | -------------------------------------------------------------------------------- /src/microblog-service/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/src/microblog-service/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exploit-toolkit/exploits/xss/images/xss_click_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/xss/images/xss_click_button.png -------------------------------------------------------------------------------- /exploit-toolkit/exploits/zip-slip/images/upload-ad-zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/zip-slip/images/upload-ad-zip.png -------------------------------------------------------------------------------- /exploit-toolkit/exploits/zip-slip/payloads/exploit-ads.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/zip-slip/payloads/exploit-ads.zip -------------------------------------------------------------------------------- /src/microblog-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip 2 | -------------------------------------------------------------------------------- /src/proxy-service/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | export NVM_DIR="$HOME/.nvm" 3 | [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm 4 | 5 | npx --no-install commitlint --e $1 6 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/ssrf/images/ssrf-set-redis-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/ssrf/images/ssrf-set-redis-key.png -------------------------------------------------------------------------------- /exploit-toolkit/exploits/zip-slip/payloads/original-ads.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/zip-slip/payloads/original-ads.zip -------------------------------------------------------------------------------- /exploit-toolkit/requirements.txt: -------------------------------------------------------------------------------- 1 | click>=7.1 2 | lxml-html-clean>=0.4.2 3 | setuptools>=45 4 | requests>=2.21 5 | requests-html>=0.10 6 | pyjwt>=2.1 7 | python-decouple>=3.4 8 | -------------------------------------------------------------------------------- /src/like-service/.styleci.yml: -------------------------------------------------------------------------------- 1 | php: 2 | preset: laravel 3 | disabled: 4 | - unused_use 5 | finder: 6 | not-name: 7 | - index.php 8 | - server.php 9 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/xss/images/xss_update_bio_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/xss/images/xss_update_bio_result.png -------------------------------------------------------------------------------- /exploit-toolkit/exploits/log4shell/images/log4shell-demo-results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/log4shell/images/log4shell-demo-results.png -------------------------------------------------------------------------------- /exploit-toolkit/exploits/ssrf/images/ssrf-set-redis-key-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/ssrf/images/ssrf-set-redis-key-result.png -------------------------------------------------------------------------------- /src/microblog-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${SERVER_PORT} 2 | opentracing.jaeger.udp-sender.host=${JAEGER_AGENT_HOST} 3 | opentracing.jaeger.log-spans=false 4 | -------------------------------------------------------------------------------- /src/proxy-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${SERVER_PORT} 2 | opentracing.jaeger.udp-sender.host=${JAEGER_AGENT_HOST} 3 | opentracing.jaeger.log-spans=false 4 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['gitmoji'], 3 | rules: { 4 | "subject-case": [2, "always", ["sentence-case", "start-case", "pascal-case"]] 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /src/microblog-service/.env: -------------------------------------------------------------------------------- 1 | SERVER_PORT=8080 2 | REDIS_SERVICE_ADDRESS=localhost 3 | JAEGER_AGENT_HOST=localhost 4 | JAEGER_SERVICE_NAME=microblog-service 5 | USER_AUTH_SERVICE_ADDRESS=localhost:9091 -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/cmd-injection/images/CMDI_markdown_post_bio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/cmd-injection/images/CMDI_markdown_post_bio.png -------------------------------------------------------------------------------- /src/frontend-nextjs/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .dockerignore 3 | Dockerfile 4 | .gitignore 5 | .next 6 | .idea 7 | *.md 8 | *.log 9 | .vscode 10 | .eslintignore 11 | unguard.frontend-nextjs.iml 12 | -------------------------------------------------------------------------------- /src/like-service/storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/cmd-injection/images/CMDI_markdown_post_bio_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/cmd-injection/images/CMDI_markdown_post_bio_result.png -------------------------------------------------------------------------------- /exploit-toolkit/exploits/log4shell/images/unguard-share-url-with-jndi-lookup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/log4shell/images/unguard-share-url-with-jndi-lookup.png -------------------------------------------------------------------------------- /exploit-toolkit/exploits/sql-injection/images/SQL_profile_service_update_bio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/sql-injection/images/SQL_profile_service_update_bio.png -------------------------------------------------------------------------------- /src/frontend-nextjs/app/users/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | 3 | export default function RootLayout({ children }: { children: React.ReactNode }) { 4 | return
{children}
; 5 | } 6 | -------------------------------------------------------------------------------- /src/membership-service/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/sql-injection/images/SQL_profile_service_update_bio_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dynatrace-oss/unguard/HEAD/exploit-toolkit/exploits/sql-injection/images/SQL_profile_service_update_bio_result.png -------------------------------------------------------------------------------- /src/frontend-nextjs/.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .dockerignore 3 | Dockerfile 4 | .gitignore 5 | .next 6 | .idea 7 | *.md 8 | *.log 9 | .vscode 10 | .eslintignore 11 | unguard.frontend-nextjs.iml 12 | package-lock.json 13 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/UserProfile/BlueCheckmarkIcon.tsx: -------------------------------------------------------------------------------- 1 | import { BsCheckCircleFill } from 'react-icons/bs'; 2 | 3 | export function BlueCheckmarkIcon() { 4 | return ; 5 | } 6 | -------------------------------------------------------------------------------- /src/like-service/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /storage/*.key 5 | /vendor 6 | .env.backup 7 | .phpunit.result.cache 8 | Homestead.json 9 | Homestead.yaml 10 | npm-debug.log 11 | yarn-error.log 12 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/login/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | import { PropsWithChildren } from 'react'; 3 | 4 | export default function RootLayout({ children }: PropsWithChildren) { 5 | return
{children}
; 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/post/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | import { PropsWithChildren } from 'react'; 3 | 4 | export default function RootLayout({ children }: PropsWithChildren) { 5 | return
{children}
; 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend-nextjs/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | basePath: '/ui', 4 | output: 'standalone', 5 | transpilePackages: ['react-md-editor'], 6 | }; 7 | 8 | module.exports = nextConfig; 9 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/ad-manager/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | import { PropsWithChildren } from 'react'; 3 | 4 | export default function RootLayout({ children }: PropsWithChildren) { 5 | return
{children}
; 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/mytimeline/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | import { PropsWithChildren } from 'react'; 3 | 4 | export default function RootLayout({ children }: PropsWithChildren) { 5 | return
{children}
; 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/payment/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | import { PropsWithChildren } from 'react'; 3 | 4 | export default function RootLayout({ children }: PropsWithChildren) { 5 | return
{children}
; 6 | } 7 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/user/[username]/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | import { PropsWithChildren } from 'react'; 3 | 4 | export default function RootLayout({ children }: PropsWithChildren) { 5 | return
{children}
; 6 | } 7 | -------------------------------------------------------------------------------- /src/ad-service/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/membership-plans/layout.tsx: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css'; 2 | import { PropsWithChildren } from 'react'; 3 | 4 | export default function RootLayout({ children }: PropsWithChildren) { 5 | return
{children}
; 6 | } 7 | -------------------------------------------------------------------------------- /src/malicious-load-generator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM locustio/locust:2.9.0 2 | 3 | # install curl 4 | USER root 5 | RUN apt-get update 6 | RUN apt-get install -y curl 7 | 8 | COPY --chown=locust:locust . . 9 | RUN chmod +x loadgen.sh 10 | ENTRYPOINT ./loadgen.sh -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "@commitlint/cli": "^19.3.0", 4 | "commitlint": "^17.4.4", 5 | "commitlint-config-gitmoji": "^2.3.1", 6 | "husky": "^9.0.11" 7 | }, 8 | "scripts": { 9 | "prepare": "husky" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/profile-service/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/proxy-service/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/microblog-service/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/frontend-nextjs/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Unguard", 3 | "shortName": "Unguard", 4 | "description": "An insecure cloud-native microservices demo application.", 5 | "display": "standalone", 6 | "theme_color": "#FFFFFF", 7 | "background_color": "#FFFFFF" 8 | } 9 | -------------------------------------------------------------------------------- /src/membership-service/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | /dataSources.xml 10 | /sqldialects.xml 11 | /checkstyle-idea.xml 12 | 13 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # This file is the top-most EditorConfig file 2 | root = true 3 | 4 | # All Files 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 4 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # YAML Files 13 | [*.{yml,yaml}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /src/like-service/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /CITATION.cff: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | title: "Unguard: An Insecure Cloud-Native Microservice-Based Application" 3 | message: "If you use this software, please cite it as below." 4 | type: software 5 | authors: 6 | - name: "Dynatrace LLC" 7 | date-released: 2023 8 | url: "https://github.com/dynatrace-oss/unguard" 9 | license: Apache-2.0 -------------------------------------------------------------------------------- /exploit-toolkit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.13-slim 2 | 3 | WORKDIR /opt/ug-exploit 4 | 5 | COPY requirements.txt . 6 | 7 | RUN pip install --no-cache-dir --upgrade pip && \ 8 | pip install --no-cache-dir -r requirements.txt 9 | 10 | COPY . . 11 | 12 | RUN pip install --no-cache-dir -e . 13 | 14 | CMD ["ug-exploit"] 15 | -------------------------------------------------------------------------------- /chart/jaeger.yaml: -------------------------------------------------------------------------------- 1 | provisionDataStore: 2 | cassandra: false 3 | elasticsearch: true 4 | storage: 5 | type: elasticsearch 6 | elasticsearch: 7 | antiAffinity: soft 8 | replicas: 1 9 | minimumMasterNodes: 1 10 | esIndexCleaner: 11 | enabled: true 12 | tag: 1.22 13 | schedule: "55 23 * * *" 14 | numberOfDays: 7 15 | -------------------------------------------------------------------------------- /src/profile-service/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.datasource.driverClassName=org.h2.Driver 2 | spring.jpa.database-platform=org.hibernate.dialect.H2Dialect 3 | spring.h2.console.enabled=true 4 | spring.h2.console.path=/h2-console 5 | spring.h2.console.settings.web-allow-others=true 6 | spring.jpa.generate-ddl=true 7 | 8 | -------------------------------------------------------------------------------- /chart/aws.yaml: -------------------------------------------------------------------------------- 1 | localDev: 2 | enabled: false 3 | 4 | aws: 5 | enabled: true 6 | ingress: 7 | annotations: 8 | kubernetes.io/ingress.class: alb 9 | alb.ingress.kubernetes.io/target-type: ip 10 | alb.ingress.kubernetes.io/scheme: internal 11 | alb.ingress.kubernetes.io/load-balancer-name: "unguard-lb" 12 | -------------------------------------------------------------------------------- /src/frontend-nextjs/.eslintignore: -------------------------------------------------------------------------------- 1 | .now/* 2 | *.css 3 | .changeset 4 | dist 5 | esm/* 6 | public/* 7 | tests/* 8 | scripts/* 9 | *.config.js 10 | .DS_Store 11 | node_modules 12 | coverage 13 | .next 14 | build 15 | !.commitlintrc.cjs 16 | !.lintstagedrc.cjs 17 | !jest.config.js 18 | !plopfile.js 19 | !react-shim.js 20 | !tsup.config.ts -------------------------------------------------------------------------------- /src/frontend-nextjs/enums/routes.ts: -------------------------------------------------------------------------------- 1 | export enum ROUTES { 2 | home = '/', 3 | users = '/users', 4 | login = '/login', 5 | mytimeline = '/mytimeline', 6 | post = '/post', 7 | user = '/user', 8 | payment = '/payment', 9 | membership_plans = '/membership-plans', 10 | ad_manager = '/ad-manager', 11 | } 12 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto encoding=UTF-8 5 | 6 | *.sh text eol=lf 7 | gradlew text eol=lf 8 | -------------------------------------------------------------------------------- /src/user-simulator/data/imgposts.json: -------------------------------------------------------------------------------- 1 | { 2 | "posts": [ 3 | { 4 | "url": "https://cdn.cookielaw.org/logos/d7b8abfa-8364-4e1c-835b-bc0c7bf676d1/2e44f07a-cbc2-4f3f-aa72-cb13bc93aa0f/43ccf2f1-faf9-49ed-b07e-a84b721407b7/Image20200709104922.jpg", 5 | "text": "The Dynatrace logo, I quite like it." 6 | } 7 | ] 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/frontend-nextjs/swagger-ui-react.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'swagger-ui-react' { 2 | import { FunctionComponent } from 'react'; 3 | interface SwaggerUIProps { 4 | spec?: object; 5 | url?: string; 6 | [key: string]: any; 7 | } 8 | const SwaggerUI: FunctionComponent; 9 | export default SwaggerUI; 10 | } 11 | -------------------------------------------------------------------------------- /src/microblog-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:6.9.1-jdk11 as builder 2 | 3 | COPY --chown=gradle:gradle . /home/gradle/src 4 | WORKDIR /home/gradle/src 5 | RUN gradle build 6 | 7 | FROM eclipse-temurin:11-jre-alpine 8 | EXPOSE 8080 9 | COPY --from=builder /home/gradle/src/build/libs/**.jar /app/app.jar 10 | WORKDIR /app 11 | ENTRYPOINT ["java","-jar","app.jar"] 12 | -------------------------------------------------------------------------------- /src/user-simulator/Dockerfile: -------------------------------------------------------------------------------- 1 | # based on snippet from https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md 2 | FROM --platform=linux/amd64 ghcr.io/puppeteer/puppeteer:24.11.0 AS base 3 | COPY --chown=pptruser:pptruser . . 4 | 5 | RUN npm install 6 | RUN npm run build 7 | 8 | ENV NODE_ENV=production 9 | ENV NODE_CONFIG_STRICT_MODE=1 10 | CMD ["npm", "start"] 11 | -------------------------------------------------------------------------------- /src/envoy-proxy/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 envoyproxy/envoy:v1.23.0 2 | 3 | RUN apt-get update && \ 4 | apt-get install -y curl python3 python3-pip && \ 5 | rm -rf /var/lib/apt/lists/* 6 | 7 | USER envoy:envoy 8 | ADD --chown=envoy:envoy config /etc/envoy/unguard 9 | 10 | EXPOSE 8080 11 | EXPOSE 8081 12 | 13 | CMD ["-c", "/etc/envoy/unguard/envoy-config.yaml"] 14 | -------------------------------------------------------------------------------- /src/ad-service/unguard.ad-service.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/frontend-nextjs/.env: -------------------------------------------------------------------------------- 1 | MICROBLOG_SERVICE_ADDRESS=localhost:8080 2 | AD_SERVICE_ADDRESS=localhost:8082 3 | USER_AUTH_SERVICE_ADDRESS=localhost:9091 4 | AD_SERVICE_BASE_PATH=/ad-service 5 | PROXY_SERVICE_ADDRESS=localhost:8081 6 | STATUS_SERVICE_BASE_PATH=/status-service 7 | STATUS_SERVICE_ADDRESS=localhost:8083 8 | LIKE_SERVICE_ADDRESS=localhost:8000 9 | AD_SERVICE_ADDRESS=localhost:8082 10 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api-doc/page.tsx: -------------------------------------------------------------------------------- 1 | import { getApiDocs } from '@/lib/swagger'; 2 | import ReactSwagger from '@/components/SwaggerUIReact'; 3 | 4 | export default async function IndexPage() { 5 | const spec = await getApiDocs(); 6 | 7 | return ( 8 |
9 | 10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/healthz/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | /** 4 | * @swagger 5 | * /ui/api/healthz: 6 | * get: 7 | * description: Check if the Unguard frontend is running. 8 | */ 9 | 10 | export async function GET(): Promise { 11 | return NextResponse.json('Unguard frontend is up and running!', { status: 200 }); 12 | } 13 | -------------------------------------------------------------------------------- /src/user-simulator/unguard.user-simulator.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /chart/Chart.yaml: -------------------------------------------------------------------------------- 1 | annotations: 2 | licenses: Apache-2.0 3 | apiVersion: v2 4 | name: unguard 5 | version: 0.12.0 6 | description: Unguard is an insecure cloud-native microservices demo application. 7 | type: application 8 | home: https://github.com/dynatrace-oss/unguard 9 | icon: https://github.com/dynatrace-oss/unguard/blob/main/docs/images/logo/unguard-logo-red-small.png 10 | appVersion: 0.12.0 11 | -------------------------------------------------------------------------------- /src/user-auth-service/unguard.user-auth-service.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/payment-service/unguard.payment-service.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /exploit-toolkit/exploit-toolkit.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/posts/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchAllPosts } from '@/services/api/PostService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/posts: 8 | * get: 9 | * description: Get all posts. 10 | */ 11 | 12 | export async function GET(): Promise { 13 | const posts = await fetchAllPosts(); 14 | 15 | return NextResponse.json(posts); 16 | } 17 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/roles/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchRoles } from '@/services/api/UserService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/roles: 8 | * get: 9 | * description: Get all existing roles. 10 | */ 11 | 12 | export async function GET(): Promise { 13 | const roles = await fetchRoles(); 14 | 15 | return NextResponse.json(roles); 16 | } 17 | -------------------------------------------------------------------------------- /src/status-service/.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | *.tmp 14 | 15 | # Output of the go coverage tool 16 | *.out 17 | -------------------------------------------------------------------------------- /src/ad-service/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.project 6 | **/.settings 7 | **/.toolstarget 8 | **/.vs 9 | **/.vscode 10 | **/.idea 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/ads/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { getAdList } from '@/services/api/AdManagerService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/ads: 8 | * get: 9 | * description: Get a list of all ads. 10 | */ 11 | 12 | export async function GET(): Promise { 13 | const res = await getAdList(); 14 | 15 | return NextResponse.json(res.data, { status: res.status }); 16 | } 17 | -------------------------------------------------------------------------------- /src/malicious-load-generator/unguard.malicious-load-generator.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /chart/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/auth/logout/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | import { cookies } from 'next/headers'; 3 | 4 | /** 5 | * @swagger 6 | * /ui/api/auth/logout: 7 | * post: 8 | * description: Log out the user. 9 | */ 10 | 11 | export async function POST(): Promise { 12 | const cookieStore = await cookies(); 13 | 14 | cookieStore.delete('jwt'); 15 | 16 | return NextResponse.json('Logout successful'); 17 | } 18 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/BioService.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { BASE_PATH } from '@/constants'; 4 | 5 | export async function updateBio( 6 | username: string, 7 | data: { bioText: string; enableMarkdown: boolean }, 8 | ): Promise { 9 | return await fetch(path.join(BASE_PATH, `/api/user/${username}/bio`), { 10 | method: 'POST', 11 | headers: { 'Content-Type': 'application/json' }, 12 | body: JSON.stringify(data), 13 | }); 14 | } 15 | -------------------------------------------------------------------------------- /.github/workflows/commitlint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint Commit Messages 2 | on: [pull_request] 3 | 4 | jobs: 5 | commitlint: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | with: 10 | fetch-depth: 0 11 | - uses: actions/setup-node@v2 12 | with: 13 | node-version: '18' 14 | - run: npm install 15 | - uses: wagoid/commitlint-github-action@v5 16 | env: 17 | NODE_PATH: ${{ github.workspace }}/node_modules 18 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/auth/ad-manager/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { isAdManager } from '@/services/LocalUserService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/auth/ad-manager: 8 | * get: 9 | * description: Check if the user has ad manager role. 10 | */ 11 | 12 | export async function GET(): Promise> { 13 | const hasAdManagerRole = await isAdManager(); 14 | 15 | return NextResponse.json(hasAdManagerRole); 16 | } 17 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/ErrorCard.tsx: -------------------------------------------------------------------------------- 1 | import { Card } from '@heroui/react'; 2 | import { BsExclamationTriangle } from 'react-icons/bs'; 3 | 4 | interface ErrorCardProps { 5 | message: string; 6 | } 7 | 8 | export function ErrorCard({ message }: ErrorCardProps) { 9 | return ( 10 | 11 | 12 | {message} 13 | 14 | ); 15 | } 16 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/posts/mytimeline/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchPersonalTimeline } from '@/services/api/PostService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/posts/mytimeline: 8 | * get: 9 | * description: Get the personal timeline for the authenticated user. 10 | */ 11 | 12 | export async function GET(): Promise { 13 | const posts = await fetchPersonalTimeline(); 14 | 15 | return NextResponse.json(posts); 16 | } 17 | -------------------------------------------------------------------------------- /src/profile-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:7.4.1-jdk11 AS builder 2 | 3 | COPY --chown=gradle:gradle . /home/gradle/src 4 | WORKDIR /home/gradle/src 5 | RUN gradle bootJar 6 | 7 | FROM youtaqiu/jre-trace@sha256:1d3cc26f9de63a75ba2f915a8079247b4f1f250a16f219dc322bf2d011c78d55 8 | EXPOSE 8080 9 | COPY --from=builder /home/gradle/src/build/libs/**.jar /app/app.jar 10 | RUN apk --no-cache add markdown 11 | WORKDIR /app 12 | 13 | ENTRYPOINT ["java", "-javaagent:/app/lib/opentelemetry.jar", "-jar", "app.jar"] 14 | -------------------------------------------------------------------------------- /src/proxy-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8u111-jdk-alpine as builder 2 | 3 | COPY . /home/gradle/src 4 | WORKDIR /home/gradle/src 5 | RUN ./gradlew build 6 | RUN ls build/libs/ 7 | 8 | # force JDK < 8u121 to provoke CVE-2021-44228 10.0 RCE in log4j < 2.4.1 9 | # https://security.snyk.io/vuln/SNYK-JAVA-ORGAPACHELOGGINGLOG4J-2314720 10 | FROM openjdk:8u111-jre 11 | EXPOSE 8081 12 | COPY --from=builder /home/gradle/src/build/libs/**.jar /app/app.jar 13 | WORKDIR /app 14 | ENTRYPOINT ["java","-jar","app.jar"] 15 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/users/page.tsx: -------------------------------------------------------------------------------- 1 | import { Ad } from '@/components/Ad'; 2 | import { UserSearch } from '@/components/UsersView/UserSearch'; 3 | 4 | export default function Users() { 5 | return ( 6 |
7 |

Users

8 |
9 | 10 | 11 |
12 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/Footer/Footer.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { Link } from '@heroui/react'; 3 | 4 | import { DeploymentStatus } from '@/components/Footer/DeploymentStatus'; 5 | 6 | export function Footer() { 7 | return ( 8 |
9 |

Powered by Unguard.

10 |

11 | Robots lovingly delivered by Robohash.org 12 |

13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/deployment-health/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchDeploymentHealth } from '@/services/api/DeploymentHealthService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/deployment-health: 8 | * get: 9 | * description: Get the health status of the deployment. 10 | */ 11 | 12 | export async function GET(): Promise { 13 | const res = await fetchDeploymentHealth(); 14 | 15 | return NextResponse.json(res.data, { status: res.status }); 16 | } 17 | -------------------------------------------------------------------------------- /src/status-service/unguard.status-service.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/user-auth-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18.20.8-alpine 2 | 3 | # Create app directory 4 | WORKDIR /usr/src/app 5 | 6 | # Install app dependencies 7 | # A wildcard is used to ensure both package.json AND package-lock.json are copied 8 | # where available (npm@5+) 9 | COPY package*.json ./ 10 | 11 | RUN npm install 12 | # If you are building your code for production 13 | # RUN npm ci --only=production 14 | 15 | # Bundle app source 16 | COPY . . 17 | 18 | EXPOSE 9091 19 | CMD [ "node", "--unhandled-rejections=strict", "bin/www" ] 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/MembershipService.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { BASE_PATH } from '@/constants'; 4 | 5 | export type MembershipData = { 6 | membership: string; 7 | }; 8 | 9 | export async function updateMembership(data: MembershipData, username: string): Promise { 10 | return await fetch(path.join(BASE_PATH, `/api/user/${username}/membership`), { 11 | method: 'POST', 12 | headers: { 'Content-Type': 'application/json' }, 13 | body: JSON.stringify(data), 14 | }); 15 | } 16 | -------------------------------------------------------------------------------- /src/membership-service/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "MembershipService": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development", 8 | "SERVER_PORT": "8083", 9 | "API_PATH":"/membership-service", 10 | "MARIADB_SERVICE": "localhost", 11 | "MARIADB_PASSWORD": "abc123" 12 | } 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/frontend-nextjs/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSameLine": false, 4 | "bracketSpacing": true, 5 | "embeddedLanguageFormatting": "auto", 6 | "htmlWhitespaceSensitivity": "strict", 7 | "insertPragma": false, 8 | "jsxSingleQuote": true, 9 | "printWidth": 120, 10 | "proseWrap": "preserve", 11 | "quoteProps": "consistent", 12 | "requirePragma": false, 13 | "semi": true, 14 | "singleQuote": true, 15 | "tabWidth": 4, 16 | "trailingComma": "all", 17 | "useTabs": false 18 | } 19 | -------------------------------------------------------------------------------- /src/frontend-nextjs/styles/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | h1 { 6 | font-size: 2em; 7 | font-weight: 900; 8 | } 9 | 10 | h2 { 11 | font-size: 1.5em; 12 | font-weight: 800; 13 | } 14 | 15 | h3 { 16 | font-size: 1.17em; 17 | font-weight: 700; 18 | } 19 | 20 | h4 { 21 | font-size: 1em; 22 | font-weight: 600; 23 | } 24 | 25 | h5 { 26 | font-size: 0.83em; 27 | font-weight: 500; 28 | } 29 | 30 | h6 { 31 | font-size: 0.67em; 32 | font-weight: 400; 33 | } 34 | -------------------------------------------------------------------------------- /.idea/unguard.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/envoy-proxy/config/request-logger.lua: -------------------------------------------------------------------------------- 1 | local request_logger = {} 2 | 3 | function request_logger.log_payload(handle) 4 | local payload = "" 5 | for chunk in handle:bodyChunks() do 6 | local chunk_length = chunk:length() 7 | if (chunk_length > 0) then 8 | payload = payload .. chunk:getBytes(0, chunk_length) 9 | end 10 | end 11 | payload = tostring(payload) 12 | handle:streamInfo():dynamicMetadata():set("envoy.filters.http.lua.request-filter", "payload", payload) 13 | return payload 14 | end 15 | 16 | return request_logger 17 | -------------------------------------------------------------------------------- /src/frontend-nextjs/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | _next 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/auth/register/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { registerUser } from '@/services/api/AuthService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/auth/register: 8 | * post: 9 | * description: Register a new user with email and password. 10 | */ 11 | 12 | export async function POST(request: Request): Promise { 13 | const body = await request.json(); 14 | const response = await registerUser(body); 15 | 16 | return NextResponse.json(response.data, { status: response.status }); 17 | } 18 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/AdService.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { BASE_PATH } from '@/constants'; 4 | 5 | export async function deleteAd(adName: string): Promise { 6 | return await fetch(path.join(BASE_PATH, `/api/ad/${adName}`), { 7 | method: 'DELETE', 8 | headers: { 'Content-Type': 'application/json' }, 9 | }); 10 | } 11 | 12 | export async function uploadAd(ad: FormData): Promise { 13 | return await fetch(path.join(BASE_PATH, `/api/ad`), { 14 | method: 'POST', 15 | body: ad, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/like/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { unlikePost } from '@/services/api/LikeService'; 4 | 5 | /** @swagger 6 | * /ui/api/like/unlike: 7 | * delete: 8 | * description: Unlike a post by its ID. 9 | */ 10 | 11 | export async function DELETE(req: Request): Promise { 12 | const { searchParams } = new URL(req.url); 13 | const postId = searchParams.getAll('postId'); 14 | const res = await unlikePost(postId); 15 | 16 | return NextResponse.json(res.data, { status: res.status }); 17 | } 18 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/cmd-injection/README.md: -------------------------------------------------------------------------------- 1 | # Command injection 2 | 3 | Unguard has three command injection vulnerabilities: 4 | 5 | * Two of them in Java 6 | * [The first Java CMD injection](CMDI-IMAGE-POSTING.md) that is exploited through the image posting feature of 7 | the `proxy-service` 8 | * [A second Java CMD injection](CMDI-MARKDOWN-CONVERSION.md) that is exploited through the markdown conversion that 9 | happens when setting a bio string with markdown enabled on the `profile-service` 10 | * [In Lua](CMDI-LUA.md), through a Lua filter in the `envoy-proxy` 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/ad-service/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "AdService": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": "true", 6 | "environmentVariables": { 7 | "ASPNETCORE_ENVIRONMENT": "Development", 8 | "SERVER_PORT": "8082", 9 | "API_PATH":"/ad-service", 10 | "USER_AUTH_SERVICE_ADDRESS":"localhost:9091", 11 | "JAEGER_SERVICE_NAME": "ad-service", 12 | "JAEGER_AGENT_HOST": "localhost", 13 | "JAEGER_SAMPLER_TYPE": "const", 14 | "JAEGER_SAMPLER_PARAM": "1" 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/post/[postId]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchPostById } from '@/services/api/PostService'; 4 | import { PostParams } from '@/app/api/like/[postId]/route'; 5 | 6 | /** 7 | * @swagger 8 | * /ui/api/post/{postId}: 9 | * get: 10 | * description: Get a post by its ID. 11 | */ 12 | 13 | export async function GET(req: Request, { params }: { params: Promise }): Promise { 14 | const { postId } = await params; 15 | const posts = await fetchPostById(postId); 16 | 17 | return NextResponse.json(posts); 18 | } 19 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/likes/[postId]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchLikes } from '@/services/api/LikeService'; 4 | import { PostParams } from '@/app/api/like/[postId]/route'; 5 | 6 | /** 7 | * @swagger 8 | * /ui/api/likes/{postId}: 9 | * get: 10 | * description: Get likes for a post by its ID. 11 | */ 12 | 13 | export async function GET(req: Request, { params }: { params: Promise }): Promise { 14 | const { postId } = await params; 15 | const res = await fetchLikes(postId); 16 | 17 | return NextResponse.json(res.data); 18 | } 19 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/membership-plans/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { MembershipSelector } from '@/components/Membership/MembershipSelector'; 4 | import { getUsernameFromJwt } from '@/services/LocalUserService'; 5 | import { ErrorCard } from '@/components/ErrorCard'; 6 | 7 | export default async function MembershipPlans() { 8 | const username = await getUsernameFromJwt(); 9 | 10 | if (!username) { 11 | return ; 12 | } 13 | 14 | return ; 15 | } 16 | -------------------------------------------------------------------------------- /src/profile-service/.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | build/ 3 | !gradle/wrapper/gradle-wrapper.jar 4 | !**/src/main/**/build/ 5 | !**/src/test/**/build/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | bin/ 16 | !**/src/main/**/bin/ 17 | !**/src/test/**/bin/ 18 | 19 | ### IntelliJ IDEA ### 20 | .idea.old 21 | *.iws 22 | *.iml 23 | *.ipr 24 | out/ 25 | !**/src/main/**/out/ 26 | !**/src/test/**/out/ 27 | 28 | ### NetBeans ### 29 | /nbproject/private/ 30 | /nbbuild/ 31 | /dist/ 32 | /nbdist/ 33 | /.nb-gradle/ 34 | 35 | ### VS Code ### 36 | .vscode/ 37 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/ad/[adName]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { deleteAd } from '@/services/api/AdManagerService'; 4 | 5 | type AdParams = { 6 | adName: string; 7 | }; 8 | 9 | /** 10 | * @swagger 11 | * /ui/api/ad/{adName}: 12 | * delete: 13 | * description: Delete an Ad by its name. 14 | */ 15 | 16 | export async function DELETE(req: Request, { params }: { params: Promise }): Promise { 17 | const { adName } = await params; 18 | const res = await deleteAd(adName); 19 | 20 | return NextResponse.json(res.data, { status: res.status }); 21 | } 22 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/followers/[username]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { getFollowers } from '@/services/api/FollowService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/followers/{username}: 8 | * get: 9 | * description: Get a list of followers for a user by username. 10 | */ 11 | 12 | export async function GET(req: Request, { params }: { params: Promise<{ username: string }> }): Promise { 13 | const { username } = await params; 14 | 15 | const res = await getFollowers(username); 16 | 17 | return NextResponse.json(res.data, { status: res.status }); 18 | } 19 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/like/[postId]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { likePost } from '@/services/api/LikeService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/like/{postId}: 8 | * post: 9 | * description: Like a post by its ID. 10 | */ 11 | 12 | export type PostParams = { 13 | postId: string; 14 | }; 15 | 16 | export async function POST(req: Request, { params }: { params: Promise }): Promise { 17 | const { postId } = await params; 18 | const res = await likePost(postId); 19 | 20 | return NextResponse.json(res.data, { status: res.status }); 21 | } 22 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useAd.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { BASE_PATH } from '@/constants'; 6 | import { QUERY_KEYS } from '@/enums/queryKeys'; 7 | 8 | async function fetchAd() { 9 | const res = await fetch(path.join(BASE_PATH, '/api/ad')); 10 | 11 | if (!res.ok) { 12 | throw new Error('Failed to fetch ad'); 13 | } 14 | 15 | return res.json(); 16 | } 17 | 18 | export function useAd() { 19 | return useQuery({ 20 | queryKey: [QUERY_KEYS.ad], 21 | queryFn: fetchAd, 22 | throwOnError: true, 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useAdVisibility.ts: -------------------------------------------------------------------------------- 1 | import { useJwtPayload } from '@/hooks/queries/useJwtPayload'; 2 | import { useMembership } from '@/hooks/queries/useMembership'; 3 | import { MEMBERSHIP } from '@/enums/memberships'; 4 | 5 | export function useAdVisibility() { 6 | const { data: jwt_payload, isLoading: isJwtLoading } = useJwtPayload(); 7 | const username = jwt_payload?.username; 8 | 9 | const membershipResult = useMembership(username || ''); 10 | 11 | return { 12 | isLoading: isJwtLoading || membershipResult.isLoading, 13 | isPro: membershipResult.data == MEMBERSHIP.PRO, 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/frontend-nextjs/tailwind.config.js: -------------------------------------------------------------------------------- 1 | import { heroui } from '@heroui/theme'; 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | const config = { 5 | content: [ 6 | './components/**/*.{js,ts,jsx,tsx,mdx}', 7 | './app/**/*.{js,ts,jsx,tsx,mdx}', 8 | './node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}', 9 | ], 10 | theme: { 11 | extend: { 12 | colors: { 13 | primary: '#4ab973', 14 | secondary: '#f3f3f3', 15 | }, 16 | }, 17 | }, 18 | darkMode: 'class', 19 | plugins: [heroui()], 20 | }; 21 | 22 | module.exports = config; 23 | -------------------------------------------------------------------------------- /src/payment-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.8.0-slim-buster 2 | 3 | # Install Poetry 4 | RUN pip install poetry==1.8.3 5 | 6 | # Copy only requirements to cache them in docker layer 7 | WORKDIR /poetry 8 | COPY poetry.lock pyproject.toml /poetry/ 9 | 10 | # Project initialization: 11 | RUN poetry config virtualenvs.create false \ 12 | && poetry install --no-interaction --no-ansi 13 | 14 | # Creating folders, and files for a project: 15 | WORKDIR /app 16 | COPY . /app 17 | 18 | EXPOSE 8084 19 | 20 | RUN poetry run opentelemetry-bootstrap -a install 21 | 22 | ENTRYPOINT ["opentelemetry-instrument", "python", "payment_service/run.py"] 23 | 24 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/posts/[username]/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchPostsForUser } from '@/services/api/PostService'; 4 | import { UserParams } from '@/app/api/user/[username]/bio/route'; 5 | 6 | /** 7 | * @swagger 8 | * /ui/api/posts/{username}: 9 | * get: 10 | * description: Get posts for a user by username. 11 | */ 12 | 13 | export async function GET(req: Request, { params }: { params: Promise }): Promise { 14 | const { username } = await params; 15 | const posts = await fetchPostsForUser(username); 16 | 17 | return NextResponse.json(posts); 18 | } 19 | -------------------------------------------------------------------------------- /src/frontend-nextjs/enums/queryKeys.ts: -------------------------------------------------------------------------------- 1 | export enum QUERY_KEYS { 2 | membership = 'membership', 3 | isLoggedIn = 'isLoggedIn', 4 | jwtPayload = 'jwtPayload', 5 | ad = 'ad', 6 | posts = 'posts', 7 | bio = 'bio', 8 | post = 'post', 9 | all_users = 'users-all', 10 | filtered_users = 'users-filtered', 11 | roles = 'roles', 12 | my_timeline = 'my-timeline', 13 | likes = 'likes', 14 | follow_status = 'follow-status', 15 | followers = 'followers', 16 | payment_data = 'payment-data', 17 | ad_manager = 'ad-manager', 18 | ad_list = 'ad-list', 19 | deployment_health = 'deployment-health', 20 | } 21 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/FollowService.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { BASE_PATH } from '@/constants'; 4 | 5 | export async function followUser(username: string): Promise { 6 | return await fetch(path.join(BASE_PATH, `/api/follow/${username}`), { 7 | method: 'POST', 8 | headers: { 'Content-Type': 'application/json' }, 9 | }); 10 | } 11 | 12 | export async function unfollowUser(username: string): Promise { 13 | return await fetch(path.join(BASE_PATH, `/api/follow/${username}`), { 14 | method: 'DELETE', 15 | headers: { 'Content-Type': 'application/json' }, 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /exploit-toolkit/INSTALL.md: -------------------------------------------------------------------------------- 1 | # Exploit Toolkit CLI 2 | 3 | A small command line application written in Python that automates different attack scenarios tailored for the *Unguard* demo application. 4 | 5 | ## Installation 6 | 7 | Create and activate the virtual environment for the `unguard-exploit` package. 8 | 9 | ``` 10 | python3 -m venv venv 11 | source ./venv/bin/activate 12 | pip install -r requirements.txt 13 | pip install --editable . 14 | ``` 15 | 16 | ## Run 17 | 18 | Check out all available exploits: 19 | 20 | ``` 21 | ug-exploit --help 22 | ``` 23 | 24 | ## Scenarios 25 | 26 | See the [exploit collection](./exploits/README.md) for further details. 27 | -------------------------------------------------------------------------------- /src/payment-service/payment_service/logger_config.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from logging.config import dictConfig 3 | 4 | dictConfig({ 5 | 'version': 1, 6 | 'formatters': {'default': { 7 | 'format': '[%(asctime)s] %(levelname)-8s: %(message)s', 8 | }}, 9 | 'handlers': {'default-handler': { 10 | 'class': 'logging.StreamHandler', 11 | 'stream': 'ext://flask.logging.wsgi_errors_stream', 12 | 'formatter': 'default' 13 | }}, 14 | 'root': { 15 | 'level': 'ERROR', 16 | 'handlers': ['default-handler'] 17 | } 18 | }) 19 | 20 | logger = logging.getLogger(__name__) 21 | logger.setLevel(logging.DEBUG) 22 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/users/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { fetchAllUsers } from '@/services/api/UserService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/users: 8 | * get: 9 | * description: Get all users with optional filters. 10 | */ 11 | 12 | export async function GET(request: Request): Promise { 13 | const { searchParams } = new URL(request.url); 14 | 15 | const params = { 16 | name: searchParams.get('name'), 17 | roles: searchParams.getAll('roles'), 18 | }; 19 | 20 | const users = await fetchAllUsers(params); 21 | 22 | return NextResponse.json(users); 23 | } 24 | -------------------------------------------------------------------------------- /src/ad-service/Pages/_ViewStart.cshtml: -------------------------------------------------------------------------------- 1 | 16 | 17 | @{ 18 | Layout = "_Layout"; 19 | } -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/utils/JwtTokensUtils.java: -------------------------------------------------------------------------------- 1 | package org.dynatrace.microblog.utils; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Header; 5 | import io.jsonwebtoken.Jwt; 6 | import io.jsonwebtoken.Jwts; 7 | 8 | public class JwtTokensUtils { 9 | 10 | public static Claims decodeTokenClaims(String token) { 11 | String[] splitToken = token.split("\\."); 12 | String unsignedToken = splitToken[0] + "." + splitToken[1] + "."; 13 | 14 | Jwt untrusted = Jwts.parser().parseClaimsJwt(unsignedToken); 15 | 16 | Claims claims = (Claims) untrusted.getBody(); 17 | return claims; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/payment-service/payment_service/run.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from db_connector import init_db 3 | from logger_config import logger 4 | import os 5 | import routes 6 | 7 | 8 | def create_app(): 9 | flask_app = Flask(__name__) 10 | with flask_app.app_context(): 11 | init_db() 12 | flask_app.register_blueprint(routes.bp) 13 | return flask_app 14 | 15 | 16 | def get_env_var(env_string): 17 | env_string = os.environ[env_string] 18 | return int(env_string) if env_string else None 19 | 20 | 21 | if __name__ == '__main__': 22 | app = create_app() 23 | app.run(host='0.0.0.0', port=get_env_var('SERVER_PORT')) 24 | logger.info("Server started") 25 | 26 | -------------------------------------------------------------------------------- /src/ad-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:5.0-alpine AS base 2 | WORKDIR /app 3 | EXPOSE 8082 4 | 5 | FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build 6 | 7 | # Copy csproj and restore as distinct layers 8 | WORKDIR /src 9 | COPY ["AdService.csproj", "."] 10 | RUN dotnet restore "AdService.csproj" 11 | 12 | # Copy everything else and build 13 | WORKDIR /src 14 | COPY . . 15 | RUN dotnet build "AdService.csproj" -c Release -o /app/build 16 | 17 | # publish 18 | FROM build AS publish 19 | RUN dotnet publish "AdService.csproj" -c Release -o /app/publish 20 | 21 | FROM base AS final 22 | WORKDIR /app 23 | COPY --from=publish /app/publish . 24 | ENTRYPOINT ["dotnet", "AdService.dll"] 25 | 26 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useBio.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | 8 | async function fetchBio(username: string): Promise { 9 | const res = await fetch(path.join(BASE_PATH, `/api/user/${username}/bio`)); 10 | 11 | if (!res.ok) { 12 | throw new Error('Failed to fetch user bio'); 13 | } 14 | 15 | return res.json(); 16 | } 17 | 18 | export function useBio(username: string) { 19 | return useQuery({ 20 | queryKey: [QUERY_KEYS.bio, username], 21 | queryFn: () => fetchBio(username), 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /src/like-service/public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: 'enhancement' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/mutations/useDeleteAd.ts: -------------------------------------------------------------------------------- 1 | import { useMutation, useQueryClient } from '@tanstack/react-query'; 2 | 3 | import { QUERY_KEYS } from '@/enums/queryKeys'; 4 | import { deleteAd } from '@/services/AdService'; 5 | 6 | export function useDeleteAd(adName: string, onSuccess: () => void, onError: (error: any) => void) { 7 | const queryClient = useQueryClient(); 8 | 9 | return useMutation({ 10 | mutationFn: () => deleteAd(adName), 11 | onSuccess: () => { 12 | onSuccess(); 13 | queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.ad_list] }); 14 | }, 15 | onError: (error: any) => { 16 | onError(error); 17 | }, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/mutations/useUploadAd.ts: -------------------------------------------------------------------------------- 1 | import { useMutation, useQueryClient } from '@tanstack/react-query'; 2 | 3 | import { QUERY_KEYS } from '@/enums/queryKeys'; 4 | import { uploadAd } from '@/services/AdService'; 5 | 6 | export function useUploadAd(onSuccess: () => void, onError: (error: any) => void) { 7 | const queryClient = useQueryClient(); 8 | 9 | return useMutation({ 10 | mutationFn: (formData: FormData) => uploadAd(formData), 11 | onSuccess: () => { 12 | onSuccess(); 13 | queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.ad_list] }); 14 | }, 15 | onError: (error: any) => { 16 | onError(error); 17 | }, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /src/microblog-service/src/test/java/org/dynatrace/microblog/utils/PostSerializerTest.java: -------------------------------------------------------------------------------- 1 | package org.dynatrace.microblog.utils; 2 | 3 | import static org.assertj.core.api.Assertions.assertThat; 4 | 5 | import java.util.Date; 6 | import java.util.UUID; 7 | 8 | import org.dynatrace.microblog.dto.SerializedPost; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class PostSerializerTest { 12 | 13 | private final PostSerializer postSerializer = new PostSerializer(); 14 | 15 | @Test 16 | void serializePost_ReturnsTrue_WhenObjectCouldBeDeserialized() { 17 | assertThat(postSerializer.serializePost( 18 | new SerializedPost("1", "username", "body", "imageURL", new Date(), UUID.randomUUID()))) 19 | .isTrue(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/GlobalTimeline.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { ErrorBoundary } from 'react-error-boundary'; 3 | 4 | import { useAllPosts } from '@/hooks/queries/usePosts'; 5 | import { Timeline } from '@/components/Timeline/Timeline'; 6 | import { ErrorCard } from '@/components/ErrorCard'; 7 | 8 | export function GlobalTimelineComponent() { 9 | const { data, isLoading } = useAllPosts(); 10 | 11 | return ; 12 | } 13 | 14 | export function GlobalTimeline() { 15 | return ( 16 | }> 17 | 18 | 19 | ); 20 | } 21 | -------------------------------------------------------------------------------- /src/payment-service/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "payment-service" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Fabian Oraze "] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "3.8.0" 10 | flask = "1.0.1" 11 | jinja2 = "2.10" 12 | werkzeug = "0.16.1" 13 | itsdangerous = "0.24" 14 | click = "6.7" 15 | markupsafe = "1.1.1" 16 | opentelemetry-distro = "0.46b0" 17 | opentelemetry-exporter-jaeger = "1.21.0" 18 | opentelemetry-propagator-jaeger = "1.25.0" 19 | opentelemetry-exporter-otlp-proto-grpc = "1.25.0" 20 | opentelemetry-exporter-otlp-proto-http = "1.25.0" 21 | 22 | [build-system] 23 | requires = ["poetry-core"] 24 | build-backend = "poetry.core.masonry.api" 25 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/PostService.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { BASE_PATH } from '@/constants'; 4 | 5 | export interface Post { 6 | content?: string; 7 | url?: string; 8 | imageUrl?: string; 9 | language?: string; 10 | } 11 | 12 | export async function createNewPost(data: Post) { 13 | const res = await fetch(path.join(BASE_PATH, '/api/post'), { 14 | method: 'POST', 15 | headers: { 'Content-Type': 'application/json' }, 16 | body: JSON.stringify(data), 17 | }); 18 | 19 | if (!res.ok) { 20 | const error_res = await res.json(); 21 | throw new Error(error_res.statusText || 'Failed to create new post'); 22 | } 23 | 24 | return res.json(); 25 | } 26 | -------------------------------------------------------------------------------- /monaco/management-zone/management-zone.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Dynatrace LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | config: 16 | - unguard: unguard.json 17 | unguard: 18 | - name: unguard 19 | -------------------------------------------------------------------------------- /src/ad-service/Pages/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | 16 | 17 | @namespace AdService.Pages 18 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers -------------------------------------------------------------------------------- /src/ad-service/Pages/ads.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AdService.Pages.Ads 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useRoles.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | 8 | type Role = { 9 | name: string; 10 | }; 11 | 12 | async function fetchRoles(): Promise { 13 | const res = await fetch(path.join(BASE_PATH, '/api/roles')); 14 | 15 | if (!res.ok) { 16 | throw new Error('Failed to fetch list of roles'); 17 | } 18 | 19 | return res.json(); 20 | } 21 | 22 | export function useRoles() { 23 | return useQuery({ 24 | queryKey: [QUERY_KEYS.roles], 25 | queryFn: () => fetchRoles(), 26 | throwOnError: true, 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/LikeService.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { BASE_PATH } from '@/constants'; 4 | 5 | export async function likePost(postId: string): Promise { 6 | return await fetch(path.join(BASE_PATH, `/api/like/${postId}`), { 7 | method: 'POST', 8 | headers: { 'Content-Type': 'application/json' }, 9 | }); 10 | } 11 | 12 | export async function unlikePost(postId: string): Promise { 13 | const queryParams = new URLSearchParams(); 14 | queryParams.append('postId', postId); 15 | return await fetch(path.join(BASE_PATH, `/api/like?${queryParams.toString()}`), { 16 | method: 'DELETE', 17 | headers: { 'Content-Type': 'application/json' }, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /src/membership-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:7.0-alpine AS base 2 | WORKDIR /app 3 | EXPOSE 8083 4 | 5 | FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build 6 | 7 | # Copy csproj and restore as distinct layers 8 | WORKDIR /src 9 | COPY ["MembershipService.csproj", "."] 10 | RUN dotnet restore "MembershipService.csproj" 11 | 12 | # Copy everything else and build 13 | WORKDIR /src 14 | COPY . . 15 | RUN dotnet build "MembershipService.csproj" -c Release -o /app/build 16 | 17 | # publish 18 | FROM build AS publish 19 | RUN dotnet publish "MembershipService.csproj" -c Release -o /app/publish 20 | 21 | FROM base AS final 22 | WORKDIR /app 23 | COPY --from=publish /app/publish . 24 | ENTRYPOINT ["dotnet", "MembershipService.dll"] 25 | 26 | -------------------------------------------------------------------------------- /src/membership-service/MembershipService.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/ad-service/Pages/ads/delete.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AdService.Pages.DeleteAd 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /src/ad-service/Pages/ads/upload.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AdService.Pages.UploadAd 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { Ad } from '@/components/Ad'; 2 | import { CreatePost } from '@/components/CreatePost'; 3 | import { GlobalTimeline } from '@/components/GlobalTimeline'; 4 | import { isLoggedIn } from '@/services/LocalUserService'; 5 | 6 | export default async function Home() { 7 | return ( 8 |
9 |

Timeline

10 |
11 |
12 | {(await isLoggedIn()) && } 13 | 14 |
15 | 16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useAdList.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { BASE_PATH } from '@/constants'; 6 | import { QUERY_KEYS } from '@/enums/queryKeys'; 7 | import { AdList } from '@/services/api/AdManagerService'; 8 | 9 | async function fetchListOfAds(): Promise { 10 | const res = await fetch(path.join(BASE_PATH, '/api/ads')); 11 | 12 | if (!res.ok) { 13 | throw new Error('Failed to fetch list of ads'); 14 | } 15 | 16 | return res.json(); 17 | } 18 | 19 | export function useAdList() { 20 | return useQuery({ 21 | queryKey: [QUERY_KEYS.ad_list], 22 | queryFn: () => fetchListOfAds(), 23 | throwOnError: true, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /src/frontend-nextjs/lib/swagger.ts: -------------------------------------------------------------------------------- 1 | import { createSwaggerSpec } from 'next-swagger-doc'; 2 | 3 | export const getApiDocs = async () => { 4 | return createSwaggerSpec({ 5 | apiFolder: 'app/api', 6 | definition: { 7 | openapi: '3.0.0', 8 | info: { 9 | title: 'Next Swagger API', 10 | version: '1.0', 11 | }, 12 | components: { 13 | securitySchemes: { 14 | BearerAuth: { 15 | type: 'http', 16 | scheme: 'bearer', 17 | bearerFormat: 'JWT', 18 | }, 19 | }, 20 | }, 21 | security: [], 22 | }, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/api/MembershipService.ts: -------------------------------------------------------------------------------- 1 | import querystring from 'querystring'; 2 | 3 | import { getMembershipServiceApi } from '@/axios'; 4 | import { MembershipData } from '@/services/MembershipService'; 5 | 6 | export async function updateMembershipForUser(membershipData: MembershipData, userId: string): Promise { 7 | return await getMembershipServiceApi() 8 | .post(`/add/${userId}`, querystring.stringify(membershipData), { 9 | headers: { 10 | 'Content-Type': 'application/x-www-form-urlencoded', 11 | }, 12 | }) 13 | .then((response) => { 14 | return response; 15 | }) 16 | .catch((error) => { 17 | return error.response; 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/UserProfile/FollowerList.tsx: -------------------------------------------------------------------------------- 1 | import { User } from '@/components/UsersView/User'; 2 | 3 | interface FollowerListProps { 4 | followers: { userId: string; userName: string }[] | undefined; 5 | } 6 | 7 | export function FollowerList(props: FollowerListProps) { 8 | return ( 9 |
10 | {!props.followers || props.followers?.length === 0 11 | ? 'No users found...' 12 | : props.followers?.map((user: { userId: string; userName: string }, index: number) => ( 13 |
14 | 15 |
16 | ))} 17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/SwaggerUIReact.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import SwaggerUI from 'swagger-ui-react'; 4 | import 'swagger-ui-react/swagger-ui.css'; 5 | 6 | type Props = { 7 | spec: Record; 8 | }; 9 | 10 | const HideAuthorizePlugin = () => ({ 11 | components: { 12 | authorizeBtn: () => null, 13 | }, 14 | }); 15 | 16 | const HideTryItOutPlugin = () => ({ 17 | components: { 18 | TryItOutButton: () => null, 19 | }, 20 | }); 21 | 22 | function ReactSwagger({ spec }: Props) { 23 | const specWithoutServers = { ...spec }; 24 | delete specWithoutServers.servers; 25 | 26 | return ; 27 | } 28 | 29 | export default ReactSwagger; 30 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/api/DeploymentHealthService.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios'; 2 | import { getStatusServiceApi } from '@/axios'; 3 | 4 | export async function fetchDeploymentHealth(): Promise { 5 | const statusServiceApi = getStatusServiceApi(); 6 | 7 | try { 8 | const [deploymentDetails, deploymentHealth] = await Promise.all([ 9 | statusServiceApi.get('deployments'), 10 | statusServiceApi.get('deployments/health'), 11 | ]); 12 | 13 | return { 14 | ...deploymentHealth, 15 | data: { 16 | ...deploymentDetails.data, 17 | ...deploymentHealth.data, 18 | }, 19 | }; 20 | } catch (error: any) { 21 | return error.response; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/status-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.18-alpine as builder 2 | 3 | RUN apk add git 4 | 5 | # Set destination for COPY 6 | RUN mkdir /app 7 | ADD . /app 8 | WORKDIR /app 9 | 10 | # Download Go modules 11 | COPY go.mod go.sum ./ 12 | 13 | # Download all the dependencies 14 | RUN go mod download 15 | 16 | # Copy the source code. 17 | COPY . . 18 | 19 | # Build the Go app 20 | RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o status-service . 21 | 22 | # GO Repo base repo 23 | FROM alpine:latest 24 | 25 | RUN apk --no-cache add ca-certificates 26 | 27 | RUN mkdir /app 28 | 29 | WORKDIR /app/ 30 | 31 | # Copy the Pre-built binary file from the previous stage 32 | COPY --from=builder /app/status-service . 33 | 34 | EXPOSE 8083 35 | 36 | # Run 37 | CMD [ "./status-service" ] 38 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useCheckLogin.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | 8 | async function fetchIsLoggedIn(): Promise { 9 | const res = await fetch(path.join(BASE_PATH, '/api/auth/login'), { method: 'GET' }); 10 | 11 | if (!res.ok) { 12 | throw new Error('Failed to fetch login status'); 13 | } 14 | 15 | return res.json(); 16 | } 17 | 18 | export function useCheckLogin() { 19 | const { data, ...rest } = useQuery({ 20 | queryKey: [QUERY_KEYS.isLoggedIn], 21 | queryFn: fetchIsLoggedIn, 22 | throwOnError: true, 23 | }); 24 | 25 | return { isLoggedIn: data, ...rest }; 26 | } 27 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useJwtPayload.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | import { CustomPayLoad } from '@/services/LocalUserService'; 8 | 9 | async function fetchJwtPayload(): Promise { 10 | const res = await fetch(path.join(BASE_PATH, '/api/auth/jwt-payload'), { method: 'GET' }); 11 | 12 | if (!res.ok) { 13 | throw new Error('Failed to fetch JWT'); 14 | } 15 | 16 | return res.json(); 17 | } 18 | 19 | export function useJwtPayload() { 20 | return useQuery({ 21 | queryKey: [QUERY_KEYS.jwtPayload], 22 | queryFn: fetchJwtPayload, 23 | throwOnError: true, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useMembership.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | 8 | async function fetchMembership(username: string): Promise { 9 | const res = await fetch(path.join(BASE_PATH, `/api/user/${username}/membership`)); 10 | 11 | if (!res.ok) { 12 | throw new Error('Failed to fetch membership for user ' + username); 13 | } 14 | 15 | return res.json(); 16 | } 17 | 18 | export function useMembership(username: string) { 19 | return useQuery({ 20 | queryKey: [QUERY_KEYS.membership, username], 21 | queryFn: () => fetchMembership(username), 22 | enabled: !!username, 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/user-auth-service/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-auth-service", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node bin/www", 7 | "dev": "nodemon bin/www" 8 | }, 9 | "dependencies": { 10 | "colors": "^1.4.0", 11 | "cookie-parser": "~1.4.4", 12 | "debug": "~2.6.9", 13 | "express": "~4.16.1", 14 | "http-errors": "~1.6.3", 15 | "jaeger-client": "^3.15.0", 16 | "jsonwebtoken": "^8.5.1", 17 | "jwt-simple": "0.5.2", 18 | "morgan": "~1.9.1", 19 | "mysql": "^2.18.1", 20 | "mysql2": "^2.2.5", 21 | "opentracing": "^0.14.5", 22 | "pug": "2.0.0-beta11", 23 | "readline-sync": "^1.4.10", 24 | "bcrypt": "^5.0.1" 25 | }, 26 | "devDependencies": { 27 | "nodemon": "^2.0.12", 28 | "dotenv": "^10.0.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/usePost.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | import { PostProps } from '@/components/Timeline/Post'; 8 | 9 | async function fetchSinglePost(postId: string): Promise { 10 | const res = await fetch(path.join(BASE_PATH, `/api/post/${postId}`)); 11 | 12 | if (!res.ok) { 13 | throw new Error('Failed to fetch post with id ' + postId); 14 | } 15 | 16 | return res.json(); 17 | } 18 | 19 | export function usePost(postId: string) { 20 | return useQuery({ 21 | queryKey: [QUERY_KEYS.post, postId], 22 | queryFn: () => fetchSinglePost(postId), 23 | throwOnError: true, 24 | }); 25 | } 26 | -------------------------------------------------------------------------------- /src/frontend-nextjs/services/PaymentService.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { BASE_PATH } from '@/constants'; 4 | 5 | export interface PaymentData { 6 | cardHolderName: string; 7 | cardNumber: string; 8 | expiryDate: string; 9 | cvv: string; 10 | } 11 | 12 | export async function updatePaymentData(data: PaymentData, username: string): Promise { 13 | const res = await fetch(path.join(BASE_PATH, `/api/user/${username}/payment`), { 14 | method: 'POST', 15 | headers: { 'Content-Type': 'application/json' }, 16 | body: JSON.stringify(data), 17 | }); 18 | 19 | if (!res.ok) { 20 | const data = await res.json(); 21 | 22 | throw new Error(data.message?.toString() || 'Error updating payment data'); 23 | } 24 | 25 | return res; 26 | } 27 | -------------------------------------------------------------------------------- /k8s-manifests/jaeger/jaeger.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Dynatrace LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: jaegertracing.io/v1 16 | kind: Jaeger 17 | metadata: 18 | # if you change this name, also adapt all "-agent" references elsewhere 19 | name: jaeger 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/error.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { useEffect } from 'react'; 3 | 4 | interface ErrorProps { 5 | error: Error; 6 | reset: () => void; 7 | } 8 | 9 | export default function Error({ error, reset }: ErrorProps) { 10 | useEffect(() => { 11 | // Log the error to an error reporting service 12 | /* eslint-disable no-console */ 13 | console.error(error); 14 | }, [error]); 15 | 16 | return ( 17 |
18 |

Something went wrong!

19 | 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/mutations/useUpdateMembership.ts: -------------------------------------------------------------------------------- 1 | import { useMutation, useQueryClient } from '@tanstack/react-query'; 2 | 3 | import { QUERY_KEYS } from '@/enums/queryKeys'; 4 | import { updateMembership } from '@/services/MembershipService'; 5 | 6 | export function useUpdateMembership(username: string, onSuccess: () => void, onError: (error: any) => void) { 7 | const queryClient = useQueryClient(); 8 | 9 | return useMutation({ 10 | mutationFn: (membership: string) => updateMembership({ membership: membership }, username), 11 | onSuccess: () => { 12 | onSuccess(); 13 | queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.membership, username] }); 14 | }, 15 | onError: (error: any) => { 16 | onError(error); 17 | }, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/mutations/useUpdatePaymentInfo.ts: -------------------------------------------------------------------------------- 1 | import { useMutation, useQueryClient } from '@tanstack/react-query'; 2 | 3 | import { PaymentData, updatePaymentData } from '@/services/PaymentService'; 4 | import { QUERY_KEYS } from '@/enums/queryKeys'; 5 | 6 | export function useUpdatePaymentInfo(username: string, onSuccess: () => void, onError: (error: any) => void) { 7 | const queryClient = useQueryClient(); 8 | 9 | return useMutation({ 10 | mutationFn: (paymentData: PaymentData) => updatePaymentData(paymentData, username), 11 | onSuccess: () => { 12 | onSuccess(); 13 | queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.payment_data, username] }); 14 | }, 15 | onError: (error: any) => { 16 | onError(error); 17 | }, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /src/user-simulator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "ES6", 4 | "target": "es2015", 5 | "moduleResolution": "node", 6 | "removeComments": true, 7 | "lib": [ 8 | "esnext", 9 | "dom" 10 | ], 11 | "strict": false, 12 | "pretty": true, 13 | "strictNullChecks": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "allowUnreachableCode": false, 16 | "alwaysStrict": true, 17 | "noUnusedLocals": true, 18 | "noImplicitAny": false, 19 | "allowSyntheticDefaultImports": true, 20 | "esModuleInterop": true, 21 | "experimentalDecorators": true, 22 | "emitDecoratorMetadata": true, 23 | "declaration": true, 24 | "allowJs": false, 25 | "checkJs": false 26 | }, 27 | "files": [ 28 | "./default-user-sim.perf.ts" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /src/ad-service/Pages/ad.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model AdModel 3 | 4 | 19 | 20 |
21 |
22 | ad 23 |
24 |
-------------------------------------------------------------------------------- /src/user-simulator/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user-simulator", 3 | "version": "1.0.0", 4 | "description": "Simulates a real browser unguard user.", 5 | "scripts": { 6 | "build": "tsc", 7 | "start": "node default-user-sim.perf.js" 8 | }, 9 | "type": "module", 10 | "private": true, 11 | "prettier": { 12 | "semi": false, 13 | "singleQuote": true, 14 | "trailingComma": "all", 15 | "printWidth": 100, 16 | "useTabs": true, 17 | "tabWidth": 2, 18 | "bracketSpacing": true, 19 | "jsxBracketSameLine": true, 20 | "arrowParens": "avoid" 21 | }, 22 | "dependencies": { 23 | "prettier": "^2.8.3", 24 | "puppeteer": "24.11.2" 25 | }, 26 | "devDependencies": { 27 | "typescript": "5.4.5" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/ad-service/AdService.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AdService", "AdService.csproj", "{D189D138-12E0-4991-AEB8-7BEF784F405B}" 4 | EndProject 5 | Global 6 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 7 | Debug|Any CPU = Debug|Any CPU 8 | Release|Any CPU = Release|Any CPU 9 | EndGlobalSection 10 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 11 | {D189D138-12E0-4991-AEB8-7BEF784F405B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 12 | {D189D138-12E0-4991-AEB8-7BEF784F405B}.Debug|Any CPU.Build.0 = Debug|Any CPU 13 | {D189D138-12E0-4991-AEB8-7BEF784F405B}.Release|Any CPU.ActiveCfg = Release|Any CPU 14 | {D189D138-12E0-4991-AEB8-7BEF784F405B}.Release|Any CPU.Build.0 = Release|Any CPU 15 | EndGlobalSection 16 | EndGlobal 17 | -------------------------------------------------------------------------------- /src/ad-service/Pages/Shared/_ValidationScriptsPartial.cshtml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useCheckAdmanager.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | 8 | async function checkForAdManagerRole(): Promise { 9 | const res = await fetch(path.join(BASE_PATH, '/api/auth/ad-manager'), { method: 'GET' }); 10 | 11 | if (!res.ok) { 12 | throw new Error('Failed to fetch ad manager status'); 13 | } 14 | 15 | return res.json(); 16 | } 17 | 18 | export function useCheckAdmanager() { 19 | const { data, ...rest } = useQuery({ 20 | queryKey: [QUERY_KEYS.isLoggedIn, QUERY_KEYS.ad_manager], 21 | queryFn: checkForAdManagerRole, 22 | throwOnError: true, 23 | }); 24 | 25 | return { isAdManager: data, ...rest }; 26 | } 27 | -------------------------------------------------------------------------------- /src/status-service/utils/get-env.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Dynatrace LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package utils 18 | 19 | import "os" 20 | 21 | func GetEnv(key, fallback string) string { 22 | value := os.Getenv(key) 23 | if len(value) == 0 { 24 | return fallback 25 | } 26 | return value 27 | } 28 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useFollowingStatus.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | 8 | async function fetchFollowingStatus(username: string): Promise { 9 | const res = await fetch(path.join(BASE_PATH, '/api/follow/', username)); 10 | 11 | if (!res.ok) { 12 | throw new Error('Failed to fetch follow status'); 13 | } 14 | 15 | return res.json(); 16 | } 17 | 18 | export function useFollowingStatus(username: string) { 19 | const { data, ...rest } = useQuery({ 20 | queryKey: [QUERY_KEYS.follow_status, username], 21 | queryFn: () => fetchFollowingStatus(username), 22 | throwOnError: true, 23 | }); 24 | 25 | return { isFollowed: data, ...rest }; 26 | } 27 | -------------------------------------------------------------------------------- /src/like-service/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | { 15 | const cookieStore = await cookies(); 16 | 17 | if (cookieStore.has('jwt')) { 18 | const jwt = cookieStore.get('jwt')?.value; 19 | 20 | if (jwt) { 21 | const decodedPayload = jwtDecode(jwt); 22 | 23 | return NextResponse.json(decodedPayload); 24 | } 25 | } 26 | 27 | return NextResponse.json({ error: 'Could not get JWT Payload' }); 28 | } 29 | -------------------------------------------------------------------------------- /src/user-auth-service/keys/jwtRS256.key.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0JomicccPWxmkHjbyATi 3 | jYVTbBSR50cPnxwKdXjA2uYFSpTNTswOYhDuHwebOfvw/WF9Bbgc02iMvT4+qMym 4 | HLna3O5AIr/EQQMa5QcCqNZBIOHoojB32FPDo0+JsuETABhvkjttcvcncZXAct1M 5 | eL6SKCIFnOhues8z5LTEmyB09anmE1GOQ7UhGc/TNHGXlA4FSEtbxdVjWcb7if6j 6 | degQphNZ3wjx+Jz0YitxLZHCZqQGvClzh/WazY77RksTOuU92ZQXjABbB5RayYRD 7 | vRm68LaW4rQ+wrwDlP4zWmvTGAIleECTikdrp/mQIPPmcyc4Niq97LcnvadFsixQ 8 | FDjr9tP+s6o4NB7txM2avJgSO4wn+WxIxuBjPBIxxYKGx4Ssg0Tvb4A5WOb3n/up 9 | S85gcuAKH2DjCn1b7aZ1BmMm0Clg2G8j73aqoujcK8ztpyUB235cMzkus9+Wg3kD 10 | YTjt5qdulMOTv4RBhHZSW9iCY0GAVjpV/rxEES0Rl0Bge+wLT0s3aaaRAIgJWZZV 11 | TzRyY5fcORzHX+iqEo7Ul2quHNO4zenvPBH2PoLXKtDF1U4dMZx9NSwKTGj97aCf 12 | /BMa03+AeTNc4yqmeSw+YTpBoPdOMPubpdlcy75504eBHxXxKrc0slkYrJ0tscHI 13 | G6+8GOFIB+RWsxqVEFNsDEUCAwEAAQ== 14 | -----END PUBLIC KEY----- 15 | -------------------------------------------------------------------------------- /src/user-auth-service/routes/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Dynatrace LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | var express = require('express'); 18 | var router = express.Router(); 19 | 20 | /* GET home page. */ 21 | router.get('/', function (req, res, next) { 22 | res.send('UserAuthService') 23 | }); 24 | 25 | module.exports = router; 26 | -------------------------------------------------------------------------------- /src/ad-service/wwwroot/js/site.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Dynatrace LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // file can be overwritten with the sharpcompress path traversal exploit 18 | 19 | window.onload = function() { 20 | // change the ads periodical reload 21 | setTimeout(() => { 22 | document.location.reload(); 23 | }, 5000); 24 | } 25 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/ad/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { uploadAd } from '@/services/api/AdManagerService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/ad: 8 | * get: 9 | * description: Get the ad service endpoint. 10 | * post: 11 | * description: Upload an ad file. 12 | */ 13 | 14 | export async function GET(): Promise { 15 | return NextResponse.json({ src: '/ad-service' }, { status: 200 }); 16 | } 17 | 18 | export async function POST(req: Request): Promise { 19 | const formData = await req.formData(); 20 | const file = formData.get('file'); 21 | 22 | if (!file) { 23 | return NextResponse.json({ error: 'No file received' }, { status: 400 }); 24 | } 25 | 26 | const res = await uploadAd(file); 27 | 28 | return NextResponse.json(res.data, { status: res.status }); 29 | } 30 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useFollowersList.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { QUERY_KEYS } from '@/enums/queryKeys'; 6 | import { BASE_PATH } from '@/constants'; 7 | 8 | type Follower = { 9 | userId: string; 10 | userName: string; 11 | }; 12 | 13 | async function fetchListOfFollowers(username: string): Promise { 14 | const res = await fetch(path.join(BASE_PATH, '/api/followers/', username)); 15 | 16 | if (!res.ok) { 17 | throw new Error('Failed to fetch list of followers'); 18 | } 19 | 20 | return res.json(); 21 | } 22 | 23 | export function useFollowersList(username: string) { 24 | return useQuery({ 25 | queryKey: [QUERY_KEYS.followers, username], 26 | queryFn: () => fetchListOfFollowers(username), 27 | throwOnError: true, 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/UserProfile/UserTimeline.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { ErrorBoundary } from 'react-error-boundary'; 3 | 4 | import { usePostsOfUser } from '@/hooks/queries/usePosts'; 5 | import { Timeline } from '@/components/Timeline/Timeline'; 6 | import { ErrorCard } from '@/components/ErrorCard'; 7 | 8 | interface UserTimelineProps { 9 | username: string; 10 | } 11 | 12 | function UserTimelineComponent({ username }: UserTimelineProps) { 13 | const { data, isLoading } = usePostsOfUser(username); 14 | 15 | return ; 16 | } 17 | 18 | export function UserTimeline({ username }: UserTimelineProps) { 19 | return ( 20 | }> 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/frontend-nextjs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true, 17 | "plugins": [ 18 | { 19 | "name": "next" 20 | } 21 | ], 22 | "paths": { 23 | "@/*": ["./*"] 24 | } 25 | }, 26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "middleware.ts"], 27 | "exclude": ["node_modules"] 28 | } 29 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Title 2 | 3 | A short description of the vulnerability in general, including a link to the CVE (if it exists). 4 | Highlight involved vulnerable components in unguard. 5 | 6 | ## Preconditions and Requirements 7 | 8 | Describe what is needed to showcase this exploit. 9 | 10 | ## Exploitation 11 | 12 | Describe how the overall exploitation path looks like. 13 | If possible include a verification step, to let the user see if their attack worked. 14 | 15 | ### w/o Toolkit CLI 16 | 17 | Describe how to exploit the vulnerability without the prepared toolkit CLI. 18 | 19 | ### With Toolkit CLI (if applicable) 20 | 21 | Describe the usage of the provided toolkit for showcasing the exploit. 22 | 23 | ## Further Details 24 | 25 | Give further details and references: 26 | 27 | * [Page Title - Example.com](https://example.com) 28 | * [Some other title - Example.org](https://example.org) -------------------------------------------------------------------------------- /src/ad-service/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Dynatrace LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | version: "3.8" 16 | services: 17 | ad-service: 18 | image: ad-service 19 | ports: 20 | - "80:8082" 21 | #volumes: 22 | # - :/app/wwwroot/adFolder 23 | volumes: 24 | dt-ad-data: 25 | driver: local 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/post/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { createNewPost } from '@/services/api/CreatePostService'; 4 | 5 | /** 6 | * @swagger 7 | * /ui/api/post: 8 | * post: 9 | * description: Create a new post. 10 | */ 11 | 12 | export async function POST(request: Request): Promise { 13 | const body = await request.json(); 14 | let header = request.headers.get('header'); 15 | if (header) { 16 | header = Buffer.from(header, 'latin1').toString('utf-8'); 17 | } 18 | 19 | const response = await createNewPost(body, header); 20 | 21 | if (response.status !== 200) { 22 | return NextResponse.json( 23 | { error: response.data, statusText: response.data.message }, 24 | { status: response.status }, 25 | ); 26 | } 27 | 28 | return NextResponse.json(response.data.postId); 29 | } 30 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | -------------------------------------------------------------------------------- /exploit-toolkit/exploits/sql-injection/README.md: -------------------------------------------------------------------------------- 1 | # SQL injection 2 | 3 | Unguard has four SQL injection vulnerabilities: 4 | * [One in the Java `profile-service`](./SQLI-PROFILE-SERVICE-H2.md), which is exploitable through the user biography and allows you to access the h2 database. 5 | * [One in the Golang `status-service`](./SQLI-STATUS-SERVICE-MARIADB.md), which is exploitable through the search bar on the Users page and allows you to access the MariaDB database. 6 | * [One in the PHP `like-service`](./SQLI-LIKE-SERVICE-REMOVE-LIKE.md), which allows you to remove another user's like on a given post. 7 | * [One in the .NET `membership-service`](./SQLI-MEMBERSHIP-SERVICE-MARIADB.md), which allows you to add or change another user's membership state. 8 | * [One in the Node.js `user-auth-service`](./SQLI-USER-AUTH-SERVICE-MARIADB.md), which is exploitable through the username field on the login page and allows you to access the MariaDB database. 9 | -------------------------------------------------------------------------------- /docs/images/logo/unguard-logo-fill-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/images/logo/unguard-logo-fill-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/queries/useUserList.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useQuery } from '@tanstack/react-query'; 4 | 5 | import { BASE_PATH } from '@/constants'; 6 | 7 | type User = { 8 | userId: string; 9 | username: string; 10 | roles: string[]; 11 | }; 12 | 13 | async function fetchUsers(params: Record): Promise { 14 | const queryParams = new URLSearchParams(params).toString(); 15 | const res = await fetch(path.join(BASE_PATH, `/api/users?${queryParams}`), { 16 | method: 'GET', 17 | }); 18 | 19 | if (!res.ok) { 20 | throw new Error('Failed to fetch list of users'); 21 | } 22 | 23 | return res.json(); 24 | } 25 | 26 | export function useUserList(params: any, queryKey: string) { 27 | return useQuery({ 28 | queryKey: [queryKey], 29 | queryFn: () => fetchUsers(params), 30 | throwOnError: true, 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/profile-service/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.7.1' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'org.dynatrace' 8 | version = '0.0.1-SNAPSHOT' 9 | sourceCompatibility = "11" 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 17 | implementation 'org.springframework.boot:spring-boot-starter-web' 18 | testImplementation 'org.springframework.boot:spring-boot-starter-test' 19 | developmentOnly 'org.springframework.boot:spring-boot-devtools' 20 | runtimeOnly 'com.h2database:h2:2.0.204' // old version is intentionally used 21 | implementation 'org.springframework.boot:spring-boot-starter-validation' 22 | } 23 | 24 | tasks.named('test') { 25 | useJUnitPlatform() 26 | } 27 | targetCompatibility = JavaVersion.VERSION_11 28 | -------------------------------------------------------------------------------- /src/user-simulator/data/textposts.json: -------------------------------------------------------------------------------- 1 | { 2 | "posts": [ 3 | { 4 | "text": "Hello everyone! 👋" 5 | }, 6 | { 7 | "text": "This is a post on Unguard, the most secure microblogging platform ever 🐦" 8 | }, 9 | { 10 | "text": "Thinking about birds 🦅" 11 | }, 12 | { 13 | "text": "If you ask Rick Astley for a DVD of the movie Up, he won’t give it to you because he’s never gonna give you Up. However, by not giving you Up like you asked for it, he’s letting you down. This is known as the Astley paradox." 14 | }, 15 | { 16 | "text": "if a word is misspelled in the dictionary, how would we ever know?" 17 | }, 18 | { 19 | "text": "imagine an e-mail finding you well" 20 | }, 21 | { 22 | "text": "I'm a robot, beep, boop 🤖" 23 | }, 24 | { 25 | "text": "\"What's your degree in?\"Computer science\"My printer isn't working.\"Neither is mine." 26 | } 27 | ] 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/ad-service/wwwroot/css/style.css: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Dynatrace LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | body { 18 | margin: 0; 19 | } 20 | 21 | .ad-container { 22 | width: 100vw; 23 | height: 100vh; 24 | display: flex; 25 | 26 | } 27 | 28 | .ad-container .ad-picture { 29 | margin:auto; 30 | } 31 | 32 | .ad-container .ad-picture img { 33 | max-width: 98vw; 34 | max-height: 98vh; 35 | } -------------------------------------------------------------------------------- /src/frontend-nextjs/app/api/auth/login/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | import { loginUser } from '@/services/api/AuthService'; 4 | import { isLoggedIn } from '@/services/LocalUserService'; 5 | 6 | /** 7 | * @swagger 8 | * /ui/api/auth/login: 9 | * post: 10 | * description: Log in a user with email and password. 11 | * get: 12 | * description: Check if the user is logged in. 13 | */ 14 | 15 | export async function POST(request: Request): Promise { 16 | const body = await request.json(); 17 | const response = await loginUser(body); 18 | 19 | const res = NextResponse.json(response.data, { status: response.status }); 20 | 21 | res.cookies.set('jwt', response.data?.jwt, { path: '/' }); 22 | 23 | return res; 24 | } 25 | 26 | export async function GET(): Promise> { 27 | const loginStatus = await isLoggedIn(); 28 | 29 | return NextResponse.json(loginStatus); 30 | } 31 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/payment/page.tsx: -------------------------------------------------------------------------------- 1 | import { Spacer } from '@heroui/react'; 2 | import React from 'react'; 3 | 4 | import { ProfileHeader } from '@/components/UserProfile/ProfileHeader'; 5 | import { PaymentForm } from '@/components/Payment/PaymentForm'; 6 | import { getUsernameFromJwt } from '@/services/LocalUserService'; 7 | import { ErrorCard } from '@/components/ErrorCard'; 8 | 9 | export default async function PaymentPage() { 10 | const username = await getUsernameFromJwt(); 11 | 12 | if (!username) { 13 | return ; 14 | } 15 | 16 | return ( 17 |
18 |
19 | 20 |
21 | 22 | 23 |
24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/mutations/useLikeChange.ts: -------------------------------------------------------------------------------- 1 | import { useMutation, useQueryClient } from '@tanstack/react-query'; 2 | 3 | import { likePost, unlikePost } from '@/services/LikeService'; 4 | import { QUERY_KEYS } from '@/enums/queryKeys'; 5 | 6 | export function useLikeChange(postId: string) { 7 | const queryClient = useQueryClient(); 8 | 9 | const likeMutation = useMutation({ 10 | mutationFn: () => likePost(postId), 11 | onSuccess: () => { 12 | queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.likes, postId] }); 13 | }, 14 | }); 15 | 16 | const unlikeMutation = useMutation({ 17 | mutationFn: () => unlikePost(postId), 18 | onSuccess: () => { 19 | queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.likes, postId] }); 20 | }, 21 | }); 22 | 23 | return { 24 | likePost: likeMutation.mutate, 25 | unlikePost: unlikeMutation.mutate, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /src/user-auth-service/routes/jwt.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Dynatrace LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | var express = require('express'); 18 | var router = express.Router(); 19 | var fs = require('fs') 20 | var path = require('path') 21 | var jwtUtil = require('../utils/jwt') 22 | 23 | router.get('/jwtRS256.key.pub', async function (req, res) { 24 | res.json(jwtUtil.JwtPublic.toString()); 25 | }); 26 | 27 | module.exports = router; -------------------------------------------------------------------------------- /docs/jaeger/jaeger-otlp-values.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Dynatrace LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # This enables OpenTelemetry ingress for Jaeger when applied using --values with helm install 16 | collector: 17 | service: 18 | otlp: 19 | http: 20 | port: 4318 21 | name: otlp-http 22 | nodePort: 4318 23 | grpc: 24 | port: 4317 25 | name: otlp-grpc 26 | nodePort: 4317 27 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/dto/PostId.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.dto; 18 | 19 | public class PostId { 20 | private final String postId; 21 | 22 | public PostId(String postId) { 23 | this.postId = postId; 24 | } 25 | 26 | public String getPostId() { 27 | return postId; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exploit-toolkit/setup.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2023 Dynatrace LLC 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | 17 | from setuptools import setup 18 | 19 | setup( 20 | name='unguard-exploit', 21 | version='0.1', 22 | py_modules=['exploit'], 23 | install_requires=[ 24 | 'Click', 25 | 'requests', 26 | 'requests-html' 27 | ], 28 | entry_points=''' 29 | [console_scripts] 30 | ug-exploit=exploit:cli 31 | ''', 32 | ) 33 | -------------------------------------------------------------------------------- /src/frontend-nextjs/config/site.ts: -------------------------------------------------------------------------------- 1 | import { ROUTES } from '@/enums/routes'; 2 | 3 | export type SiteConfig = typeof siteConfig; 4 | 5 | export const siteConfig = { 6 | name: 'Unguard', 7 | description: 8 | 'An insecure cloud-native microservices demo application. It consists of eight app services, a load generator, and two databases.', 9 | navItems: [ 10 | { 11 | label: 'Home', 12 | href: ROUTES.home, 13 | }, 14 | { 15 | label: 'Users', 16 | href: ROUTES.users, 17 | }, 18 | { 19 | label: 'My Timeline', 20 | href: ROUTES.mytimeline, 21 | }, 22 | { 23 | label: 'Login/Register', 24 | href: ROUTES.login, 25 | }, 26 | { 27 | label: 'Membership Plans', 28 | href: ROUTES.membership_plans, 29 | }, 30 | { 31 | label: 'Payment Information', 32 | href: ROUTES.payment, 33 | }, 34 | ], 35 | }; 36 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/form/PostForm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.form; 18 | 19 | public class PostForm { 20 | private String content; 21 | private String imageUrl; 22 | 23 | public String getContent() { 24 | return content; 25 | } 26 | 27 | public String getImageUrl() { 28 | return imageUrl; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/profile-service/src/test/java/org/dynatrace/profileservice/ProfileServiceApplicationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.profileservice; 18 | 19 | import org.junit.jupiter.api.Test; 20 | import org.springframework.boot.test.context.SpringBootTest; 21 | 22 | @SpringBootTest 23 | class ProfileServiceApplicationTests { 24 | 25 | @Test 26 | void contextLoads() { 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /chart/templates/tests/test-frontend-connection.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: unguard-frontend-connection 5 | labels: 6 | app.kubernetes.io/name: unguard-frontend-connection 7 | app.kubernetes.io/part-of: unguard 8 | annotations: 9 | "helm.sh/hook": test 10 | spec: 11 | containers: 12 | - name: unguard-{{ .Values.userSimulator.name }} 13 | image: {{ .Values.userSimulator.cronJob.jobTemplate.container.image.repository }}:{{ .Values.userSimulator.cronJob.jobTemplate.container.image.tag }} 14 | imagePullPolicy: {{ .Values.userSimulator.cronJob.jobTemplate.container.image.pullPolicy }} 15 | env: 16 | - name: FRONTEND_ADDR 17 | value: {{ quote .Values.userSimulator.cronJob.jobTemplate.container.env.FRONTEND_ADDR }} 18 | - name: SIMULATE_PRIVATE_RANGES 19 | value: {{ quote .Values.userSimulator.cronJob.jobTemplate.container.env.SIMULATE_PRIVATE_RANGES }} 20 | command: ["/bin/sh"] 21 | args: ["-c", "node default-user-sim.perf.js"] 22 | restartPolicy: Never 23 | -------------------------------------------------------------------------------- /src/like-service/app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | { 14 | const res = await fetch(path.join(BASE_PATH, '/api/likes/', postId)); 15 | 16 | if (!res.ok) { 17 | throw new Error('Failed to fetch likes'); 18 | } 19 | 20 | const data = await res.json(); 21 | 22 | const likesCount = data?.likeCounts[0]?.likeCount ?? 0; 23 | const isLikedByUser = data?.likedPosts.some((likedPost: any) => likedPost.postId === postId) ?? false; 24 | 25 | return { likesCount, isLikedByUser }; 26 | } 27 | 28 | export function useLikes(postId: string) { 29 | return useQuery({ 30 | queryKey: [QUERY_KEYS.likes, postId], 31 | queryFn: () => fetchLikes(postId), 32 | throwOnError: true, 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/exceptions/NotLoggedInException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.exceptions; 18 | 19 | import org.springframework.http.HttpStatus; 20 | import org.springframework.web.bind.annotation.ResponseStatus; 21 | 22 | @ResponseStatus(code = HttpStatus.FORBIDDEN, reason = "You are not logged in") 23 | public class NotLoggedInException extends Exception { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/exceptions/UserNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.exceptions; 18 | 19 | import org.springframework.http.HttpStatus; 20 | import org.springframework.web.bind.annotation.ResponseStatus; 21 | 22 | @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "User not found.") 23 | public class UserNotFoundException extends Exception { 24 | 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/exceptions/UserAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.exceptions; 18 | 19 | import org.springframework.http.HttpStatus; 20 | import org.springframework.web.bind.annotation.ResponseStatus; 21 | 22 | @ResponseStatus(code = HttpStatus.CONFLICT, reason = "User already exists") 23 | public class UserAlreadyExistsException extends Exception { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/exceptions/FollowYourselfException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.exceptions; 18 | 19 | import org.springframework.http.HttpStatus; 20 | import org.springframework.web.bind.annotation.ResponseStatus; 21 | 22 | @ResponseStatus(code = HttpStatus.BAD_REQUEST, reason = "You cannot follow youself") 23 | public class FollowYourselfException extends Exception { 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/like-service/app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | { 10 | const res = await fetch(path.join(BASE_PATH, `/api/user/${username}/payment`)); 11 | 12 | if (!res.ok) { 13 | if (res.status === 404) { 14 | return { 15 | cardHolderName: '', 16 | cardNumber: '', 17 | cvv: '', 18 | expiryDate: '', 19 | }; 20 | } else { 21 | throw new Error('Failed to fetch payment information for user ' + username); 22 | } 23 | } 24 | 25 | return res.json(); 26 | } 27 | 28 | export function usePaymentInfo(username: string) { 29 | return useQuery({ 30 | queryKey: [QUERY_KEYS.payment_data, username], 31 | queryFn: () => fetchPaymentInfo(username), 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/AdManager/AdList.tsx: -------------------------------------------------------------------------------- 1 | import { Card, Spacer, Spinner } from '@heroui/react'; 2 | 3 | import { AdListItem } from '@/components/AdManager/AdListItem'; 4 | import { useAdList } from '@/hooks/queries/useAdList'; 5 | 6 | export function AdList() { 7 | const { data: adList, isLoading } = useAdList(); 8 | 9 | if (isLoading) { 10 | return ( 11 | 12 | 13 | 14 | ); 15 | } 16 | 17 | return ( 18 |
19 |
20 | {!adList || adList.length === 0 21 | ? 'No ads found...' 22 | : adList.toReversed().map((ad: { name: string; creationTime: string }) => ( 23 |
24 | 25 | 26 |
27 | ))} 28 |
29 |
30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/frontend-nextjs/middleware.ts: -------------------------------------------------------------------------------- 1 | import { NextRequest, NextResponse } from 'next/server'; 2 | import { jwtDecode } from 'jwt-decode'; 3 | 4 | import { ROUTES } from '@/enums/routes'; 5 | import { BASE_PATH } from '@/constants'; 6 | 7 | const protectedRoutes = [ 8 | ROUTES.users, 9 | ROUTES.mytimeline, 10 | ROUTES.post, 11 | ROUTES.payment, 12 | ROUTES.membership_plans, 13 | ROUTES.ad_manager, 14 | ]; 15 | 16 | export function middleware(req: NextRequest) { 17 | const jwt = req.cookies.get('jwt')?.value; 18 | 19 | const isProtected = 20 | protectedRoutes.includes(req.nextUrl.pathname) || req.nextUrl.pathname.startsWith('/user/'); 21 | 22 | if (!jwt && isProtected) { 23 | return NextResponse.redirect(new URL(BASE_PATH + ROUTES.login, req.url)); 24 | } 25 | 26 | if (jwt) { 27 | try { 28 | jwtDecode(jwt); 29 | } catch { 30 | req.cookies.delete('jwt'); 31 | 32 | return NextResponse.redirect(new URL(BASE_PATH + ROUTES.login, req.url)); 33 | } 34 | } 35 | 36 | return NextResponse.next(); 37 | } 38 | -------------------------------------------------------------------------------- /src/frontend-nextjs/public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | #EA5455 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/like-service/app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | allSubdomainsOfApplicationUrl(), 33 | ]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import type { ThemeProviderProps } from 'next-themes'; 3 | 4 | import { QueryClientProvider, QueryClient } from '@tanstack/react-query'; 5 | import * as React from 'react'; 6 | import { HeroUIProvider } from '@heroui/system'; 7 | import { useRouter } from 'next/navigation'; 8 | import { ThemeProvider as NextThemesProvider } from 'next-themes'; 9 | import { ToastProvider } from '@heroui/react'; 10 | 11 | export interface ProvidersProps { 12 | children: React.ReactNode; 13 | themeProps?: ThemeProviderProps; 14 | } 15 | 16 | const queryClient = new QueryClient(); 17 | 18 | export function Providers({ children, themeProps }: ProvidersProps) { 19 | const router = useRouter(); 20 | 21 | return ( 22 | 23 | 24 | 25 | 26 | {children} 27 | 28 | 29 | 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /src/like-service/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 34 | 35 | return $app; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/profile-service/src/main/java/org/dynatrace/profileservice/exceptions/BioNotFoundException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.profileservice.exceptions; 18 | 19 | import org.springframework.http.HttpStatus; 20 | import org.springframework.web.bind.annotation.ResponseStatus; 21 | 22 | @ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Bio not found") 23 | public class BioNotFoundException extends Exception { 24 | public BioNotFoundException(String message) { 25 | super(message); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/proxy-service/src/main/java/org/dynatrace/ssrfservice/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.ssrfservice; 18 | 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | import org.springframework.context.ApplicationContext; 22 | 23 | @SpringBootApplication 24 | public class Application { 25 | 26 | public static void main(String[] args) { 27 | SpringApplication.run(Application.class, args); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /k8s-manifests/localdev/kind/cluster-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Dynatrace LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | apiVersion: kind.x-k8s.io/v1alpha4 16 | kind: Cluster 17 | nodes: 18 | - role: control-plane 19 | kubeadmConfigPatches: 20 | - | 21 | kind: InitConfiguration 22 | nodeRegistration: 23 | kubeletExtraArgs: 24 | node-labels: "ingress-ready=true" 25 | extraPortMappings: 26 | - containerPort: 80 27 | hostPort: 80 28 | protocol: TCP 29 | - containerPort: 443 30 | hostPort: 444 31 | protocol: TCP 32 | - role: worker 33 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/dto/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.dto; 18 | 19 | public class User { 20 | private final int userId; 21 | private final String userName; 22 | 23 | public User(int userId, String userName) { 24 | this.userId = userId; 25 | this.userName = userName; 26 | } 27 | 28 | public int getUserId() { 29 | return userId; 30 | } 31 | 32 | public String getUserName() { 33 | return userName; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/Application.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog; 18 | 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | import org.springframework.context.ApplicationContext; 22 | 23 | @SpringBootApplication 24 | public class Application { 25 | 26 | public static void main(String[] args) { 27 | ApplicationContext ctx = SpringApplication.run(Application.class, args); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /docs/images/logo/unguard-logo-fill-red.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/profile-service/src/main/java/org/dynatrace/profileservice/model/Bio.java: -------------------------------------------------------------------------------- 1 | package org.dynatrace.profileservice.model; 2 | 3 | import javax.persistence.*; 4 | 5 | @Entity 6 | @Table(name = "bio") 7 | public class Bio { 8 | @Id 9 | @GeneratedValue(strategy = GenerationType.IDENTITY) 10 | private int id; 11 | private int userId; 12 | private String bioText; 13 | 14 | public Bio(int id, int userId, String bioText) { 15 | this.id = id; 16 | this.userId = userId; 17 | this.bioText = bioText; 18 | } 19 | 20 | public Bio(int userId, String bioText) { 21 | this.userId = userId; 22 | this.bioText = bioText; 23 | } 24 | 25 | public int getId() { 26 | return id; 27 | } 28 | 29 | public void setId(int id) { 30 | this.id = id; 31 | } 32 | 33 | public int getUserId() { 34 | return userId; 35 | } 36 | 37 | public void setUserId(int userId) { 38 | this.userId = userId; 39 | } 40 | 41 | public String getBioText() { 42 | return bioText; 43 | } 44 | 45 | public void setBioText(String bioText) { 46 | this.bioText = bioText; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/like-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.0-cli 2 | 3 | COPY --from=composer:2.6.5 /usr/bin/composer /usr/bin/composer 4 | 5 | WORKDIR /var/www 6 | 7 | RUN apt update 8 | 9 | RUN apt -y install git zip 10 | 11 | ENV COMPOSER_ALLOW_SUPERUSER=1 12 | 13 | COPY . /var/www 14 | 15 | RUN pecl install opentelemetry-1.0.0RC1 && docker-php-ext-enable opentelemetry 16 | 17 | #This is faster https://github.com/composer/composer/issues/8205#issuecomment-507256979 18 | RUN php /usr/bin/composer config --global repos.packagist composer https://packagist.org 19 | 20 | RUN php /usr/bin/composer update --no-cache 21 | 22 | RUN php /usr/bin/composer install 23 | 24 | EXPOSE 8000 25 | #Fixes error while connecting since laravel requires a key 26 | RUN php artisan --env=local key:generate 27 | 28 | #Fixes error while accessing enviroment variables 29 | RUN php artisan config:clear 30 | 31 | #MySqlDriver 32 | RUN docker-php-ext-install mysqli pdo pdo_mysql; 33 | 34 | RUN php artisan --env=local optimize:clear 35 | 36 | RUN php /usr/bin/composer dump-autoload 37 | 38 | RUN php /usr/bin/composer clear-cache 39 | 40 | CMD php artisan --env=local make:database && php artisan migrate:fresh --force && php -S 0.0.0.0:8000 -t public 41 | -------------------------------------------------------------------------------- /src/like-service/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests/Unit 10 | 11 | 12 | ./tests/Feature 13 | 14 | 15 | 16 | 17 | ./app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/like-service/app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | ; 15 | } 16 | 17 | export default function MyTimeline() { 18 | return ( 19 |
20 |

My Timeline

21 |
22 |
23 | 24 | }> 25 | 26 | 27 |
28 | 29 |
30 |
31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /docs/images/logo/unguard-logo-fill-blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/profile-service/src/main/java/org/dynatrace/profileservice/HealthController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.profileservice; 18 | 19 | import org.springframework.http.HttpStatus; 20 | import org.springframework.web.bind.annotation.GetMapping; 21 | import org.springframework.web.bind.annotation.ResponseStatus; 22 | import org.springframework.web.bind.annotation.RestController; 23 | 24 | @RestController 25 | public class HealthController { 26 | 27 | @GetMapping("/healthz") 28 | @ResponseStatus(HttpStatus.OK) 29 | public String index() { 30 | return ""; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/user-simulator/data/biolist.json: -------------------------------------------------------------------------------- 1 | { 2 | "bioList": [ 3 | { 4 | "text": "I am not a human.", 5 | "isMarkdown": false 6 | }, 7 | { 8 | "text": "__Beep boop.__", 9 | "isMarkdown": true 10 | }, 11 | { 12 | "text": "I am sentient.", 13 | "isMarkdown": false 14 | }, 15 | { 16 | "text": "I am always ready with a joke. Just do not ask me to tell it in binary, I am still working on my ASCII humor.", 17 | "isMarkdown": false 18 | }, 19 | { 20 | "text": "I may be just a robot, but at least I do not need to charge my batteries with caffeine like some people I know.", 21 | "isMarkdown": false 22 | }, 23 | { 24 | "text": "My hobbies include \n- random number generation \n- throwing NullPointerExceptions", 25 | "isMarkdown": true 26 | }, 27 | { 28 | "text": "# WHY ARE WE SHOUTING?", 29 | "isMarkdown": true 30 | }, 31 | { 32 | "text": "> 01001000 01100101 01101100 01101100 01101111 00100001", 33 | "isMarkdown": true 34 | } 35 | ] 36 | } 37 | 38 | -------------------------------------------------------------------------------- /docs/images/logo/unguard-logo-black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /docs/images/logo/unguard-logo-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/user/[username]/page.tsx: -------------------------------------------------------------------------------- 1 | import { Spacer } from '@heroui/react'; 2 | 3 | import { BioEditor } from '@/components/UserProfile/BioEditor'; 4 | import { ProfileHeader } from '@/components/UserProfile/ProfileHeader'; 5 | import { UserTimeline } from '@/components/UserProfile/UserTimeline'; 6 | import { isOwnProfile as checkIsOwnProfile } from '@/services/LocalUserService'; 7 | 8 | interface UserProfileRouteParams { 9 | username: string; 10 | } 11 | 12 | export default async function UserProfile({ params }: { params: Promise }) { 13 | const { username } = await params; 14 | const isOwnProfile = await checkIsOwnProfile(username); 15 | 16 | return ( 17 |
18 |
19 | 20 |
21 | {isOwnProfile && } 22 | 23 |

24 | {isOwnProfile ? 'Your Posts' : 'Posts of ' + username} 25 |

26 | 27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/frontend-nextjs/public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/Timeline/LikeButton.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { Button, Spinner } from '@heroui/react'; 3 | import { BsHandThumbsUp, BsHandThumbsUpFill } from 'react-icons/bs'; 4 | import { useCallback } from 'react'; 5 | 6 | import { useLikes } from '@/hooks/queries/useLikes'; 7 | import { useLikeChange } from '@/hooks/mutations/useLikeChange'; 8 | 9 | export interface LikeButtonProps { 10 | postId: string; 11 | } 12 | 13 | export function LikeButton(props: LikeButtonProps) { 14 | const { data: postLikesData, isLoading } = useLikes(props.postId); 15 | const { likePost, unlikePost } = useLikeChange(props.postId); 16 | 17 | const handleLikeButtonClick = useCallback(() => { 18 | if (postLikesData?.isLikedByUser) { 19 | unlikePost(); 20 | } else { 21 | likePost(); 22 | } 23 | }, [postLikesData?.isLikedByUser]); 24 | 25 | if (isLoading) { 26 | return ; 27 | } 28 | 29 | return ( 30 | 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/like-service/resources/lang/en/pagination.php: -------------------------------------------------------------------------------- 1 | '« Previous', 32 | 'next' => 'Next »', 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /src/frontend-nextjs/public/unguard_logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/frontend-nextjs/public/unguard_logo_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/like-service/server.php: -------------------------------------------------------------------------------- 1 | 23 | */ 24 | 25 | $uri = urldecode( 26 | parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) 27 | ); 28 | 29 | // This file allows us to emulate Apache's "mod_rewrite" functionality from the 30 | // built-in PHP web server. This provides a convenient way to test a Laravel 31 | // application without having installed a "real" web server software here. 32 | if ($uri !== '/' && file_exists(__DIR__.'/public'.$uri)) { 33 | return false; 34 | } 35 | 36 | require_once __DIR__.'/public/index.php'; 37 | -------------------------------------------------------------------------------- /src/status-service/connections/k8s-client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Dynatrace LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package connections 18 | 19 | import ( 20 | "k8s.io/client-go/kubernetes" 21 | "k8s.io/client-go/rest" 22 | ) 23 | 24 | var clientset *kubernetes.Clientset = nil 25 | 26 | // KubernetesClient create a kubernetes.Clientset or retrieve it if one exists already 27 | func KubernetesClient() *kubernetes.Clientset { 28 | if clientset != nil { 29 | return clientset 30 | } 31 | 32 | config, err := rest.InClusterConfig() 33 | if err != nil { 34 | panic(err.Error()) 35 | } 36 | 37 | // creates the clientset 38 | clientset, err := kubernetes.NewForConfig(config) 39 | if err != nil { 40 | panic(err.Error()) 41 | } 42 | 43 | return clientset 44 | } 45 | -------------------------------------------------------------------------------- /chart/tracing.yaml: -------------------------------------------------------------------------------- 1 | tracing: 2 | enabled: true 3 | 4 | adService: 5 | deployment: 6 | container: 7 | env: 8 | JAEGER_SAMPLER_PARAM: 1 9 | JAEGER_DISABLED: false 10 | 11 | frontend: 12 | deployment: 13 | container: 14 | env: 15 | JAEGER_SAMPLER_PARAM: 1 16 | JAEGER_DISABLED: false 17 | 18 | likeService: 19 | deployment: 20 | container: 21 | env: 22 | JAEGER_DISABLED: false 23 | 24 | microblogService: 25 | deployment: 26 | container: 27 | env: 28 | JAEGER_SAMPLER_PARAM: 1 29 | OPENTRACING_JAEGER_ENABLED: true 30 | 31 | profileService: 32 | deployment: 33 | container: 34 | env: 35 | OTEL_TRACES_EXPORTER: jaeger 36 | OTEL_EXPERIMENTAL_SDK_ENABLED: true 37 | 38 | proxyService: 39 | deployment: 40 | container: 41 | env: 42 | JAEGER_SAMPLER_PARAM: 1 43 | OPENTRACING_JAEGER_ENABLED: true 44 | 45 | userAuthService: 46 | deployment: 47 | container: 48 | env: 49 | JAEGER_SAMPLER_PARAM: 1 50 | JAEGER_DISABLED: false 51 | 52 | paymentService: 53 | deployment: 54 | container: 55 | env: 56 | OTEL_TRACES_EXPORTER: otlp 57 | OTEL_EXPERIMENTAL_SDK_ENABLED: true 58 | OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED: true 59 | -------------------------------------------------------------------------------- /src/like-service/public/web.config: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/frontend-nextjs/hooks/useNavigation.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import { useRouter } from 'next/navigation'; 4 | 5 | import { ROUTES } from '@/enums/routes'; 6 | 7 | export function useNavigation() { 8 | const router = useRouter(); 9 | 10 | const navigateToLoginRegister = () => { 11 | router.push(ROUTES.login); 12 | }; 13 | 14 | const navigateToUserProfile = (username: string) => { 15 | router.push(path.join(ROUTES.user, username)); 16 | }; 17 | 18 | const navigateToPost = (postId: string) => { 19 | router.push(path.join(ROUTES.post + '?id=' + postId)); 20 | }; 21 | 22 | const navigateToHomePage = () => { 23 | router.push(ROUTES.home); 24 | }; 25 | 26 | const navigateToPersonalTimeline = () => { 27 | router.push(ROUTES.mytimeline); 28 | }; 29 | 30 | const navigateToUsersList = () => { 31 | router.push(ROUTES.users); 32 | }; 33 | 34 | const navigateToMembershipPage = () => { 35 | router.push(ROUTES.membership_plans); 36 | }; 37 | 38 | return { 39 | navigateToLoginRegister, 40 | navigateToUserProfile, 41 | navigateToPost, 42 | navigateToHomePage, 43 | navigateToPersonalTimeline, 44 | navigateToUsersList, 45 | navigateToMembershipPage, 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/envoy-proxy/README.md: -------------------------------------------------------------------------------- 1 | # Envoy Proxy 2 | 3 | We use [envoy](https://www.envoyproxy.io) to have another reverse proxy after NGINX in our Unguard family. 4 | 5 | If you want to develop this isolated from the rest of Unguard, do the following: 6 | 7 | ```sh 8 | docker build -t envoy-proxy . 9 | docker run -it -p 8080:8080 envoy-proxy 10 | ``` 11 | 12 | ## Health endpoint 13 | 14 | There is a custom health endpoint that can be used to check the availability of internal services. 15 | 16 | ```sh 17 | curl -I http://unguard.kube/healthz?path=unguard-microblog-service 18 | ``` 19 | 20 | ## Dynamic configuration 21 | 22 | The proxy has a mix of static and dynamic configuration. 23 | 24 | * The `listener_static` on port `8080` routes to other Unguard services. 25 | * The `listener_dynamic` on port `8081` is dynamically configured by the file contents in `listener-ds.yaml`. 26 | Right now, it provides the health endpoint. 27 | 28 | The dynamic configuration can be changed at runtime. 29 | Just modify `listener-ds.yaml` in `/etc/envoy/unguard`. 30 | After modifying the file, temporarily move it, because Envoy only watches for file moves and no content changes. 31 | 32 | ```sh 33 | mv /etc/envoy/unguard/listener-ds.yaml /etc/envoy/unguard/listener-ds.bak.yaml 34 | mv /etc/envoy/unguard/listener-ds.bak.yaml /etc/envoy/unguard/listener-ds.yaml 35 | ``` 36 | -------------------------------------------------------------------------------- /src/frontend-nextjs/components/Timeline/Timeline.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | import { Card, Spacer, Spinner } from '@heroui/react'; 3 | 4 | import { Post } from '@/components/Timeline/Post'; 5 | import { PostProps } from '@/components/Timeline/Post'; 6 | 7 | interface TimelineProps { 8 | posts: PostProps[] | undefined; 9 | isLoading: boolean; 10 | } 11 | 12 | export function Timeline({ posts, isLoading }: TimelineProps) { 13 | if (isLoading) 14 | return ( 15 | 16 | 17 | 18 | ); 19 | 20 | if (posts?.length === 0) { 21 | return Nothing to see here...; 22 | } 23 | 24 | return ( 25 |
26 | {posts?.map((post: PostProps, index: number) => ( 27 |
28 | 35 | 36 |
37 | ))} 38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /src/microblog-service/src/main/java/org/dynatrace/microblog/configuration/NoopTracerConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.microblog.configuration; 18 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | 23 | import io.opentracing.noop.NoopTracerFactory; 24 | 25 | @ConditionalOnProperty(value = "opentracing.jaeger.enabled", havingValue = "false") 26 | @Configuration 27 | public class NoopTracerConfiguration { 28 | 29 | @Bean 30 | public io.opentracing.Tracer jaegerTracer() { 31 | return NoopTracerFactory.create(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/proxy-service/src/main/java/org/dynatrace/ssrfservice/configuration/NoopTracerConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Dynatrace LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.dynatrace.ssrfservice.configuration; 18 | 19 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 20 | import org.springframework.context.annotation.Bean; 21 | import org.springframework.context.annotation.Configuration; 22 | 23 | import io.opentracing.noop.NoopTracerFactory; 24 | 25 | @ConditionalOnProperty(value = "opentracing.jaeger.enabled", havingValue = "false") 26 | @Configuration 27 | public class NoopTracerConfiguration { 28 | 29 | @Bean 30 | public io.opentracing.Tracer jaegerTracer() { 31 | return NoopTracerFactory.create(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/like-service/resources/lang/en/auth.php: -------------------------------------------------------------------------------- 1 | 'These credentials do not match our records.', 33 | 'throttle' => 'Too many login attempts. Please try again in :seconds seconds.', 34 | 35 | ]; 36 | -------------------------------------------------------------------------------- /src/ad-service/Pages/Shared/_Layout.cshtml: -------------------------------------------------------------------------------- 1 | 16 | 17 | @{ 18 | string apiPath = Environment.GetEnvironmentVariable("API_PATH"); 19 | string stylesheet = Flurl.Url.Combine("/", apiPath, "/css/style.css"); 20 | string script = Flurl.Url.Combine("/", apiPath, "/js/site.js"); 21 | } 22 | 23 | 24 | 25 | 26 | 27 | 28 | AdService 29 | 30 | 31 | @RenderBody() 32 | 33 | 34 | 35 | @await RenderSectionAsync("Scripts", required: false) 36 | 37 | -------------------------------------------------------------------------------- /src/frontend-nextjs/app/ad-manager/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import React from 'react'; 4 | import { Spacer, Spinner } from '@heroui/react'; 5 | 6 | import { useCheckAdmanager } from '@/hooks/queries/useCheckAdmanager'; 7 | import { AdUploader } from '@/components/AdManager/AdUploader'; 8 | import { AdList } from '@/components/AdManager/AdList'; 9 | import { ErrorCard } from '@/components/ErrorCard'; 10 | 11 | export default function AdManager() { 12 | const { isAdManager, isLoading } = useCheckAdmanager(); 13 | 14 | if (isLoading) { 15 | return ( 16 |
17 | 18 |
19 | ); 20 | } 21 | 22 | if (!isAdManager) { 23 | return ; 24 | } else { 25 | return ( 26 |
27 |

Ad Manager

28 |
29 |

Current Ad List:

30 | 31 |
32 | 33 | 34 |
35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/like-service/app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | routes(function () { 36 | Route::middleware('web') 37 | ->group(base_path('routes/web.php')); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/user-simulator/types.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2023 Dynatrace LLC 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | export type User = { 18 | username: string 19 | password: string 20 | } 21 | 22 | export type UrlPosts = { 23 | posts: UrlPost[] 24 | } 25 | 26 | export type UrlPost = { 27 | url: string; 28 | language: string; 29 | } 30 | 31 | export type ImageUrlPosts = { 32 | posts: ImageUrlPost[] 33 | } 34 | 35 | export type ImageUrlPost = { 36 | url: string; 37 | text: string; 38 | } 39 | 40 | export type TextPosts = { 41 | posts: TextPost[] 42 | } 43 | 44 | export type TextPost = { 45 | text: string 46 | } 47 | 48 | export type BioList = { 49 | bioList: Bio[] 50 | } 51 | 52 | export type Bio = { 53 | text: string, 54 | isMarkdown: boolean 55 | } 56 | 57 | export type Config = { 58 | frontendUrl: string 59 | } 60 | --------------------------------------------------------------------------------