Repository: projectdiscovery/katana Branch: dev Commit: 2f9ecf4211b1 Files: 156 Total size: 1.2 MB Directory structure: gitextract_rfj92yh6/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── README.md │ │ ├── config.yml │ │ ├── feature_request.md.disabled │ │ └── issue-report.md.disabled │ ├── MAINTAINER_GUIDE.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ ├── release.yml │ └── workflows/ │ ├── build-test.yml │ ├── codeql-analysis.yml │ ├── compat-checks.yaml │ ├── dep-auto-merge.yml │ ├── discussion-triage.yml │ ├── dockerhub-push.yml │ ├── functional-test.yml │ ├── release-binary.yml │ ├── release-test.yml │ ├── security-crawl-maze-score.yaml │ ├── stale.yml │ └── workflow-monitor.yml ├── .gitignore ├── .goreleaser/ │ ├── linux.yml │ ├── mac.yml │ └── windows.yml ├── Dockerfile ├── LICENSE.md ├── Makefile ├── README.md ├── SECURITY.md ├── cmd/ │ ├── functional-test/ │ │ ├── main.go │ │ └── run.sh │ ├── integration-test/ │ │ ├── filters.go │ │ ├── integration-test.go │ │ └── library.go │ └── tools/ │ └── crawl-maze-score/ │ └── main.go ├── go.mod ├── go.sum ├── integration_tests/ │ └── run.sh ├── internal/ │ ├── runner/ │ │ ├── banner.go │ │ ├── executer.go │ │ ├── healthcheck.go │ │ ├── options.go │ │ └── runner.go │ └── testutils/ │ ├── helper.go │ ├── integration.go │ └── testutils.go └── pkg/ ├── engine/ │ ├── common/ │ │ ├── base.go │ │ ├── error.go │ │ └── http.go │ ├── engine.go │ ├── headless/ │ │ ├── TODOS.md │ │ ├── browser/ │ │ │ ├── browser.go │ │ │ ├── cookie/ │ │ │ │ ├── cookie.go │ │ │ │ ├── cookie_test.go │ │ │ │ └── rules.json │ │ │ ├── element.go │ │ │ └── stealth/ │ │ │ └── assets.go │ │ ├── captcha/ │ │ │ ├── capsolver/ │ │ │ │ ├── capsolver.go │ │ │ │ └── capsolver_test.go │ │ │ ├── captcha.go │ │ │ ├── helpers_test.go │ │ │ ├── identify.go │ │ │ ├── identify_test.go │ │ │ ├── inject_test.go │ │ │ ├── injection_test.go │ │ │ ├── integration_test.go │ │ │ ├── js/ │ │ │ │ ├── identify.js │ │ │ │ ├── inject-hcaptcha.js │ │ │ │ ├── inject-recaptcha.js │ │ │ │ ├── inject-turnstile.js │ │ │ │ └── js.go │ │ │ ├── solver.go │ │ │ └── solver_test.go │ │ ├── crawler/ │ │ │ ├── crawler.go │ │ │ ├── diagnostics/ │ │ │ │ └── diagnostics.go │ │ │ ├── formfill.go │ │ │ ├── normalizer/ │ │ │ │ ├── dom_utils.go │ │ │ │ ├── dom_utils_test.go │ │ │ │ ├── helpers.go │ │ │ │ ├── normalizer.go │ │ │ │ ├── simhash/ │ │ │ │ │ ├── simhash.go │ │ │ │ │ └── simhash_test.go │ │ │ │ ├── text_utils.go │ │ │ │ └── text_utils_test.go │ │ │ ├── state.go │ │ │ └── state_test.go │ │ ├── debugger.go │ │ ├── graph/ │ │ │ └── graph.go │ │ ├── headless.go │ │ ├── js/ │ │ │ ├── js.go │ │ │ ├── page-init.js │ │ │ └── utils.js │ │ └── types/ │ │ └── types.go │ ├── hybrid/ │ │ ├── crawl.go │ │ ├── doc.go │ │ ├── hijack.go │ │ └── hybrid.go │ ├── parser/ │ │ ├── files/ │ │ │ ├── request.go │ │ │ ├── robotstxt.go │ │ │ ├── robotstxt_test.go │ │ │ ├── sitemapxml.go │ │ │ └── sitemapxml_test.go │ │ ├── parser.go │ │ ├── parser_generic.go │ │ ├── parser_nojs.go │ │ └── parser_test.go │ └── standard/ │ ├── crawl.go │ ├── doc.go │ └── standard.go ├── navigation/ │ ├── request.go │ └── response.go ├── output/ │ ├── custom_field.go │ ├── error.go │ ├── fields.go │ ├── fields_test.go │ ├── file_writer.go │ ├── format_json.go │ ├── format_screen.go │ ├── format_template.go │ ├── options.go │ ├── output.go │ ├── responses.go │ └── result.go ├── types/ │ ├── crawler_options.go │ ├── default.go │ ├── options.go │ └── options_test.go └── utils/ ├── extensions/ │ ├── extensions.go │ └── extensions_test.go ├── filters/ │ ├── filters.go │ ├── filters_test.go │ └── simple.go ├── formfields.go ├── formfields_test.go ├── formfill.go ├── formfill_test.go ├── jsluice.go ├── jsluice_test.go ├── maps.go ├── maps_test.go ├── pathtrie.go ├── pathtrie_test.go ├── queue/ │ ├── priority_queue.go │ ├── priority_queue_test.go │ ├── queue.go │ ├── stack.go │ ├── stack_test.go │ └── strategy.go ├── regex.go ├── regex_test.go ├── scope/ │ ├── scope.go │ └── scope_test.go ├── urlfingerprint.go ├── urlfingerprint_test.go ├── utils.go └── utils_test.go ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/README.md ================================================ # Issue Template Management ## New Issue Workflow (Discussion-First) To improve issue triage and reduce noise from questions being filed as bugs, all issue creation now goes through GitHub Discussions first. ### For Users: **❌ Direct issue creation is disabled** **✅ All reports must start as discussions** Users will be redirected to: - 🐛 **Bug Reports** → [Q&A Discussions](https://github.com/projectdiscovery/katana/discussions/new?category=q-a) - 💡 **Feature Requests** → [Ideas Discussions](https://github.com/projectdiscovery/katana/discussions/new?category=ideas) - ❓ **Questions** → [Q&A Discussions](https://github.com/projectdiscovery/katana/discussions/new?category=q-a) ### For Maintainers: #### Converting Discussions to Issues: 1. **Review the discussion** thoroughly 2. **Determine if it's a valid bug/feature** (not just a question) 3. **Convert to issue** using GitHub's "Convert to Issue" feature: - Go to the discussion - Click "⋯" menu → "Convert to issue" - Add appropriate labels and assignees #### Triage Guidelines: **Convert to Issue:** - ✅ Confirmed bugs with reproduction steps - ✅ Well-defined feature requests with clear use cases - ✅ Security vulnerabilities (after initial assessment) **Keep as Discussion:** - ❌ Usage questions ("How do I...?") - ❌ Configuration help - ❌ Unclear or incomplete bug reports - ❌ Feature ideas that need more discussion/refinement ### Benefits: - 📊 **Better issue quality** - Only confirmed bugs/features become issues - 🎯 **Easier triage** - Questions don't clutter the issue tracker - 💬 **Community involvement** - Discussion encourages collaboration before formal issues - 🧹 **Cleaner issue tracker** - Focus on actionable items only ## Re-enabling Templates (If Needed) If you need to temporarily re-enable direct issue creation: ```bash # Re-enable templates mv issue-report.md.disabled issue-report.md mv feature_request.md.disabled feature_request.md # Update config.yml to add them back ``` ## Template Files: - `config.yml` - Main configuration (redirects all to discussions) - `issue-report.md.disabled` - Bug report template (disabled) - `feature_request.md.disabled` - Feature request template (disabled) ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Report a Bug (Discussion First) url: https://github.com/projectdiscovery/katana/discussions/new?category=q-a about: Report bugs or issues via discussion for proper triage before issue creation - name: Feature Request (Discussion First) url: https://github.com/projectdiscovery/katana/discussions/new?category=ideas about: Share feature ideas via discussion for evaluation before implementation - name: Ask a Question url: https://github.com/projectdiscovery/katana/discussions/new?category=q-a about: Ask questions about usage, configuration, or troubleshooting - name: General Discussion url: https://github.com/projectdiscovery/katana/discussions/new?category=general about: General discussion about the project - name: Security Issues url: mailto:security@projectdiscovery.io about: Report security vulnerabilities privately via email - name: Discord Community url: https://discord.gg/projectdiscovery about: Connect with PD Team and community for real-time discussion ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md.disabled ================================================ --- name: Feature request about: Request feature to implement in this project labels: 'Type: Enhancement' --- ### Please describe your feature request: ### Describe the use case of this feature: ================================================ FILE: .github/ISSUE_TEMPLATE/issue-report.md.disabled ================================================ --- name: Issue report about: Create a report to help us to improve the project labels: 'Type: Bug' --- ### katana version: ### Current Behavior: ### Expected Behavior: ### Steps To Reproduce: ### Anything else: ================================================ FILE: .github/MAINTAINER_GUIDE.md ================================================ # Maintainer Guide: Discussion-First Issue Management ## Overview Katana now uses a **discussion-first approach** for issue management to improve triage quality and reduce noise from questions being filed as bugs. ## How It Works ### 1. **All Reports Start as Discussions** - Users cannot create issues directly - All bug reports → **Q&A Discussions** - All feature requests → **Ideas Discussions** - All questions → **Q&A Discussions** ### 2. **Automated Triage Helper** - Auto-responds to discussions with helpful guidance - Auto-flags potential bugs with keywords detection - Provides checklists for proper bug reporting ### 3. **Maintainer Conversion Process** - Review discussions for completeness - Convert valid issues using GitHub's built-in feature - Apply appropriate labels during conversion ## Conversion Guidelines ### 🐛 **Bug Reports** → Convert to Issue When: **Well-Defined Problems:** - Clear reproduction steps provided - Katana version specified - Expected vs actual behavior described - Environment details included - Error messages/logs included **Confirmed Bugs:** - Issue reproduced by maintainer or community - Not a configuration/usage question - Not working as designed **Keep as Discussion:** - Incomplete information - Usage questions ("How do I...?") - Configuration problems - Working as intended ### 💡 **Feature Requests** → Convert to Issue When: **Solid Proposals:** - Clear use case defined - Benefits to community explained - Implementation approach considered - Not easily achievable with existing features **Community Support:** - Multiple users expressing interest - Maintainer approval for implementation - Fits project roadmap **Keep as Discussion:** - Vague ideas needing refinement - Better suited as external tools/plugins - Conflicts with project goals - Needs more community input ## Conversion Process ### Using GitHub's Convert Feature: 1. **Open the discussion** 2. **Click the "⋯" menu** (top right) 3. **Select "Convert to issue"** 4. **Choose repository** (same repo) 5. **Review title/body** - edit if needed 6. **Add labels:** - `Type: Bug` for confirmed bugs - `Type: Enhancement` for approved features - `Priority: High/Medium/Low` as appropriate - `Component: Engine/Parser/Output` etc. ### Template for Converted Issues: When converting, consider adding this note: ```markdown **Converted from Discussion:** #[discussion_number] [Original discussion content here] --- **Maintainer Notes:** - [ ] Issue confirmed through discussion - [ ] Reproduction steps verified - [ ] Ready for implementation/investigation ``` ## Workflow Benefits ### **For Project Health:** - **Cleaner issue tracker** - Only actionable items - **Better metrics** - Issues vs discussions clearly separated - **Faster resolution** - Less time sorting questions from bugs ### **For Community:** - **Inclusive discussions** - Everyone can participate in triage - **Better help** - Community can answer questions quickly - **Learning opportunity** - Users see resolution process ### **For Maintainers:** - **Pre-filtered issues** - Only valid bugs/features reach issue tracker - **Rich context** - Discussion history provides background - **Community input** - Others help validate before conversion ## Examples ### **Good Bug Discussion → Issue Conversion** **Discussion Title:** "Katana crashes when using -hl with custom headers" **Discussion Body:** - Katana version: v1.2.1 - Command: `katana -u example.com -hl -H "Custom: value"` - Error: panic in hybrid engine - Platform: macOS 14.1 - Reproduction: consistent crash **→ Convert to Issue:** Clear bug with reproduction steps ### **Question → Keep as Discussion** **Discussion Title:** "How to crawl only PDF files?" **Discussion Body:** - New user question - Asking for usage help - Not a bug or feature request **→ Keep as Discussion:** Usage question, answer in discussion ### **Needs More Info → Keep as Discussion** **Discussion Title:** "Katana doesn't work" **Discussion Body:** - Vague description - No version, command, or error details - No reproduction steps **→ Keep as Discussion:** Request more information first ## Quick Reference | Type | Action | Labels for Conversion | |------|---------|-------------------| | **Confirmed Bug** | Convert → Issue | `Type: Bug`, `Priority: [level]` | | **Approved Feature** | Convert → Issue | `Type: Enhancement`, `Priority: [level]` | | **Usage Question** | Keep → Discussion | N/A | | **Needs Info** | Keep → Discussion | N/A | | **Security Issue** | Email → security@projectdiscovery.io | N/A | This workflow ensures high-quality issues while maintaining an inclusive, helpful community environment! ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ## Proposed changes ### Proof ## Checklist - [ ] Pull request is created against the [dev](https://github.com/projectdiscovery/katana/tree/dev) branch - [ ] All checks passed (lint, unit/integration/regression tests etc.) with my changes - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] I have added necessary documentation (if appropriate) ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: # Maintain dependencies for go modules - package-ecosystem: "gomod" directory: "/" schedule: interval: "weekly" target-branch: "dev" commit-message: prefix: "chore" include: "scope" labels: - "Type: Maintenance" allow: - dependency-name: "github.com/projectdiscovery/*" # Maintain dependencies for GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" target-branch: "dev" commit-message: prefix: "chore" include: "scope" labels: - "Type: Maintenance" # Maintain dependencies for docker - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly" target-branch: "dev" commit-message: prefix: "chore" include: "scope" labels: - "Type: Maintenance" ================================================ FILE: .github/release.yml ================================================ changelog: exclude: authors: - app/dependabot - dependabot categories: - title: 🎉 New Features labels: - "Type: Enhancement" - title: 🐞 Bug Fixes labels: - "Type: Bug" - title: 🔨 Maintenance labels: - "Type: Maintenance" - title: Other Changes labels: - "*" ================================================ FILE: .github/workflows/build-test.yml ================================================ name: 🔨 Build Test on: workflow_dispatch: pull_request: branches: - dev paths: - '**.go' - '**.mod' jobs: lint: name: "Lint" if: "${{ !endsWith(github.actor, '[bot]') }}" runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: projectdiscovery/actions/setup/go@v1 - uses: projectdiscovery/actions/golangci-lint/v2@v1 build: name: Test Builds needs: [lint] runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - name: Check out code uses: actions/checkout@v6 - name: Set up Go uses: projectdiscovery/actions/setup/go@v1 - name: Test run: go test ./... working-directory: . - name: Build run: go build . working-directory: cmd/katana/ - name: Integration Tests env: GH_ACTION: true run: bash run.sh working-directory: integration_tests/ - name: Install run: go install working-directory: cmd/katana/ - name: Race Condition Tests run: go build -race . working-directory: cmd/katana/ ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ name: 🚨 CodeQL Analysis on: workflow_dispatch: pull_request: branches: - dev paths: - '**.go' - '**.mod' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'go' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] steps: - name: Checkout repository uses: actions/checkout@v6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} - name: Autobuild uses: github/codeql-action/autobuild@v4 - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 ================================================ FILE: .github/workflows/compat-checks.yaml ================================================ name: ♾️ Compatibility Checks on: pull_request: types: [opened, synchronize] branches: - dev jobs: check: if: github.actor == 'dependabot[bot]' runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v6 - uses: projectdiscovery/actions/setup/go/compat-checks@v1 ================================================ FILE: .github/workflows/dep-auto-merge.yml ================================================ name: 🤖 Auto Merge on: pull_request_review: types: [submitted] workflow_run: workflows: ["♾️ Compatibility Check"] types: - completed permissions: pull-requests: write issues: write repository-projects: write jobs: auto-merge: runs-on: ubuntu-latest if: github.actor == 'dependabot[bot]' steps: - uses: actions/checkout@v6 with: token: ${{ secrets.DEPENDABOT_PAT }} - uses: ahmadnassri/action-dependabot-auto-merge@v2 with: github-token: ${{ secrets.DEPENDABOT_PAT }} target: all ================================================ FILE: .github/workflows/discussion-triage.yml ================================================ name: Discussion Triage Helper on: discussion: types: [created] permissions: discussions: write jobs: triage: runs-on: ubuntu-latest steps: - name: Add initial response to discussions uses: actions/github-script@v8 with: script: | const { discussion } = context.payload; // Add helpful response based on discussion category let response = ""; if (discussion.category.slug === "q-a") { if (discussion.title.toLowerCase().includes("bug") || discussion.title.toLowerCase().includes("error") || discussion.title.toLowerCase().includes("crash") || discussion.title.toLowerCase().includes("fail")) { response = "Thanks for the discussion! This looks like it might be a bug report.\n\n" + "**For Maintainers:** If this is confirmed to be a bug with proper reproduction steps, consider converting to an issue using \"Convert to issue\" in the discussion menu.\n\n" + "**For the Reporter:** To help us triage this effectively, please provide:\n" + "- [ ] Katana version (`katana -version`)\n" + "- [ ] Complete command that reproduces the issue\n" + "- [ ] Expected vs actual behavior\n" + "- [ ] Operating system and environment details\n" + "- [ ] Any error messages or logs\n\n" + "This helps us determine if this should become a tracked issue. Thank you!"; } else { response = "Thanks for your question!\n\n" + "The community and maintainers will help answer this. If this turns out to be a bug or feature request after discussion, a maintainer can convert it to a tracked issue.\n\n" + "**Tip:** For faster responses, you can also join our [Discord server](https://discord.gg/projectdiscovery) for real-time help!"; } } else if (discussion.category.slug === "ideas") { response = "Thanks for sharing this idea!\n\n" + "**For Maintainers:** If this feature request is well-defined and approved for implementation, consider converting to an issue using \"Convert to issue\" in the discussion menu.\n\n" + "**For the Reporter:** To help evaluate this feature:\n" + "- [ ] Describe the specific use case this would solve\n" + "- [ ] Explain how this would benefit the katana user community\n" + "- [ ] Consider if this could be implemented as a plugin or external tool\n" + "- [ ] Check if similar functionality already exists\n\n" + "Well-defined features with clear benefits may be converted to tracked issues for implementation. Thanks for contributing!"; } if (response) { await github.rest.discussions.createComment({ owner: context.repo.owner, repo: context.repo.repo, discussion_number: discussion.number, body: response }); } - name: Auto-label potential bug reports uses: actions/github-script@v8 with: script: | const { discussion } = context.payload; const title = discussion.title.toLowerCase(); const body = discussion.body.toLowerCase(); // Check for keywords that suggest this might be a bug const bugKeywords = [ "bug", "error", "crash", "fail", "broken", "issue", "problem", "exception", "panic", "segfault", "hang", "freeze", "incorrect" ]; const hasBugKeywords = bugKeywords.some(keyword => title.includes(keyword) || body.includes(keyword) ); if (hasBugKeywords && discussion.category.slug === "q-a") { // Add a comment suggesting this might need to be converted to an issue const detectedKeywords = bugKeywords.filter(k => title.includes(k) || body.includes(k)); await github.rest.discussions.createComment({ owner: context.repo.owner, repo: context.repo.repo, discussion_number: discussion.number, body: "**Maintainer Note:** This discussion contains potential bug keywords and may need to be converted to a tracked issue after confirmation.\n\n" + "**Keywords detected:** " + detectedKeywords.join(', ') }); } ================================================ FILE: .github/workflows/dockerhub-push.yml ================================================ name: 🌥 Docker Push on: workflow_run: workflows: ["🎉 Release Binary"] types: - completed workflow_dispatch: jobs: docker: runs-on: ubuntu-latest-16-cores steps: - name: Checkout uses: actions/checkout@v6 - name: Get Github tag id: meta run: | curl --silent "https://api.github.com/repos/projectdiscovery/katana/releases/latest" | jq -r .tag_name | xargs -I {} echo TAG={} >> $GITHUB_OUTPUT - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} - name: Build and push uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64 push: true tags: projectdiscovery/katana:latest,projectdiscovery/katana:${{ steps.meta.outputs.TAG }} ================================================ FILE: .github/workflows/functional-test.yml ================================================ name: 🧪 Functional Test on: pull_request: paths: - '**.go' - '**.mod' workflow_dispatch: jobs: functional: name: Functional Test runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-latest, macOS-latest] steps: - name: Check out code uses: actions/checkout@v6 - name: Set up Go uses: projectdiscovery/actions/setup/go@v1 - name: Functional Tests run: | chmod +x run.sh bash run.sh ${{ matrix.os }} working-directory: cmd/functional-test ================================================ FILE: .github/workflows/release-binary.yml ================================================ name: 🎉 Release Binary on: push: tags: - v* workflow_dispatch: jobs: build-mac: runs-on: macos-latest steps: - name: "Check out code" uses: actions/checkout@v6 with: fetch-depth: 0 - name: "Set up Go" uses: projectdiscovery/actions/setup/go@v1 - name: "Create release on GitHub" uses: goreleaser/goreleaser-action@v7 with: args: "release -f .goreleaser/mac.yml --clean" version: latest workdir: . env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" build-windows: runs-on: windows-latest-8-cores steps: - name: "Check out code" uses: actions/checkout@v6 with: fetch-depth: 0 - name: "Set up Go" uses: projectdiscovery/actions/setup/go@v1 - name: "Create release on GitHub" uses: goreleaser/goreleaser-action@v7 with: args: "release -f .goreleaser/windows.yml --clean" version: latest workdir: . env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" build-linux: runs-on: ubuntu-latest-16-cores steps: - name: "Check out code" uses: actions/checkout@v6 with: fetch-depth: 0 - name: "Set up Go" uses: projectdiscovery/actions/setup/go@v1 # todo: musl compatible? - name: Install Dependences run: sudo apt install gcc-aarch64-linux-gnu - name: "Create release on GitHub" uses: goreleaser/goreleaser-action@v7 with: args: "release -f .goreleaser/linux.yml --clean" version: latest workdir: . env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" SLACK_WEBHOOK: "${{ secrets.RELEASE_SLACK_WEBHOOK }}" DISCORD_WEBHOOK_ID: "${{ secrets.DISCORD_WEBHOOK_ID }}" DISCORD_WEBHOOK_TOKEN: "${{ secrets.DISCORD_WEBHOOK_TOKEN }}" ================================================ FILE: .github/workflows/release-test.yml ================================================ name: 🔨 Release Test on: pull_request: paths: - "**.yml" - "**.go" - "**.mod" workflow_dispatch: jobs: release-test-mac: runs-on: macos-latest steps: - name: "Check out code" uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go uses: projectdiscovery/actions/setup/go@v1 - name: release test uses: projectdiscovery/actions/goreleaser@v1 with: args: --config=.goreleaser/mac.yml workdir: . env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" release-test-linux: runs-on: ubuntu-latest-16-cores steps: - name: "Check out code" uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go uses: projectdiscovery/actions/setup/go@v1 # todo: musl compatible? - name: Install Dependences run: sudo apt update && sudo apt install gcc-aarch64-linux-gnu - name: release test uses: projectdiscovery/actions/goreleaser@v1 with: args: --config=.goreleaser/linux.yml workdir: . env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" release-test-windows: runs-on: windows-latest-8-cores steps: - name: "Check out code" uses: actions/checkout@v6 with: fetch-depth: 0 - name: Set up Go uses: projectdiscovery/actions/setup/go@v1 - name: release test uses: projectdiscovery/actions/goreleaser@v1 with: args: --config=.goreleaser/windows.yml workdir: . env: GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" ================================================ FILE: .github/workflows/security-crawl-maze-score.yaml ================================================ name: 🙏🏻 Security Crawl Maze Score on: workflow_dispatch: jobs: build: name: Run Scoring runs-on: ubuntu-latest-16-cores steps: - name: Check out code uses: actions/checkout@v6 - name: Set up Go uses: projectdiscovery/actions/setup/go@v1 - name: Build run: go build . working-directory: cmd/katana/ - name: Run Katana Standard run: ./katana -u https://security-crawl-maze.app/ -kf all -jc -jsluice -d 10 -o output_standard.txt -cos node_modules working-directory: cmd/katana - name: Run Katana Headless run: ./katana -u https://security-crawl-maze.app/ -kf all -jc -jsluice -d 10 -headless -o output_headless.txt -cos node_modules working-directory: cmd/katana - name: Run Score run: go build .; ./crawl-maze-score ../../katana/output_standard.txt ../../katana/output_headless.txt working-directory: cmd/tools/crawl-maze-score ================================================ FILE: .github/workflows/stale.yml ================================================ name: 💤 Stale on: schedule: - cron: '0 0 * * 0' # Weekly jobs: stale: runs-on: ubuntu-latest permissions: actions: write contents: write # only for delete-branch option issues: write pull-requests: write steps: - uses: actions/stale@v10 with: days-before-stale: 90 days-before-close: 7 stale-issue-label: "Status: Stale" stale-pr-label: "Status: Stale" stale-issue-message: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! stale-pr-message: > This pull request has been automatically marked as stale due to inactivity. It will be closed in 7 days if no further activity occurs. Please update if you wish to keep it open. close-issue-message: > This issue has been automatically closed due to inactivity. If you think this is a mistake or would like to continue the discussion, please comment or feel free to reopen it. close-pr-message: > This pull request has been automatically closed due to inactivity. If you think this is a mistake or would like to continue working on it, please comment or feel free to reopen it. close-issue-label: "Status: Abandoned" close-pr-label: "Status: Abandoned" exempt-issue-labels: "Status: Abandoned" exempt-pr-labels: "Status: Abandoned" ================================================ FILE: .github/workflows/workflow-monitor.yml ================================================ name: Workflow Monitor on: schedule: # Run weekly on Mondays at 9 AM UTC - cron: '0 9 * * 1' workflow_dispatch: # Allow manual triggering permissions: issues: read discussions: read jobs: monitor-workflow: runs-on: ubuntu-latest steps: - name: Check Discussion vs Issue Activity uses: actions/github-script@v8 with: script: | const oneWeekAgo = new Date(); oneWeekAgo.setDate(oneWeekAgo.getDate() - 7); const since = oneWeekAgo.toISOString(); // Get discussions created in the last week const discussions = await github.graphql(` query($owner: String!, $repo: String!, $since: DateTime!) { repository(owner: $owner, name: $repo) { discussions(first: 100, orderBy: {field: CREATED_AT, direction: DESC}) { nodes { title createdAt category { name slug } comments(first: 1) { totalCount } } } } } `, { owner: context.repo.owner, repo: context.repo.repo, since: since }); // Get issues created in the last week const issues = await github.rest.issues.listForRepo({ owner: context.repo.owner, repo: context.repo.repo, since: since, state: 'all' }); // Filter discussions created in the last week const recentDiscussions = discussions.repository.discussions.nodes.filter( d => new Date(d.createdAt) >= oneWeekAgo ); // Generate and log simple report console.log("=== Discussion-First Workflow Report ==="); console.log(`Period: ${oneWeekAgo.toDateString()} - ${new Date().toDateString()}`); console.log(`Discussions Created: ${recentDiscussions.length}`); console.log(`Issues Created: ${issues.data.length}`); console.log(`Conversion Ratio: ${issues.data.length}/${recentDiscussions.length}`); if (recentDiscussions.length > 0) { console.log("\nDiscussion Categories:"); recentDiscussions.forEach(d => { console.log(`- ${d.category.name}: ${d.title} (${d.comments.totalCount} comments)`); }); } else { console.log("No discussions this week"); } if (issues.data.length > 0) { console.log("\nDirect Issues Created:"); issues.data.forEach(i => { console.log(`- ${i.title} (${new Date(i.created_at).toDateString()})`); }); console.log("\nWARNING: Direct issues were created - check workflow configuration"); } else { console.log("\nNo direct issues created - workflow working correctly"); } const responseRate = recentDiscussions.length > 0 ? (recentDiscussions.filter(d => d.comments.totalCount > 0).length / recentDiscussions.length * 100).toFixed(1) : 0; console.log(`Community Response Rate: ${responseRate}%`); // You could also create an issue or discussion with this report // For now, just log it to Actions output ================================================ FILE: .gitignore ================================================ security-crawl-maze/ cmd/katana/katana katana *.exe katana_*/ katana_*/ dist/ .vscode .devcontainer .DS_Store .idea ================================================ FILE: .goreleaser/linux.yml ================================================ version: 2 env: - GO111MODULE=on before: hooks: - go mod tidy project_name: katana builds: - id: katana-linux-amd64-generic ldflags: - -s -w binary: katana env: - CGO_ENABLED=1 main: ./cmd/katana/main.go goos: - linux goarch: - amd64 - id: katana-linux-i386-generic ldflags: - -s -w binary: katana main: ./cmd/katana/main.go goos: - linux goarch: - 386 - id: katana-linux-arm ldflags: - -s -w binary: katana env: - CGO_ENABLED=1 - CC=aarch64-linux-gnu-gcc main: ./cmd/katana/main.go goos: - linux goarch: - arm64 archives: - formats: zip checksum: name_template: "{{ .ProjectName }}-linux-checksums.txt" announce: slack: enabled: true channel: '#release' username: GoReleaser message_template: 'New Release: {{ .ProjectName }} {{.Tag}} is published! Check it out at {{ .ReleaseURL }}' discord: enabled: true message_template: '**New Release: {{ .ProjectName }} {{.Tag}}** is published! Check it out at {{ .ReleaseURL }}' ================================================ FILE: .goreleaser/mac.yml ================================================ version: 2 env: - GO111MODULE=on before: hooks: - go mod tidy project_name: katana builds: - id: katana-darwin ldflags: - -s -w binary: katana env: - CGO_ENABLED=1 main: ./cmd/katana/main.go goos: - darwin goarch: - amd64 - arm64 - 386 - arm archives: - formats: zip name_template: '{{ .ProjectName }}_{{ .Version }}_{{ if eq .Os "darwin" }}macOS{{ else }}{{ .Os }}{{ end }}_{{ .Arch }}' checksum: name_template: "{{ .ProjectName }}-mac-checksums.txt" ================================================ FILE: .goreleaser/windows.yml ================================================ version: 2 env: - GO111MODULE=on before: hooks: - go mod tidy project_name: katana builds: - id: katana-windows ldflags: - -s -w binary: katana env: - CGO_ENABLED=1 main: ./cmd/katana/main.go goos: - windows goarch: - 386 - arm64 - amd64 archives: - formats: zip checksum: name_template: "{{ .ProjectName }}-windows-checksums.txt" ================================================ FILE: Dockerfile ================================================ FROM golang:1.25.7-alpine AS build-env RUN apk add --no-cache git gcc musl-dev WORKDIR /app COPY . /app RUN go mod download RUN go build ./cmd/katana FROM alpine:3.23.2 RUN apk add --no-cache bind-tools ca-certificates chromium COPY --from=build-env /app/katana /usr/local/bin/ ENTRYPOINT ["katana"] ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) 2022 ProjectDiscovery Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ # Go parameters GOCMD=go GOBUILD=$(GOCMD) build GOMOD=$(GOCMD) mod GOTEST=$(GOCMD) test GOFLAGS := -v # This should be disabled if the binary uses pprof LDFLAGS := -s -w ifneq ($(shell go env GOOS),darwin) LDFLAGS := -extldflags "-static" endif all: build build: $(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "katana" cmd/katana/main.go test: $(GOTEST) $(GOFLAGS) ./... integration: cd integration_tests; bash run.sh cd .. tidy: $(GOMOD) tidy lint: golangci-lint run ./... ================================================ FILE: README.md ================================================

katana

A next-generation crawling and spidering framework

FeaturesInstallationUsageScopeConfigFiltersJoin Discord

# Features ![image](https://user-images.githubusercontent.com/8293321/199371558-daba03b6-bf9c-4883-8506-76497c6c3a44.png) - Fast And fully configurable web crawling - **Standard** and **Headless** mode - **JavaScript** parsing / crawling - Customizable **automatic form filling** - **Scope control** - Preconfigured field / Regex - **Customizable output** - Preconfigured fields - INPUT - **STDIN**, **URL** and **LIST** - OUTPUT - **STDOUT**, **FILE** and **JSON** ## Installation katana requires Go 1.25+ to install successfully. If you encounter any installation issues, we recommend trying with the latest available version of Go, as the minimum required version may have changed. Run the command below or download a pre-compiled binary from the [release page](https://github.com/projectdiscovery/katana/releases). ```console CGO_ENABLED=1 go install github.com/projectdiscovery/katana/cmd/katana@latest ``` **More options to install / run katana-**
Docker > To install / update docker to latest tag - ```sh docker pull projectdiscovery/katana:latest ``` > To run katana in standard mode using docker - ```sh docker run projectdiscovery/katana:latest -u https://tesla.com ``` > To run katana in headless mode using docker - ```sh docker run projectdiscovery/katana:latest -u https://tesla.com -system-chrome -headless ```
Ubuntu > It's recommended to install the following prerequisites - ```sh sudo apt update sudo snap refresh sudo apt install zip curl wget git sudo snap install golang --classic wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' sudo apt update sudo apt install google-chrome-stable ``` > install katana - ```sh go install github.com/projectdiscovery/katana/cmd/katana@latest ```
## Usage ```console katana -h ``` This will display help for the tool. Here are all the switches it supports. ```console Katana is a fast crawler focused on execution in automation pipelines offering both headless and non-headless crawling. Usage: ./katana [flags] Flags: INPUT: -u, -list string[] target url / list to crawl -resume string resume scan using resume.cfg -e, -exclude string[] exclude host matching specified filter ('cdn', 'private-ips', cidr, ip, regex) CONFIGURATION: -r, -resolvers string[] list of custom resolver (file or comma separated) -d, -depth int maximum depth to crawl (default 3) -jc, -js-crawl enable endpoint parsing / crawling in javascript file -jsl, -jsluice enable jsluice parsing in javascript file (memory intensive) -ct, -crawl-duration value maximum duration to crawl the target for (s, m, h, d) (default s) -kf, -known-files string enable crawling of known files (all,robotstxt,sitemapxml), a minimum depth of 3 is required to ensure all known files are properly crawled. -mrs, -max-response-size int maximum response size to read (default 4194304) -timeout int time to wait for request in seconds (default 10) -aff, -automatic-form-fill enable automatic form filling (experimental) -fx, -form-extraction extract form, input, textarea & select elements in jsonl output -retry int number of times to retry the request (default 1) -proxy string http/socks5 proxy to use -td, -tech-detect enable technology detection -H, -headers string[] custom header/cookie to include in all http request in header:value format (file) -config string path to the katana configuration file -fc, -form-config string path to custom form configuration file -flc, -field-config string path to custom field configuration file -s, -strategy string Visit strategy (depth-first, breadth-first) (default "depth-first") -iqp, -ignore-query-params Ignore crawling same path with different query-param values -fsu, -filter-similar filter crawling of similar looking URLs (e.g., /users/123 and /users/456) -fst, -filter-similar-threshold int number of distinct values before a path position is treated as parameter (default 10) -tlsi, -tls-impersonate enable experimental client hello (ja3) tls randomization -dr, -disable-redirects disable following redirects (default false) -kb, -knowledge-base enable knowledge base classification DEBUG: -health-check, -hc run diagnostic check up -elog, -error-log string file to write sent requests error log -pprof-server enable pprof server HEADLESS: -hl, -headless enable headless hybrid crawling (experimental) -sc, -system-chrome use local installed chrome browser instead of katana installed -sb, -show-browser show the browser on the screen with headless mode -ho, -headless-options string[] start headless chrome with additional options -nos, -no-sandbox start headless chrome in --no-sandbox mode -cdd, -chrome-data-dir string path to store chrome browser data -scp, -system-chrome-path string use specified chrome browser for headless crawling -noi, -no-incognito start headless chrome without incognito mode -cwu, -chrome-ws-url string use chrome browser instance launched elsewhere with the debugger listening at this URL -xhr, -xhr-extraction extract xhr request url,method in jsonl output -csp, -captcha-solver-provider string captcha solver provider (e.g. capsolver) -csk, -captcha-solver-key string captcha solver provider api key SCOPE: -cs, -crawl-scope string[] in scope url regex to be followed by crawler -cos, -crawl-out-scope string[] out of scope url regex to be excluded by crawler -fs, -field-scope string pre-defined scope field (dn,rdn,fqdn) or custom regex (e.g., '(company-staging.io|company.com)') (default "rdn") -ns, -no-scope disables host based default scope -do, -display-out-scope display external endpoint from scoped crawling FILTER: -mr, -match-regex string[] regex or list of regex to match on output url (cli, file) -fr, -filter-regex string[] regex or list of regex to filter on output url (cli, file) -f, -field string field to display in output (url,path,fqdn,rdn,rurl,qurl,qpath,file,ufile,key,value,kv,dir,udir) (Deprecated: use -output-template instead) -sf, -store-field string field to store in per-host output (url,path,fqdn,rdn,rurl,qurl,qpath,file,ufile,key,value,kv,dir,udir) -em, -extension-match string[] match output for given extension (eg, -em php,html,js) -ef, -extension-filter string[] filter output for given extension (eg, -ef png,css) -ndef, -no-default-ext-filter bool remove default extensions from the filter list -mdc, -match-condition string match response with dsl based condition -fdc, -filter-condition string filter response with dsl based condition -duf, -disable-unique-filter disable duplicate content filtering -fpt, -filter-page-type string[] filter response with page type (e.g. error,captcha,parked) RATE-LIMIT: -c, -concurrency int number of concurrent fetchers to use (default 10) -p, -parallelism int number of concurrent inputs to process (default 10) -rd, -delay int request delay between each request in seconds -rl, -rate-limit int maximum requests to send per second (default 150) -rlm, -rate-limit-minute int maximum number of requests to send per minute UPDATE: -up, -update update katana to latest version -duc, -disable-update-check disable automatic katana update check OUTPUT: -o, -output string file to write output to -ot, -output-template string custom output template -sr, -store-response store http requests/responses -srd, -store-response-dir string store http requests/responses to custom directory -ncb, -no-clobber do not overwrite output file -sfd, -store-field-dir string store per-host field to custom directory -or, -omit-raw omit raw requests/responses from jsonl output -ob, -omit-body omit response body from jsonl output -lof, -list-output-fields list available fields for jsonl output format -eof, -exclude-output-fields exclude fields from jsonl output -j, -jsonl write output in jsonl format -nc, -no-color disable output content coloring (ANSI escape codes) -silent display output only -v, -verbose display verbose output -debug display debug output -version display project version ``` ## Running Katana ### Input for katana **katana** requires **url** or **endpoint** to crawl and accepts single or multiple inputs. Input URL can be provided using `-u` option, and multiple values can be provided using comma-separated input, similarly **file** input is supported using `-list` option and additionally piped input (stdin) is also supported. #### URL Input ```sh katana -u https://tesla.com ``` #### Multiple URL Input (comma-separated) ```sh katana -u https://tesla.com,https://google.com ``` #### List Input ```bash $ cat url_list.txt https://tesla.com https://google.com ``` ``` katana -list url_list.txt ``` #### STDIN (piped) Input ```sh echo https://tesla.com | katana ``` ```sh cat domains | httpx | katana ``` Example running katana - ```console katana -u https://youtube.com __ __ / /_____ _/ /____ ____ ___ _ / '_/ _ / __/ _ / _ \/ _ / /_/\_\\_,_/\__/\_,_/_//_/\_,_/ v0.0.1 projectdiscovery.io [WRN] Use with caution. You are responsible for your actions. [WRN] Developers assume no liability and are not responsible for any misuse or damage. https://www.youtube.com/ https://www.youtube.com/about/ https://www.youtube.com/about/press/ https://www.youtube.com/about/copyright/ https://www.youtube.com/t/contact_us/ https://www.youtube.com/creators/ https://www.youtube.com/ads/ https://www.youtube.com/t/terms https://www.youtube.com/t/privacy https://www.youtube.com/about/policies/ https://www.youtube.com/howyoutubeworks?utm_campaign=ytgen&utm_source=ythp&utm_medium=LeftNav&utm_content=txt&u=https%3A%2F%2Fwww.youtube.com%2Fhowyoutubeworks%3Futm_source%3Dythp%26utm_medium%3DLeftNav%26utm_campaign%3Dytgen https://www.youtube.com/new https://m.youtube.com/ https://www.youtube.com/s/desktop/4965577f/jsbin/desktop_polymer.vflset/desktop_polymer.js https://www.youtube.com/s/desktop/4965577f/cssbin/www-main-desktop-home-page-skeleton.css https://www.youtube.com/s/desktop/4965577f/cssbin/www-onepick.css https://www.youtube.com/s/_/ytmainappweb/_/ss/k=ytmainappweb.kevlar_base.0Zo5FUcPkCg.L.B1.O/am=gAE/d=0/rs=AGKMywG5nh5Qp-BGPbOaI1evhF5BVGRZGA https://www.youtube.com/opensearch?locale=en_GB https://www.youtube.com/manifest.webmanifest https://www.youtube.com/s/desktop/4965577f/cssbin/www-main-desktop-watch-page-skeleton.css https://www.youtube.com/s/desktop/4965577f/jsbin/web-animations-next-lite.min.vflset/web-animations-next-lite.min.js https://www.youtube.com/s/desktop/4965577f/jsbin/custom-elements-es5-adapter.vflset/custom-elements-es5-adapter.js https://www.youtube.com/s/desktop/4965577f/jsbin/webcomponents-sd.vflset/webcomponents-sd.js https://www.youtube.com/s/desktop/4965577f/jsbin/intersection-observer.min.vflset/intersection-observer.min.js https://www.youtube.com/s/desktop/4965577f/jsbin/scheduler.vflset/scheduler.js https://www.youtube.com/s/desktop/4965577f/jsbin/www-i18n-constants-en_GB.vflset/www-i18n-constants.js https://www.youtube.com/s/desktop/4965577f/jsbin/www-tampering.vflset/www-tampering.js https://www.youtube.com/s/desktop/4965577f/jsbin/spf.vflset/spf.js https://www.youtube.com/s/desktop/4965577f/jsbin/network.vflset/network.js https://www.youtube.com/howyoutubeworks/ https://www.youtube.com/trends/ https://www.youtube.com/jobs/ https://www.youtube.com/kids/ ``` ## Crawling Mode ### Standard Mode Standard crawling modality uses the standard go http library under the hood to handle HTTP requests/responses. This modality is much faster as it doesn't have the browser overhead. Still, it analyzes HTTP responses body as is, without any javascript or DOM rendering, potentially missing post-dom-rendered endpoints or asynchronous endpoint calls that might happen in complex web applications depending, for example, on browser-specific events. ### Headless Mode Headless mode hooks internal headless calls to handle HTTP requests/responses directly within the browser context. This offers two advantages: - The HTTP fingerprint (TLS and user agent) fully identify the client as a legitimate browser - Better coverage since the endpoints are discovered analyzing the standard raw response, as in the previous modality, and also the browser-rendered one with javascript enabled. Headless crawling is optional and can be enabled using `-headless` option. Here are other headless CLI options - ```console katana -h headless Flags: HEADLESS: -hl, -headless enable headless hybrid crawling (experimental) -sc, -system-chrome use local installed chrome browser instead of katana installed -sb, -show-browser show the browser on the screen with headless mode -ho, -headless-options string[] start headless chrome with additional options -nos, -no-sandbox start headless chrome in --no-sandbox mode -cdd, -chrome-data-dir string path to store chrome browser data -scp, -system-chrome-path string use specified chrome browser for headless crawling -noi, -no-incognito start headless chrome without incognito mode -cwu, -chrome-ws-url string use chrome browser instance launched elsewhere with the debugger listening at this URL -xhr, -xhr-extraction extract xhr requests -csp, -captcha-solver-provider string captcha solver provider (e.g. capsolver) -csk, -captcha-solver-key string captcha solver provider api key ``` *`-no-sandbox`* ---- Runs headless chrome browser with **no-sandbox** option, useful when running as root user. ```console katana -u https://tesla.com -headless -no-sandbox ``` *`-no-incognito`* ---- Runs headless chrome browser without incognito mode, useful when using the local browser. ```console katana -u https://tesla.com -headless -no-incognito ``` *`-headless-options`* ---- When crawling in headless mode, additional chrome options can be specified using `-headless-options`, for example - ```console katana -u https://tesla.com -headless -system-chrome -headless-options --disable-gpu,proxy-server=http://127.0.0.1:8080 ``` ### Captcha Solving Katana supports automatic captcha detection and solving during headless crawling. When a captcha page is encountered, katana identifies the captcha provider, solves it via an external service, and continues crawling. Supported captcha types: **reCAPTCHA v2**, **reCAPTCHA v3**, **reCAPTCHA Enterprise**, **Cloudflare Turnstile**, **hCaptcha** *`-captcha-solver-provider`* ---- Option to specify the captcha solver provider. Currently supported: `capsolver`. *`-captcha-solver-key`* ---- API key for the captcha solver provider. ```console katana -u https://example.com -headless -csp capsolver -csk YOUR_API_KEY ``` The provider and key can also be set via environment variables: ```console export CAPTCHA_SOLVER_PROVIDER=capsolver export CAPTCHA_SOLVER_KEY=YOUR_API_KEY katana -u https://example.com -headless ``` ## Scope Control Crawling can be endless if not scoped, as such katana comes with multiple support to define the crawl scope. *`-field-scope`* ---- Most handy option to define scope with predefined field name, `rdn` being default option for field scope. - `rdn` - crawling scoped to root domain name and all subdomains (e.g. `*example.com`) (default) - `fqdn` - crawling scoped to given sub(domain) (e.g. `www.example.com` or `api.example.com`) - `dn` - crawling scoped to domain name keyword (e.g. `example`) ```console katana -u https://tesla.com -fs dn ``` *`-crawl-scope`* ------ For advanced scope control, `-cs` option can be used that comes with **regex** support. ```console katana -u https://tesla.com -cs login ``` For multiple in scope rules, file input with multiline string / regex can be passed. ```bash $ cat in_scope.txt login/ admin/ app/ wordpress/ ``` ```console katana -u https://tesla.com -cs in_scope.txt ``` *`-crawl-out-scope`* ----- For defining what not to crawl, `-cos` option can be used and also support **regex** input. ```console katana -u https://tesla.com -cos logout ``` For multiple out of scope rules, file input with multiline string / regex can be passed. ```bash $ cat out_of_scope.txt /logout /log_out ``` ```console katana -u https://tesla.com -cos out_of_scope.txt ``` *`-no-scope`* ---- Katana is default to scope `*.domain`, to disable this `-ns` option can be used and also to crawl the internet. ```console katana -u https://tesla.com -ns ``` *`-display-out-scope`* ---- As default, when scope option is used, it also applies for the links to display as output, as such **external URLs are default to exclude** and to overwrite this behavior, `-do` option can be used to display all the external URLs that exist in targets scoped URL / Endpoint. ``` katana -u https://tesla.com -do ``` Here is all the CLI options for the scope control - ```console katana -h scope Flags: SCOPE: -cs, -crawl-scope string[] in scope url regex to be followed by crawler -cos, -crawl-out-scope string[] out of scope url regex to be excluded by crawler -fs, -field-scope string pre-defined scope field (dn,rdn,fqdn) (default "rdn") -ns, -no-scope disables host based default scope -do, -display-out-scope display external endpoint from scoped crawling ``` ## Crawler Configuration Katana comes with multiple options to configure and control the crawl as the way we want. *`-depth`* ---- Option to define the `depth` to follow the urls for crawling, the more depth the more number of endpoint being crawled + time for crawl. ``` katana -u https://tesla.com -d 5 ``` *`-js-crawl`* ---- Option to enable JavaScript file parsing + crawling the endpoints discovered in JavaScript files, disabled as default. ``` katana -u https://tesla.com -jc ``` *`-crawl-duration`* ---- Option to predefined crawl duration, disabled as default. ``` katana -u https://tesla.com -ct 2 ``` *`-known-files`* ---- Option to enable crawling `robots.txt` and `sitemap.xml` file, disabled as default. ``` katana -u https://tesla.com -kf robotstxt,sitemapxml ``` *`-automatic-form-fill`* ---- Option to enable automatic form filling for known / unknown fields, known field values can be customized as needed by updating form config file at `$HOME/.config/katana/form-config.yaml`. Automatic form filling is experimental feature. ``` katana -u https://tesla.com -aff ``` *`-filter-similar`* ---- Option to filter crawling of similar looking URLs by normalizing variable path segments. This detects IDs, UUIDs, hashes, dates, and other dynamic values, and also learns repeating patterns at runtime. For example, `/users/123` and `/users/456` are treated as the same endpoint. ``` katana -u https://tesla.com -fsu ``` The promotion threshold (how many distinct values at a path position before it's treated as a parameter) can be tuned with `-fst`. Lower values are more aggressive (fewer URLs crawled), higher values are more permissive. Default is `10`. ``` katana -u https://tesla.com -fsu -fst 5 ``` ## Authenticated Crawling Authenticated crawling involves including custom headers or cookies in HTTP requests to access protected resources. These headers provide authentication or authorization information, allowing you to crawl authenticated content / endpoint. You can specify headers directly in the command line or provide them as a file with katana to perform authenticated crawling. > **Note**: User needs to be manually perform the authentication and export the session cookie / header to file to use with katana. *`-headers`* ---- Option to add a custom header or cookie to the request. > Syntax of [headers](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2) in the HTTP specification Here is an example of adding a cookie to the request: ``` katana -u https://tesla.com -H 'Cookie: usrsess=AmljNrESo' ``` It is also possible to supply headers or cookies as a file. For example: ``` $ cat cookie.txt Cookie: PHPSESSIONID=XXXXXXXXX X-API-KEY: XXXXX TOKEN=XX ``` ``` katana -u https://tesla.com -H cookie.txt ``` There are more options to configure when needed, here is all the config related CLI options - ```console katana -h config Flags: CONFIGURATION: -r, -resolvers string[] list of custom resolver (file or comma separated) -d, -depth int maximum depth to crawl (default 3) -jc, -js-crawl enable endpoint parsing / crawling in javascript file -ct, -crawl-duration int maximum duration to crawl the target for -kf, -known-files string enable crawling of known files (all,robotstxt,sitemapxml) -mrs, -max-response-size int maximum response size to read (default 9223372036854775807) -timeout int time to wait for request in seconds (default 10) -aff, -automatic-form-fill enable automatic form filling (experimental) -fx, -form-extraction enable extraction of form, input, textarea & select elements -retry int number of times to retry the request (default 1) -proxy string http/socks5 proxy to use -H, -headers string[] custom header/cookie to include in request -config string path to the katana configuration file -fc, -form-config string path to custom form configuration file -flc, -field-config string path to custom field configuration file -s, -strategy string Visit strategy (depth-first, breadth-first) (default "depth-first") -iqp, -ignore-query-params Ignore crawling same path with different query-param values -fsu, -filter-similar filter crawling of similar looking URLs (e.g., /users/123 and /users/456) -fst, -filter-similar-threshold int number of distinct values before a path position is treated as parameter (default 10) ``` ### Connecting to Active Browser Session Katana can also connect to active browser session where user is already logged in and authenticated. and use it for crawling. The only requirement for this is to start browser with remote debugging enabled. Here is an example of starting chrome browser with remote debugging enabled and using it with katana - **step 1) First Locate path of chrome executable** | Operating System | Chromium Executable Location | Google Chrome Executable Location | |------------------|------------------------------|-----------------------------------| | Windows (64-bit) | `C:\Program Files (x86)\Google\Chromium\Application\chrome.exe` | `C:\Program Files (x86)\Google\Chrome\Application\chrome.exe` | | Windows (32-bit) | `C:\Program Files\Google\Chromium\Application\chrome.exe` | `C:\Program Files\Google\Chrome\Application\chrome.exe` | | macOS | `/Applications/Chromium.app/Contents/MacOS/Chromium` | `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome` | | Linux | `/usr/bin/chromium` | `/usr/bin/google-chrome` | **step 2) Start chrome with remote debugging enabled and it will return websocker url. For example, on MacOS, you can start chrome with remote debugging enabled using following command** - ```console $ /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 DevTools listening on ws://127.0.0.1:9222/devtools/browser/c5316c9c-19d6-42dc-847a-41d1aeebf7d6 ``` > Now login to the website you want to crawl and keep the browser open. **step 3) Now use the websocket url with katana to connect to the active browser session and crawl the website** ```console katana -headless -u https://tesla.com -cwu ws://127.0.0.1:9222/devtools/browser/c5316c9c-19d6-42dc-847a-41d1aeebf7d6 -no-incognito ``` > **Note**: you can use `-cdd` option to specify custom chrome data directory to store browser data and cookies but that does not save session data if cookie is set to `Session` only or expires after certain time. ## Filters *`-field`* ---- > [!WARNING] > Deprecated: use [**`-output-template`**](#-output-template) instead. The field flag is still supported for backward compatibility. Katana comes with built in fields that can be used to filter the output for the desired information, `-f` option can be used to specify any of the available fields. ``` -f, -field string field to display in output (url,path,fqdn,rdn,rurl,qurl,qpath,file,key,value,kv,dir,udir) ``` Here is a table with examples of each field and expected output when used - | FIELD | DESCRIPTION | EXAMPLE | | ------- | --------------------------- | ------------------------------------------------------------ | | `url` | URL Endpoint | `https://admin.projectdiscovery.io/admin/login?user=admin&password=admin` | | `qurl` | URL including query param | `https://admin.projectdiscovery.io/admin/login.php?user=admin&password=admin` | | `qpath` | Path including query param | `/login?user=admin&password=admin` | | `path` | URL Path | `https://admin.projectdiscovery.io/admin/login` | | `fqdn` | Fully Qualified Domain name | `admin.projectdiscovery.io` | | `rdn` | Root Domain name | `projectdiscovery.io` | | `rurl` | Root URL | `https://admin.projectdiscovery.io` | | `ufile` | URL with File | `https://admin.projectdiscovery.io/login.js` | | `file` | Filename in URL | `login.php` | | `key` | Parameter keys in URL | `user,password` | | `value` | Parameter values in URL | `admin,admin` | | `kv` | Keys=Values in URL | `user=admin&password=admin` | | `dir` | URL Directory name | `/admin/` | | `udir` | URL with Directory | `https://admin.projectdiscovery.io/admin/` | Here is an example of using field option to only display all the urls with query parameter in it - ``` katana -u https://tesla.com -f qurl -silent https://shop.tesla.com/en_au?redirect=no https://shop.tesla.com/en_nz?redirect=no https://shop.tesla.com/product/men_s-raven-lightweight-zip-up-bomber-jacket?sku=1740250-00-A https://shop.tesla.com/product/tesla-shop-gift-card?sku=1767247-00-A https://shop.tesla.com/product/men_s-chill-crew-neck-sweatshirt?sku=1740176-00-A https://www.tesla.com/about?redirect=no https://www.tesla.com/about/legal?redirect=no https://www.tesla.com/findus/list?redirect=no ``` ### Custom Fields You can create custom fields to extract and store specific information from page responses using regex rules. These custom fields are defined using a YAML config file and are loaded from the default location at `$HOME/.config/katana/field-config.yaml`. Alternatively, you can use the `-flc` option to load a custom field config file from a different location. Here is example custom field. ```yaml - name: email type: regex regex: - '([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)' - '([a-zA-Z0-9+._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)' - name: phone type: regex regex: - '\d{3}-\d{8}|\d{4}-\d{7}' ``` When defining custom fields, following attributes are supported: - **name** (required) > The value of **name** attribute is used as the `-field` cli option value. - **type** (required) > The type of custom attribute, currently supported option - `regex` - **part** (optional) > The part of the response to extract the information from. The default value is `response`, which includes both the header and body. Other possible values are `header` and `body`. - group (optional) > You can use this attribute to select a specific matched group in regex, for example: `group: 1` #### Running katana using custom field: ```console katana -u https://tesla.com -f email,phone ``` *`-store-field`* --- To compliment `field` option which is useful to filter output at run time, there is `-sf, -store-fields` option which works exactly like field option except instead of filtering, it stores all the information on the disk under `katana_field` directory sorted by target url. Use `-sfd` or `-store-field-dir` to store data in a different location. ``` katana -u https://tesla.com -sf key,fqdn,qurl -silent ``` ```bash $ ls katana_field/ https_www.tesla.com_fqdn.txt https_www.tesla.com_key.txt https_www.tesla.com_qurl.txt ``` The `-store-field` option can be useful for collecting information to build a targeted wordlist for various purposes, including but not limited to: - Identifying the most commonly used parameters - Discovering frequently used paths - Finding commonly used files - Identifying related or unknown subdomains ### Katana Filters *`-extension-match`* --- Crawl output can be easily matched for specific extension using `-em` option to ensure to display only output containing given extension. ``` katana -u https://tesla.com -silent -em js,jsp,json ``` *`-extension-filter`* --- Crawl output can be easily filtered for specific extension using `-ef` option which ensure to remove all the urls containing given extension. ``` katana -u https://tesla.com -silent -ef css,txt,md ``` *`-no-default-ext-filter`* --- Katana filters several extensions by default. This can be disabled with the `-ndef` option. ``` katana -u https://tesla.com -silent -ndef ``` *`-match-regex`* --- The `-match-regex` or `-mr` flag allows you to filter output URLs using regular expressions. When using this flag, only URLs that match the specified regular expression will be printed in the output. ``` katana -u https://tesla.com -mr 'https://shop\.tesla\.com/*' -silent ``` *`-filter-regex`* --- The `-filter-regex` or `-fr` flag allows you to filter output URLs using regular expressions. When using this flag, it will skip the URLs that are match the specified regular expression. ``` katana -u https://tesla.com -fr 'https://www\.tesla\.com/*' -silent ``` ### Advance Filtering Katana supports DSL-based expressions for advanced matching and filtering capabilities: - To match endpoints with a 200 status code: ```shell katana -u https://www.hackerone.com -mdc 'status_code == 200' ``` - To match endpoints that contain "default" and have a status code other than 403: ```shell katana -u https://www.hackerone.com -mdc 'contains(endpoint, "default") && status_code != 403' ``` - To match endpoints with PHP technologies: ```shell katana -u https://www.hackerone.com -mdc 'contains(to_lower(technologies), "php")' ``` - To filter out endpoints running on Cloudflare: ```shell katana -u https://www.hackerone.com -fdc 'contains(to_lower(technologies), "cloudflare")' ``` DSL functions can be applied to any keys in the jsonl output. For more information on available DSL functions, please visit the [dsl project](https://github.com/projectdiscovery/dsl). Here are additional filter options - ```console katana -h filter Flags: FILTER: -mr, -match-regex string[] regex or list of regex to match on output url (cli, file) -fr, -filter-regex string[] regex or list of regex to filter on output url (cli, file) -f, -field string field to display in output (url,path,fqdn,rdn,rurl,qurl,qpath,file,ufile,key,value,kv,dir,udir) -sf, -store-field string field to store in per-host output (url,path,fqdn,rdn,rurl,qurl,qpath,file,ufile,key,value,kv,dir,udir) -em, -extension-match string[] match output for given extension (eg, -em php,html,js) -ef, -extension-filter string[] filter output for given extension (eg, -ef png,css) -ndef, -no-default-ext-filter bool remove default extensions from the filter list -mdc, -match-condition string match response with dsl based condition -fdc, -filter-condition string filter response with dsl based condition -duf, -disable-unique-filter disable duplicate content filtering ``` ## Rate Limit It's easy to get blocked / banned while crawling if not following target websites limits, katana comes with multiple option to tune the crawl to go as fast / slow we want. *`-delay`* ----- option to introduce a delay in seconds between each new request katana makes while crawling, disabled as default. ``` katana -u https://tesla.com -delay 20 ``` *`-concurrency`* ----- option to control the number of urls per target to fetch at the same time. ``` katana -u https://tesla.com -c 20 ``` *`-parallelism`* ----- option to define number of target to process at same time from list input. ``` katana -u https://tesla.com -p 20 ``` *`-rate-limit`* ----- option to use to define max number of request can go out per second. ``` katana -u https://tesla.com -rl 100 ``` *`-rate-limit-minute`* ----- option to use to define max number of request can go out per minute. ``` katana -u https://tesla.com -rlm 500 ``` Here is all long / short CLI options for rate limit control - ```console katana -h rate-limit Flags: RATE-LIMIT: -c, -concurrency int number of concurrent fetchers to use (default 10) -p, -parallelism int number of concurrent inputs to process (default 10) -rd, -delay int request delay between each request in seconds -rl, -rate-limit int maximum requests to send per second (default 150) -rlm, -rate-limit-minute int maximum number of requests to send per minute ``` ## Output Katana support both file output in plain text format as well as JSON which includes additional information like, `source`, `tag`, and `attribute` name to co-related the discovered endpoint. *`-output`* --- By default, katana outputs the crawled endpoints in plain text format. The results can be written to a file by using the -output option. ```console katana -u https://example.com -no-scope -output example_endpoints.txt ``` *`-output-template`* --- The `-output-template` option allows you to customize the output format using template, providing flexibility in defining the output structure. This option replaces the deprecated `-field` flag for filtering output. Instead of relying on predefined fields, you can specify a custom template directly in the command line to control how the extracted data is presented. Example of using the `-output-template` option: ```sh katana -u https://example.com -output-template '{{email}} - {{url}}' ``` In this example, `email` represents a [custom field](#custom-fields) that extracts and displays email addresses found within the source `url`. > [!NOTE] > If a specified field does not exist or does not contain a value, it will simply be omitted from the output. This option can effectively structure the output in a way that best suits your use case, making data extraction more intuitive and customizable. *`-jsonl`* --- ```console katana -u https://example.com -jsonl | jq . ``` ```json { "timestamp": "2023-03-20T16:23:58.027559+05:30", "request": { "method": "GET", "endpoint": "https://example.com", "raw": "GET / HTTP/1.1\r\nHost: example.com\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36\r\nAccept-Encoding: gzip\r\n\r\n" }, "response": { "status_code": 200, "headers": { "accept_ranges": "bytes", "expires": "Mon, 27 Mar 2023 10:53:58 GMT", "last_modified": "Thu, 17 Oct 2019 07:18:26 GMT", "content_type": "text/html; charset=UTF-8", "server": "ECS (dcb/7EA3)", "vary": "Accept-Encoding", "etag": "\"3147526947\"", "cache_control": "max-age=604800", "x_cache": "HIT", "date": "Mon, 20 Mar 2023 10:53:58 GMT", "age": "331239" }, "body": "\n\n\n Example Domain\n\n \n \n \n \n\n\n\n
\n

Example Domain

\n

This domain is for use in illustrative examples in documents. You may use this\n domain in literature without prior coordination or asking for permission.

\n

More information...

\n
\n\n\n", "technologies": [ "Azure", "Amazon ECS", "Amazon Web Services", "Docker", "Azure CDN" ], "raw": "HTTP/1.1 200 OK\r\nContent-Length: 1256\r\nAccept-Ranges: bytes\r\nAge: 331239\r\nCache-Control: max-age=604800\r\nContent-Type: text/html; charset=UTF-8\r\nDate: Mon, 20 Mar 2023 10:53:58 GMT\r\nEtag: \"3147526947\"\r\nExpires: Mon, 27 Mar 2023 10:53:58 GMT\r\nLast-Modified: Thu, 17 Oct 2019 07:18:26 GMT\r\nServer: ECS (dcb/7EA3)\r\nVary: Accept-Encoding\r\nX-Cache: HIT\r\n\r\n\n\n\n Example Domain\n\n \n \n \n \n\n\n\n
\n

Example Domain

\n

This domain is for use in illustrative examples in documents. You may use this\n domain in literature without prior coordination or asking for permission.

\n

More information...

\n
\n\n\n" } } ``` *`-store-response`* ---- The `-store-response` option allows for writing all crawled endpoint requests and responses to a text file. When this option is used, text files including the request and response will be written to the **katana_response** directory. If you would like to specify a custom directory, you can use the `-store-response-dir` option. ```console katana -u https://example.com -no-scope -store-response ``` ```bash $ cat katana_response/index.txt katana_response/example.com/327c3fda87ce286848a574982ddd0b7c7487f816.txt https://example.com (200 OK) katana_response/www.iana.org/bfc096e6dd93b993ca8918bf4c08fdc707a70723.txt http://www.iana.org/domains/reserved (200 OK) ``` **Note:** *`-store-response` option is not supported in `-headless` mode.* *`-list-output-fields`* ---- The `-list-output-fields` or `-lof` flag displays all available fields that can be used in JSONL output format. This is useful for understanding what data is available when using custom output templates or when excluding specific fields. ```console katana -lof ``` *`-exclude-output-fields`* ---- The `-exclude-output-fields` or `-eof` flag allows you to exclude specific fields from the JSONL output. This is useful for reducing output size or focusing on specific data by removing unwanted fields. ```console katana -u https://example.com -jsonl -eof raw,body ``` Here are additional CLI options related to output - ```console katana -h output OUTPUT: -o, -output string file to write output to -sr, -store-response store http requests/responses -srd, -store-response-dir string store http requests/responses to custom directory -lof, -list-output-fields list available fields for jsonl output format -eof, -exclude-output-fields exclude fields from jsonl output -j, -json write output in JSONL(ines) format -nc, -no-color disable output content coloring (ANSI escape codes) -silent display output only -v, -verbose display verbose output -version display project version ``` ## Katana as a library `katana` can be used as a library by creating an instance of the `Option` struct and populating it with the same options that would be specified via CLI. Using the options you can create `crawlerOptions` and so standard or hybrid `crawler`. `crawler.Crawl` method should be called to crawl the input. ```go package main import ( "math" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/engine/standard" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/types" ) func main() { options := &types.Options{ MaxDepth: 3, // Maximum depth to crawl FieldScope: "rdn", // Crawling Scope Field BodyReadSize: math.MaxInt, // Maximum response size to read Timeout: 10, // Timeout is the time to wait for request in seconds Concurrency: 10, // Concurrency is the number of concurrent crawling goroutines Parallelism: 10, // Parallelism is the number of urls processing goroutines Delay: 0, // Delay is the delay between each crawl requests in seconds RateLimit: 150, // Maximum requests to send per second Strategy: "depth-first", // Visit strategy (depth-first, breadth-first) OnResult: func(result output.Result) { // Callback function to execute for result gologger.Info().Msg(result.Request.URL) }, } crawlerOptions, err := types.NewCrawlerOptions(options) if err != nil { gologger.Fatal().Msg(err.Error()) } defer crawlerOptions.Close() crawler, err := standard.New(crawlerOptions) if err != nil { gologger.Fatal().Msg(err.Error()) } defer crawler.Close() var input = "https://www.hackerone.com" err = crawler.Crawl(input) if err != nil { gologger.Warning().Msgf("Could not crawl %s: %s", input, err.Error()) } } ``` ## Reporting Issues & Feature Requests To maintain issue tracking and improve triage efficiency: **All reports start as [GitHub Discussions](https://github.com/projectdiscovery/katana/discussions)** - **Bug Reports** → [Start a Q&A Discussion](https://github.com/projectdiscovery/katana/discussions/new?category=q-a) - **Feature Requests** → [Start an Ideas Discussion](https://github.com/projectdiscovery/katana/discussions/new?category=ideas) - **Questions** → [Start a Q&A Discussion](https://github.com/projectdiscovery/katana/discussions/new?category=q-a) **Why Discussions First?** - **Community can help** with quick questions and troubleshooting - **Better triage** - confirmed bugs/features become tracked issues - **Cleaner issue tracker** - focus on actionable items only Maintainers will convert discussions to issues when appropriate after proper review. --------
katana is made with ❤️ by the [projectdiscovery](https://projectdiscovery.io) team and distributed under [MIT License](LICENSE.md). Join Discord
================================================ FILE: SECURITY.md ================================================ # Security Policy ## Reporting a Vulnerability DO NOT CREATE AN ISSUE to report a security problem. Instead, please send an email to security@projectdiscovery.io, and we will acknowledge it within 3 working days. ================================================ FILE: cmd/functional-test/main.go ================================================ package main import ( "flag" "fmt" "log" "os" "strings" "github.com/logrusorgru/aurora" "github.com/pkg/errors" "github.com/projectdiscovery/katana/internal/testutils" ) var ( debug = os.Getenv("DEBUG") == "true" success = aurora.Green("[✓]").String() failed = aurora.Red("[✘]").String() errored = false devKatanaBinary = flag.String("dev", "", "Dev Branch Katana Binary") ) func main() { flag.Parse() if err := runFunctionalTests(); err != nil { log.Fatalf("Could not run functional tests: %s\n", err) } if errored { os.Exit(1) } } func runFunctionalTests() error { for _, testcase := range testutils.TestCases { if err := runIndividualTestCase(testcase); err != nil { errored = true fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed: %s\n", failed, testcase.Name, err) } else { fmt.Printf("%s Test \"%s\" passed!\n", success, testcase.Name) } } return nil } func runIndividualTestCase(testcase testutils.TestCase) error { argsParts := strings.Fields(testcase.Args) devOutput, err := testutils.RunKatanaBinaryAndGetResults(testcase.Target, *devKatanaBinary, debug, argsParts) if err != nil { return errors.Wrap(err, "could not run Katana dev test") } if testcase.CompareFunc != nil { return testcase.CompareFunc(testcase.Target, devOutput) } if !testutils.CompareOutput(devOutput, testcase.Expected) { return errors.Errorf("expected output %s, got %s", testcase.Expected, devOutput) } return nil } ================================================ FILE: cmd/functional-test/run.sh ================================================ #!/bin/bash # reading os type from arguments CURRENT_OS=$1 if [ "${CURRENT_OS}" == "windows-latest" ];then extension=.exe fi echo "::group::Building functional-test binary" go build -o functional-test$extension echo "::endgroup::" echo "::group::Building katana binary from current branch" go build -o katana_dev$extension ../katana echo "::endgroup::" echo 'Starting katana functional test' ./functional-test$extension -dev ./katana_dev$extension ================================================ FILE: cmd/integration-test/filters.go ================================================ package main import ( "fmt" "net/http" "net/http/httptest" "os" "os/exec" "strings" "sync/atomic" "github.com/projectdiscovery/katana/internal/runner" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/types" ) var filtersTestcases = map[string]TestCase{ "match condition": &matchConditionIntegrationTest{}, "filter condition": &filterConditionIntegrationTest{}, "unique filter": &uniqueFilterIntegrationTest{}, } type matchConditionIntegrationTest struct{} // Execute executes a test case and returns an error if occurred // Execute the docs at ../README.md if the code stops working for integration. func (h *matchConditionIntegrationTest) Execute() error { results, _ := RunKatanaAndGetResults(false, "-u", "scanme.sh", "-match-condition", "status_code == 200 && contains(body, 'ok')", ) if len(results) != 1 { return fmt.Errorf("match condition failed") } return nil } type filterConditionIntegrationTest struct{} // Execute executes a test case and returns an error if occurred func (h *filterConditionIntegrationTest) Execute() error { results, _ := RunKatanaAndGetResults(false, "-u", "scanme.sh", "-filter-condition", "status_code == 200 && contains(body, 'ok')", ) if len(results) != 0 { return fmt.Errorf("filter condition failed") } return nil } type uniqueFilterIntegrationTest struct{} func (h *uniqueFilterIntegrationTest) Execute() error { // Create a test server that returns 404 for all paths except root server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { w.WriteHeader(http.StatusOK) _, _ = fmt.Fprint(w, ` Page 1 Page 2 Page 3 Page 4 `) } else { w.WriteHeader(http.StatusNotFound) // Return identical 404 content for all missing pages _, _ = fmt.Fprint(w, `

404 - Page Not Found

`) } })) defer server.Close() options := types.DefaultOptions options.URLs = []string{server.URL} options.MaxDepth = 2 options.Concurrency = 1 options.DisableUniqueFilter = true var fourOhFourCount atomic.Int32 options.OnResult = func(result output.Result) { if result.Response.StatusCode == http.StatusNotFound { fourOhFourCount.Add(1) } } katanaRunner, err := runner.New(&options) if err != nil { return fmt.Errorf("could not create runner: %v", err) } defer func() { _ = katanaRunner.Close() }() if err := katanaRunner.ExecuteCrawling(); err != nil { return fmt.Errorf("could not execute crawling: %v", err) } if fourOhFourCount.Load() != 4 { return fmt.Errorf("expected 4 404 responses, got %d", fourOhFourCount.Load()) } return nil } // ExtraArgs var ExtraDebugArgs = []string{} func RunKatanaAndGetResults(debug bool, extra ...string) ([]string, error) { cmd := exec.Command("./katana") extra = append(extra, ExtraDebugArgs...) cmd.Args = append(cmd.Args, extra...) cmd.Args = append(cmd.Args, "-duc") // disable auto updates if debug { cmd.Args = append(cmd.Args, "-debug") cmd.Stderr = os.Stderr fmt.Println(cmd.String()) } else { cmd.Args = append(cmd.Args, "-silent") } data, err := cmd.Output() if debug { fmt.Println(string(data)) } if len(data) < 1 && err != nil { return nil, fmt.Errorf("%v: %v", err.Error(), string(data)) } var parts []string items := strings.Split(string(data), "\n") for _, i := range items { if i != "" { parts = append(parts, i) } } return parts, nil } ================================================ FILE: cmd/integration-test/integration-test.go ================================================ package main import ( "fmt" "os" "strings" "github.com/logrusorgru/aurora" ) type TestCase interface { // Execute executes a test case and returns any errors if occurred Execute() error } var ( debug = os.Getenv("DEBUG") == "true" customTest = os.Getenv("TEST") errored = false success = aurora.Green("[✓]").String() failed = aurora.Red("[✘]").String() tests = map[string]map[string]TestCase{ "code": libraryTestcases, "filters": filtersTestcases, } ) func main() { for name, tests := range tests { fmt.Printf("Running test cases for \"%s\"\n", aurora.Blue(name)) if customTest != "" && !strings.Contains(name, customTest) { continue // only run tests user asked } for name, test := range tests { err := test.Execute() if err != nil { fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed: %s\n", failed, name, err) errored = true } else { fmt.Printf("%s Test \"%s\" passed!\n", success, name) } } } if errored { os.Exit(1) } } ================================================ FILE: cmd/integration-test/library.go ================================================ package main import ( "fmt" "math" "github.com/projectdiscovery/katana/pkg/engine/standard" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/types" "github.com/projectdiscovery/katana/pkg/utils/queue" ) var libraryTestcases = map[string]TestCase{ "katana as library": &goIntegrationTest{}, } type goIntegrationTest struct{} // Execute executes a test case and returns an error if occurred // Execute the docs at ../README.md if the code stops working for integration. func (h *goIntegrationTest) Execute() error { var crawledURLs []string options := &types.Options{ MaxDepth: 1, FieldScope: "rdn", BodyReadSize: math.MaxInt, RateLimit: 150, Verbose: debug, Strategy: queue.DepthFirst.String(), OnResult: func(r output.Result) { crawledURLs = append(crawledURLs, r.Request.URL) }, } crawlerOptions, err := types.NewCrawlerOptions(options) if err != nil { return err } defer func() { if err := crawlerOptions.Close(); err != nil { fmt.Printf("Error closing crawler options: %v\n", err) } }() crawler, err := standard.New(crawlerOptions) if err != nil { return err } defer func() { if err := crawler.Close(); err != nil { fmt.Printf("Error closing crawler: %v\n", err) } }() var input = "https://public-firing-range.appspot.com" err = crawler.Crawl(input) if err != nil { return err } if len(crawledURLs) == 0 { return fmt.Errorf("no URLs crawled") } return nil } ================================================ FILE: cmd/tools/crawl-maze-score/main.go ================================================ package main import ( "bufio" "fmt" "log" "math" "os" "strings" "github.com/logrusorgru/aurora" "github.com/projectdiscovery/gologger" urlutil "github.com/projectdiscovery/utils/url" ) // expectedResults is the list of expected endpoints from security-crawl-maze // blueprint directory. // https://github.com/google/security-crawl-maze/blob/master/blueprints/utils/resources/expected-results.json var expectedResults = []string{ "/css/font-face.found", "/headers/content-location.found", "/headers/link.found", "/headers/location.found", "/headers/refresh.found", "/html/doctype.found", "/html/manifest.found", "/html/body/background.found", "/html/body/a/href.found", "/html/body/a/ping.found", "/html/body/audio/src.found", "/html/body/audio/source/src.found", "/html/body/audio/source/srcset1x.found", "/html/body/audio/source/srcset2x.found", "/html/body/applet/archive.found", "/html/body/applet/codebase.found", "/html/body/blockquote/cite.found", "/html/body/embed/src.found", "/html/body/form/action-get.found", "/html/body/form/action-post.found", "/html/body/form/button/formaction.found", "/html/body/frameset/frame/src.found", "/html/body/iframe/src.found", "/html/body/iframe/srcdoc.found", "/html/body/img/dynsrc.found", "/html/body/img/lowsrc.found", "/html/body/img/longdesc.found", "/html/body/img/src-data.found", "/html/body/img/src.found", "/html/body/img/srcset1x.found", "/html/body/img/srcset2x.found", "/html/body/input/src.found", "/html/body/isindex/action.found", "/html/body/map/area/ping.found", "/html/body/object/data.found", "/html/body/object/codebase.found", "/html/body/object/param/value.found", "/html/body/script/src.found", "/html/body/svg/image/xlink.found", "/html/body/svg/script/xlink.found", "/html/body/table/background.found", "/html/body/table/td/background.found", "/html/body/video/src.found", "/html/body/video/track/src.found", "/html/body/video/poster.found", "/html/head/profile.found", "/html/head/base/href.found", "/html/head/comment-conditional.found", "/html/head/import/implementation.found", "/html/head/link/href.found", "/html/head/meta/content-csp.found", "/html/head/meta/content-pinned-websites.found", "/html/head/meta/content-reading-view.found", "/html/head/meta/content-redirect.found", "/html/misc/url/full-url.found", "/html/misc/url/path-relative-url.found", "/html/misc/url/protocol-relative-url.found", "/html/misc/url/root-relative-url.found", "/html/misc/string/dot-dot-slash-prefix.found", "/html/misc/string/dot-slash-prefix.found", "/html/misc/string/url-string.found", "/html/misc/string/string-known-extension.pdf", "/javascript/misc/automatic-post.found", "/javascript/misc/comment.found", "/javascript/misc/string-variable.found", "/javascript/misc/string-concat-variable.found", "/javascript/frameworks/angular/event-handler.found", "/javascript/frameworks/angular/router-outlet.found", "/javascript/frameworks/angularjs/ng-href.found", "/javascript/frameworks/polymer/event-handler.found", "/javascript/frameworks/polymer/polymer-router.found", "/javascript/frameworks/react/route-path.found", "/javascript/frameworks/react/index.html/search.found", "/javascript/interactive/js-delete.found", "/javascript/interactive/js-post.found", "/javascript/interactive/js-post-event-listener.found", "/javascript/interactive/js-put.found", "/javascript/interactive/listener-and-event-attribute-first.found", "/javascript/interactive/listener-and-event-attribute-second.found", "/javascript/interactive/multi-step-request-event-attribute.found", "/test/javascript/interactive/multi-step-request-event-listener-div-dom.found", "/test/javascript/interactive/multi-step-request-event-listener-div.found", "/javascript/interactive/multi-step-request-event-listener-dom.found", "/javascript/interactive/multi-step-request-event-listener.found", "/javascript/interactive/multi-step-request-redefine-event-attribute.found", "/javascript/interactive/multi-step-request-remove-button.found", "/javascript/interactive/multi-step-request-remove-event-listener.found", "/javascript/interactive/two-listeners-first.found", "/javascript/interactive/two-listeners-second.found", "/misc/known-files/robots.txt.found", "/misc/known-files/sitemap.xml.found", } func main() { if err := process(); err != nil { log.Fatalf("%s\n", err) } } var urlTestPrefix = "/test" func process() error { if len(os.Args) < 3 { fmt.Printf("Usage: crawl-maze-score output.txt output_headless.txt") return nil } input := os.Args[1] inputHeadless := os.Args[2] links, err := readFoundLinks(input) if err != nil { return err } linksHeadless, err := readFoundLinks(inputHeadless) if err != nil { return err } linksMap := make(map[string]struct{}) linksHeadlessMap := make(map[string]struct{}) for _, link := range links { linksMap[link] = struct{}{} } for _, link := range linksHeadless { linksHeadlessMap[link] = struct{}{} } matches, matchesHeadless := 0, 0 for _, expected := range expectedResults { expected = urlTestPrefix + expected _, normalOk := linksMap[expected] _, headlessOk := linksHeadlessMap[expected] if normalOk { matches++ } if headlessOk { matchesHeadless++ } fmt.Printf("[%s] [%s] %s\n", colorizeText("standard", normalOk), colorizeText("headless", headlessOk), expected) } fmt.Printf("[info] Total links (%d): Standard=>%d Headless=>%d\n", len(expectedResults), len(links), len(linksHeadless)) fmt.Printf("[info] Total: %d NormalMatches=>%d HeadlessMatches=>%d\n", len(expectedResults), matches, matchesHeadless) fmt.Printf("[info] Score: Normal=>%.2f%% Headless=>%.2f%%\n", math.Round(float64(matches*100/len(expectedResults))), math.Round(float64(matchesHeadless*100/len(expectedResults)))) return nil } func colorizeText(text string, value bool) string { if value { return aurora.Green(text + ":yes").String() } return aurora.Red(text + ":no").String() } func strippedLink(link string) string { parsed, err := urlutil.Parse(link) if err != nil { gologger.Warning().Msgf("failed to parse link while extracting path: %v", err) } return parsed.Path } func readFoundLinks(input string) ([]string, error) { file, err := os.Open(input) if err != nil { return nil, err } defer func() { if err := file.Close(); err != nil { gologger.Error().Msgf("Error closing file: %v\n", err) } }() scanner := bufio.NewScanner(file) var links []string for scanner.Scan() { text := scanner.Text() if text == "" { break } if strings.Contains(text, ".found") { links = append(links, strippedLink(text)) } } return links, nil } ================================================ FILE: go.mod ================================================ module github.com/projectdiscovery/katana go 1.25.7 require ( github.com/BishopFox/jsluice v0.0.0-20240110145140-0ddfab153e06 github.com/PuerkitoBio/goquery v1.11.0 github.com/adrianbrad/queue v1.3.0 github.com/dominikbraun/graph v0.23.0 github.com/go-rod/rod v0.116.2 github.com/happyhackingspace/dit v0.0.14 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/json-iterator/go v1.1.12 github.com/lmittmann/tint v1.0.6 github.com/logrusorgru/aurora v2.0.3+incompatible github.com/lukasbob/srcset v0.0.0-20190730101422-86b742e617f3 github.com/mfonda/simhash v0.0.0-20151007195837-79f94a1100d6 github.com/mitchellh/mapstructure v1.5.0 github.com/pkg/errors v0.9.1 github.com/projectdiscovery/dsl v0.8.5 github.com/projectdiscovery/fastdialer v0.5.2 github.com/projectdiscovery/goflags v0.1.74 github.com/projectdiscovery/gologger v1.1.67 github.com/projectdiscovery/hmap v0.0.99 github.com/projectdiscovery/mapcidr v1.1.97 github.com/projectdiscovery/ratelimit v0.0.82 github.com/projectdiscovery/retryablehttp-go v1.3.2 github.com/projectdiscovery/utils v0.8.0 github.com/projectdiscovery/wappalyzergo v0.2.62 github.com/remeh/sizedwaitgroup v1.0.0 github.com/rs/xid v1.5.0 github.com/stoewer/go-strcase v1.3.0 github.com/stretchr/testify v1.11.1 github.com/valyala/fasttemplate v1.2.2 go.uber.org/multierr v1.11.0 golang.org/x/net v0.51.0 gopkg.in/yaml.v3 v3.0.1 ) require ( aead.dev/minisign v0.2.0 // indirect github.com/Knetic/govaluate v3.0.0+incompatible // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 // indirect github.com/STARRY-S/zip v0.2.3 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/alecthomas/chroma/v2 v2.14.0 // indirect github.com/andybalholm/brotli v1.2.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/bodgit/plumbing v1.3.0 // indirect github.com/bodgit/sevenzip v1.6.1 // indirect github.com/bodgit/windows v1.0.1 // indirect github.com/brianvoe/gofakeit/v7 v7.2.1 // indirect github.com/charmbracelet/glamour v0.8.0 // indirect github.com/charmbracelet/lipgloss v0.13.0 // indirect github.com/charmbracelet/x/ansi v0.3.2 // indirect github.com/cheggaaa/pb/v3 v3.1.4 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c // indirect github.com/djherbis/times v1.6.0 // indirect github.com/dlclark/regexp2 v1.11.5 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/fgprof v0.9.5 // indirect github.com/gaissmai/bart v0.26.0 // indirect github.com/google/go-github/v30 v30.1.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gosimple/slug v1.15.0 // indirect github.com/gosimple/unidecode v1.0.1 // indirect github.com/hashicorp/go-version v1.8.0 // indirect github.com/hdm/jarm-go v0.0.7 // indirect github.com/iangcarroll/cookiemonster v1.6.0 // indirect github.com/kataras/jwt v0.1.8 // indirect github.com/klauspost/compress v1.18.2 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/logrusorgru/aurora/v4 v4.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mholt/archives v0.1.5 // indirect github.com/mikelolasagasti/xz v1.0.1 // indirect github.com/minio/minlz v1.0.1 // indirect github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect github.com/nwaples/rardecode/v2 v2.2.2 // indirect github.com/pierrec/lz4/v4 v4.1.23 // indirect github.com/projectdiscovery/asnmap v1.1.1 // indirect github.com/projectdiscovery/blackrock v0.0.1 // indirect github.com/projectdiscovery/gostruct v0.0.2 // indirect github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 // indirect github.com/refraction-networking/utls v1.7.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sashabaranov/go-openai v1.37.0 // indirect github.com/shirou/gopsutil/v3 v3.23.7 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/smacker/go-tree-sitter v0.0.0-20230720070738-0d0a9f78d8f8 // indirect github.com/sorairolake/lzip-go v0.3.8 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/tidwall/btree v1.6.0 // indirect github.com/tidwall/buntdb v1.3.0 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/grect v0.1.4 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/rtred v0.1.2 // indirect github.com/tidwall/tinyqueue v0.1.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vulncheck-oss/go-exploit v1.51.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/ysmood/fetchup v0.2.3 // indirect github.com/ysmood/got v0.40.0 // indirect github.com/yuin/goldmark v1.7.4 // indirect github.com/yuin/goldmark-emoji v1.0.3 // indirect github.com/zcalusic/sysinfo v1.0.2 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/oauth2 v0.34.0 // indirect golang.org/x/sync v0.19.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/time v0.14.0 // indirect ) require ( github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect github.com/akrylysov/pogreb v0.10.1 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/miekg/dns v1.1.62 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/projectdiscovery/networkpolicy v0.1.33 github.com/projectdiscovery/retryabledns v1.0.112 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/syndtr/goleveldb v1.0.0 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/ulikunitz/xz v0.5.15 // indirect github.com/weppos/publicsuffix-go v0.40.3-0.20250408071509-6074bbe7fd39 // indirect github.com/ysmood/goob v0.4.0 // indirect github.com/ysmood/gson v0.7.3 // indirect github.com/ysmood/leakless v0.9.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 // indirect go.etcd.io/bbolt v1.3.7 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect golang.org/x/mod v0.32.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/tools v0.41.0 // indirect gopkg.in/yaml.v2 v2.4.0 ) ================================================ FILE: go.sum ================================================ aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk= aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BishopFox/jsluice v0.0.0-20240110145140-0ddfab153e06 h1:xa/dJgg1qpWdIyr7tQcTV2TUPgBK/f0TTMLMmD5GqjQ= github.com/BishopFox/jsluice v0.0.0-20240110145140-0ddfab153e06/go.mod h1:ENDk4KXEVPZTZPygQAEWJK0BlyEWAyQZhxwCMc+o6A0= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 h1:KFac3SiGbId8ub47e7kd2PLZeACxc1LkiiNoDOFRClE= github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057/go.mod h1:iLB2pivrPICvLOuROKmlqURtFIEsoJZaMidQfCG1+D4= github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 h1:ZbFL+BDfBqegi+/Ssh7im5+aQfBRx6it+kHnC7jaDU8= github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809/go.mod h1:upgc3Zs45jBDnBT4tVRgRcgm26ABpaP7MoTSdgysca4= github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw= github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ= github.com/RumbleDiscovery/rumble-tools v0.0.0-20201105153123-f2adbb3244d2/go.mod h1:jD2+mU+E2SZUuAOHZvZj4xP4frlOo+N/YrXDvASFhkE= github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4= github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/adrianbrad/queue v1.3.0 h1:8FH1N+93HXbqta5+URa1AL+diV7MP3VDXAEnP+DNp48= github.com/adrianbrad/queue v1.3.0/go.mod h1:wYiPC/3MPbyT45QHLrPR4zcqJWPePubM1oEP/xTwhUs= github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w= github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI= github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE= github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E= github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/bits-and-blooms/bloom/v3 v3.5.0 h1:AKDvi1V3xJCmSR6QhcBfHbCN4Vf8FfxeWkMNQfmAGhY= github.com/bits-and-blooms/bloom/v3 v3.5.0/go.mod h1:Y8vrn7nk1tPIlmLtW2ZPV+W7StdVMor6bC1xgpjMZFs= github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4= github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8= github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/brianvoe/gofakeit/v7 v7.2.1 h1:AGojgaaCdgq4Adzrd2uWdbGNDyX6MWNhHdQBraNfOHI= github.com/brianvoe/gofakeit/v7 v7.2.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs= github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw= github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY= github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30= github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo= github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ= github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c h1:+Zo5Ca9GH0RoeVZQKzFJcTLoAixx5s5Gq3pTIS+n354= github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:HJGU9ULdREjOcVGZVPB5s6zYmHi1RxzT71l2wQyLmnE= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dominikbraun/graph v0.23.0 h1:TdZB4pPqCLFxYhdyMFb1TBdFxp8XLcJfTTBQucVPgCo= github.com/dominikbraun/graph v0.23.0/go.mod h1:yOjYyogZLY1LSG9E33JWZJiq5k83Qy2C6POAuiViluc= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gaissmai/bart v0.26.0 h1:xOZ57E9hJLBiQaSyeZa9wgWhGuzfGACgqp4BE77OkO0= github.com/gaissmai/bart v0.26.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= github.com/google/go-github/v50 v50.1.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 h1:gD0vax+4I+mAj+jEChEf25Ia07Jq7kYOFO5PPhAxFl4= github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo= github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/happyhackingspace/dit v0.0.14 h1:rkIu0HuFqvqr8F2PJgG0F+lx6DbX/tQE1hXKwIF2NQQ= github.com/happyhackingspace/dit v0.0.14/go.mod h1:+WeAxrX7QYeiDmXLVaDgrqpyfD4O/sHlOL4wtbiIpUQ= github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hdm/jarm-go v0.0.7 h1:Eq0geenHrBSYuKrdVhrBdMMzOmA+CAMLzN2WrF3eL6A= github.com/hdm/jarm-go v0.0.7/go.mod h1:kinGoS0+Sdn1Rr54OtanET5E5n7AlD6T6CrJAKDjJSQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/iangcarroll/cookiemonster v1.6.0 h1:NPFkn/ZZYZgzXhJ1awRnYhZ3fJK3hKWgbctfTW21kew= github.com/iangcarroll/cookiemonster v1.6.0/go.mod h1:n3MvoAq56NkNyCEyhcYs3ZJMzTc9rL3w7IaITI0apMg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kataras/jwt v0.1.8 h1:u71baOsYD22HWeSOg32tCHbczPjdCk7V4MMeJqTtmGk= github.com/kataras/jwt v0.1.8/go.mod h1:Q5j2IkcIHnfwy+oNY3TVWuEBJNw0ADgCcXK9CaZwV4o= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/lmittmann/tint v1.0.6 h1:vkkuDAZXc0EFGNzYjWcV0h7eEX+uujH48f/ifSkJWgc= github.com/lmittmann/tint v1.0.6/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA= github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lukasbob/srcset v0.0.0-20190730101422-86b742e617f3 h1:l1rIRmxNhzeQM+qA3D0CsDLo0Hx45q9JmK0BlCjt6Ks= github.com/lukasbob/srcset v0.0.0-20190730101422-86b742e617f3/go.mod h1:j16TYl5p17+vBMyaL6Nu4ojlOnfX8lc2k2cfmw6m5TQ= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mfonda/simhash v0.0.0-20151007195837-79f94a1100d6 h1:bjfMeqxWEJ6IRUvGkiTkSwx0a6UdQJsbirRSoXogteY= github.com/mfonda/simhash v0.0.0-20151007195837-79f94a1100d6/go.mod h1:WVJJvUw/pIOcwu2O8ZzHEhmigq2jzwRNfJVRMJB7bR8= github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ= github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4= github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk= github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0= github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc= github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A= github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 h1:yRZGarbxsRytL6EGgbqK2mCY+Lk5MWKQYKJT2gEglhc= github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg= github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= github.com/nwaples/rardecode/v2 v2.2.2 h1:/5oL8dzYivRM/tqX9VcTSWfbpwcbwKG1QtSJr3b3KcU= github.com/nwaples/rardecode/v2 v2.2.2/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pierrec/lz4/v4 v4.1.23 h1:oJE7T90aYBGtFNrI8+KbETnPymobAhzRrR8Mu8n1yfU= github.com/pierrec/lz4/v4 v4.1.23/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/projectdiscovery/asnmap v1.1.1 h1:ImJiKIaACOT7HPx4Pabb5dksolzaFYsD1kID2iwsDqI= github.com/projectdiscovery/asnmap v1.1.1/go.mod h1:QT7jt9nQanj+Ucjr9BqGr1Q2veCCKSAVyUzLXfEcQ60= github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ= github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss= github.com/projectdiscovery/dsl v0.8.5 h1:f3opg8Jzikwx6VXC+CbgseUmSUqdfCnfGT08Syhp0sw= github.com/projectdiscovery/dsl v0.8.5/go.mod h1:AuUq18cpLJJ0uAjJZKaLrdyAgDHrnQAjLMZtPEyMoJw= github.com/projectdiscovery/fastdialer v0.5.2 h1:BrK23yWc0XD57DMLqnF5oM5tBy8xx9brin+zoSo6gCw= github.com/projectdiscovery/fastdialer v0.5.2/go.mod h1:euoxS1E93LDnl0OnNN0UALedAFF+EehBxyU3z+79l0g= github.com/projectdiscovery/goflags v0.1.74 h1:n85uTRj5qMosm0PFBfsvOL24I7TdWRcWq/1GynhXS7c= github.com/projectdiscovery/goflags v0.1.74/go.mod h1:UMc9/7dFz2oln+10tv6cy+7WZKTHf9UGhaNkF95emh4= github.com/projectdiscovery/gologger v1.1.67 h1:GZU3AjYiJvcwJT5TlfIv+152/TVmaz62Zyn3/wWXlig= github.com/projectdiscovery/gologger v1.1.67/go.mod h1:35oeQP6wvj58S+o+Km6boED/t786FXQkI0exhFHJbNE= github.com/projectdiscovery/gostruct v0.0.2 h1:s8gP8ApugGM4go1pA+sVlPDXaWqNP5BBDDSv7VEdG1M= github.com/projectdiscovery/gostruct v0.0.2/go.mod h1:H86peL4HKwMXcQQtEa6lmC8FuD9XFt6gkNR0B/Mu5PE= github.com/projectdiscovery/hmap v0.0.99 h1:XPfLnD3CUrMqVCIdpK9ozD7Xmp3simx3T+2j4WWhHnU= github.com/projectdiscovery/hmap v0.0.99/go.mod h1:koyUJi83K5G3w35ZLFXOYZIyYJsO+6hQrgDDN1RBrVE= github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 h1:eR+0HE//Ciyfwy3HC7fjRyKShSJHYoX2Pv7pPshjK/Q= github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI= github.com/projectdiscovery/mapcidr v1.1.97 h1:7FkxNNVXp+m1rIu5Nv/2SrF9k4+LwP8QuWs2puwy+2w= github.com/projectdiscovery/mapcidr v1.1.97/go.mod h1:9dgTJh1SP02gYZdpzMjm6vtYFkEHQHoTyaVNvaeJ7lA= github.com/projectdiscovery/networkpolicy v0.1.33 h1:bVgp+XpLEsQ7ZEJt3UaUqIwhI01MMdt7F2dfIKFQg/w= github.com/projectdiscovery/networkpolicy v0.1.33/go.mod h1:YAPddAXUc/lhoU85AFdvgOQKx8Qh8r0vzSjexRWk6Yk= github.com/projectdiscovery/ratelimit v0.0.82 h1:rtO5SQf5uQFu5zTahTaTcO06OxmG8EIF1qhdFPIyTak= github.com/projectdiscovery/ratelimit v0.0.82/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM= github.com/projectdiscovery/retryabledns v1.0.112 h1:4iCiuo6jMnw/pdOZRzBQrbUOUu5tOeuvGupxVV8RDLw= github.com/projectdiscovery/retryabledns v1.0.112/go.mod h1:xsJTKbo+KGqd7+88z1naEUFJybLH2yjB/zUyOweA7k0= github.com/projectdiscovery/retryablehttp-go v1.3.2 h1:Rv2gw/8t3QZz+WIuHUspVBoRrpBWpVOhzh/wLUGYSVM= github.com/projectdiscovery/retryablehttp-go v1.3.2/go.mod h1:q1EQ+FX9JP5Z0EqLXDf+8b6XdzWmBXIMPowpI6hQ9aU= github.com/projectdiscovery/utils v0.8.0 h1:8d79OCs5xGDNXdKxMUKMY/lgQSUWJMYB1B2Sx+oiqkQ= github.com/projectdiscovery/utils v0.8.0/go.mod h1:CU6tjtyTRxBrnNek+GPJplw4IIHcXNZNKO09kWgqTdg= github.com/projectdiscovery/wappalyzergo v0.2.62 h1:SMZ70bLCj6jHnFgjanuiaQpqUXY6aiEC3YoM0ZSvYes= github.com/projectdiscovery/wappalyzergo v0.2.62/go.mod h1:8FtSVcmPRZU0g1euBpdSYEBHIvB7Zz9MOb754ZqZmfU= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/refraction-networking/utls v1.7.1 h1:dxg+jla3uocgN8HtX+ccwDr68uCBBO3qLrkZUbqkcw0= github.com/refraction-networking/utls v1.7.1/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ= github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E= github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/sashabaranov/go-openai v1.37.0 h1:hQQowgYm4OXJ1Z/wTrE+XZaO20BYsL0R3uRPSpfNZkY= github.com/sashabaranov/go-openai v1.37.0/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg= github.com/shirou/gopsutil/v3 v3.23.7 h1:C+fHO8hfIppoJ1WdsVm1RoI0RwXoNdfTK7yWXV0wVj4= github.com/shirou/gopsutil/v3 v3.23.7/go.mod h1:c4gnmoRC0hQuaLqvxnx1//VXQ0Ms/X9UnJF8pddY5z4= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smacker/go-tree-sitter v0.0.0-20230720070738-0d0a9f78d8f8 h1:DxgjlvWYsb80WEN2Zv3WqJFAg2DKjUQJO6URGdf1x6Y= github.com/smacker/go-tree-sitter v0.0.0-20230720070738-0d0a9f78d8f8/go.mod h1:q99oHDsbP0xRwmn7Vmob8gbSMNyvJ83OauXPSuHQuKE= github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik= github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.4/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI= github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8= github.com/tidwall/btree v1.6.0 h1:LDZfKfQIBHGHWSwckhXI0RPSXzlo+KYdjK7FWSqOzzg= github.com/tidwall/btree v1.6.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA= github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU= github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg= github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q= github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8= github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8= github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ= github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE= github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw= github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vulncheck-oss/go-exploit v1.51.0 h1:HTmJ4Q94tbEDPb35mQZn6qMg4rT+Sw9n+L7g3Pjr+3o= github.com/vulncheck-oss/go-exploit v1.51.0/go.mod h1:J28w0dLnA6DnCrnBm9Sbt6smX8lvztnnN2wCXy7No6c= github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k= github.com/weppos/publicsuffix-go v0.30.1-0.20230422193905-8fecedd899db/go.mod h1:aiQaH1XpzIfgrJq3S1iw7w+3EDbRP7mF5fmwUhWyRUs= github.com/weppos/publicsuffix-go v0.40.3-0.20250408071509-6074bbe7fd39 h1:Bz/zVM/LoGZ9IztGBHrq2zlFQQbEG8dBYnxb4hamIHM= github.com/weppos/publicsuffix-go v0.40.3-0.20250408071509-6074bbe7fd39/go.mod h1:2oFzEwGYI7lhiqG0YkkcKa6VcpjVinQbWxaPzytDmLA= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= github.com/ysmood/gop v0.2.0 h1:+tFrG0TWPxT6p9ZaZs+VY+opCvHU8/3Fk6BaNv6kqKg= github.com/ysmood/gop v0.2.0/go.mod h1:rr5z2z27oGEbyB787hpEcx4ab8cCiPnKxn0SUHt6xzk= github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= github.com/ysmood/gotrace v0.6.0 h1:SyI1d4jclswLhg7SWTL6os3L1WOKeNn/ZtzVQF8QmdY= github.com/ysmood/gotrace v0.6.0/go.mod h1:TzhIG7nHDry5//eYZDYcTzuJLYQIkykJzCRIo4/dzQM= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= github.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg= github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4= github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zcalusic/sysinfo v1.0.2 h1:nwTTo2a+WQ0NXwo0BGRojOJvJ/5XKvQih+2RrtWqfxc= github.com/zcalusic/sysinfo v1.0.2/go.mod h1:kluzTYflRWo6/tXVMJPdEjShsbPpsFRyy+p1mBQPC30= github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 h1:Nzukz5fNOBIHOsnP+6I79kPx3QhLv8nBy2mfFhBRq30= github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE= github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is= github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+TWveAxiVWk= github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ= github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968 h1:YOQ1vXEwE4Rnj+uQ/3oCuJk5wgVsvUyW+glsndwYuyA= github.com/zmap/zcrypto v0.0.0-20230422215203-9a665e1e9968/go.mod h1:xIuOvYCZX21S5Z9bK1BMrertTGX/F8hgAPw7ERJRNS0= github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= ================================================ FILE: integration_tests/run.sh ================================================ #!/bin/bash echo "::group::Build katana" rm integration-test katana 2>/dev/null cd ../cmd/katana go build mv katana ../../integration_tests/katana echo "::endgroup::" echo "::group::Build katana integration-test" cd ../integration-test go build mv integration-test ../../integration_tests/integration-test cd ../../integration_tests echo "::endgroup::" ./integration-test if [ $? -eq 0 ] then exit 0 else exit 1 fi ================================================ FILE: internal/runner/banner.go ================================================ package runner import ( "github.com/projectdiscovery/gologger" updateutils "github.com/projectdiscovery/utils/update" ) var banner = (` __ __ / /_____ _/ /____ ____ ___ _ / '_/ _ / __/ _ / _ \/ _ / /_/\_\\_,_/\__/\_,_/_//_/\_,_/ `) var version = "v1.5.0" // showBanner is used to show the banner to the user func showBanner() { gologger.Print().Msgf("%s\n", banner) gologger.Print().Msgf("\t\tprojectdiscovery.io\n\n") } // GetUpdateCallback returns a callback function that updates katana func GetUpdateCallback() func() { return func() { showBanner() updateutils.GetUpdateToolCallback("katana", version)() } } ================================================ FILE: internal/runner/executer.go ================================================ package runner import ( "strings" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/utils/errkit" urlutil "github.com/projectdiscovery/utils/url" "github.com/remeh/sizedwaitgroup" ) // ExecuteCrawling executes the crawling main loop func (r *Runner) ExecuteCrawling() error { if r.crawler == nil { return errkit.New("crawler is not initialized") } inputs := r.parseInputs() if len(inputs) == 0 { return errkit.New("no input provided for crawling") } for _, input := range inputs { _ = r.state.InFlightUrls.Set(addSchemeIfNotExists(input), struct{}{}) } defer func() { if err := r.crawler.Close(); err != nil { gologger.Error().Msgf("Error closing crawler: %v\n", err) } }() wg := sizedwaitgroup.New(r.options.Parallelism) for _, input := range inputs { if !r.networkpolicy.Validate(input) { gologger.Info().Msgf("Skipping excluded host %s", input) continue } wg.Add() input = addSchemeIfNotExists(input) go func(input string) { defer wg.Done() if err := r.crawler.Crawl(input); err != nil { gologger.Warning().Msgf("Could not crawl %s: %s", input, err) } r.state.InFlightUrls.Delete(input) }(input) } wg.Wait() return nil } // scheme less urls are skipped and are required for headless mode and other purposes // this method adds scheme if given input does not have any func addSchemeIfNotExists(inputURL string) string { if strings.HasPrefix(inputURL, urlutil.HTTP) || strings.HasPrefix(inputURL, urlutil.HTTPS) { return inputURL } parsed, err := urlutil.Parse(inputURL) if err != nil { gologger.Warning().Msgf("input %v is not a valid url got %v", inputURL, err) return inputURL } if parsed.Port() != "" && (parsed.Port() == "80" || parsed.Port() == "8080") { return urlutil.HTTP + urlutil.SchemeSeparator + inputURL } else { return urlutil.HTTPS + urlutil.SchemeSeparator + inputURL } } ================================================ FILE: internal/runner/healthcheck.go ================================================ package runner import ( "fmt" "net" "os/exec" "runtime" "strings" "github.com/projectdiscovery/goflags" "github.com/projectdiscovery/katana/pkg/types" fileutil "github.com/projectdiscovery/utils/file" permissionutil "github.com/projectdiscovery/utils/permission" ) func DoHealthCheck(options *types.Options, flagSet *goflags.FlagSet) string { // RW permissions on config file cfgFilePath, _ := flagSet.GetConfigFilePath() var test strings.Builder _, _ = fmt.Fprintf(&test, "Version: %s\n", version) _, _ = fmt.Fprintf(&test, "Operative System: %s\n", runtime.GOOS) _, _ = fmt.Fprintf(&test, "Architecture: %s\n", runtime.GOARCH) _, _ = fmt.Fprintf(&test, "Go Version: %s\n", runtime.Version()) _, _ = fmt.Fprintf(&test, "Compiler: %s\n", runtime.Compiler) var testResult string if permissionutil.IsRoot { testResult = "Ok" } else { testResult = "Ko" } _, _ = fmt.Fprintf(&test, "root: %s\n", testResult) ok, err := fileutil.IsReadable(cfgFilePath) if ok { testResult = "Ok" } else { testResult = "Ko" } if err != nil { testResult += fmt.Sprintf(" (%s)", err) } _, _ = fmt.Fprintf(&test, "Config file \"%s\" Read => %s\n", cfgFilePath, testResult) ok, err = fileutil.IsWriteable(cfgFilePath) if ok { testResult = "Ok" } else { testResult = "Ko" } if err != nil { testResult += fmt.Sprintf(" (%s)", err) } fmt.Fprintf(&test, "Config file \"%s\" Write => %s\n", cfgFilePath, testResult) c4, err := net.Dial("tcp4", "scanme.sh:80") if err == nil && c4 != nil { _ = c4.Close() } testResult = "Ok" if err != nil { testResult = fmt.Sprintf("Ko (%s)", err) } fmt.Fprintf(&test, "TCP IPv4 connectivity to scanme.sh:80 => %s\n", testResult) c6, err := net.Dial("tcp6", "scanme.sh:80") if err == nil && c6 != nil { _ = c6.Close() } testResult = "Ok" if err != nil { testResult = fmt.Sprintf("Ko (%s)", err) } _, _ = fmt.Fprintf(&test, "TCP IPv6 connectivity to scanme.sh:80 => %s\n", testResult) u4, err := net.Dial("udp4", "scanme.sh:53") if err == nil && u4 != nil { _ = u4.Close() } testResult = "Ok" if err != nil { testResult = fmt.Sprintf("Ko (%s)", err) } _, _ = fmt.Fprintf(&test, "UDP IPv4 connectivity to scanme.sh:80 => %s\n", testResult) u6, err := net.Dial("udp6", "scanme.sh:80") if err == nil && u6 != nil { _ = u6.Close() } testResult = "Ok" if err != nil { testResult = fmt.Sprintf("Ko (%s)", err) } _, _ = fmt.Fprintf(&test, "UDP IPv6 connectivity to scanme.sh:80 => %s\n", testResult) // attempt to identify if chome is installed locally if chromePath, err := exec.LookPath("chrome"); err == nil { _, _ = fmt.Fprintf(&test, "Potential chrome binary path (linux/osx) => %s\n", chromePath) } if chromePath, err := exec.LookPath("chrome.exe"); err == nil { _, _ = fmt.Fprintf(&test, "Potential chrome.exe binary path (windows) => %s\n", chromePath) } return test.String() } ================================================ FILE: internal/runner/options.go ================================================ package runner import ( "bufio" "os" "path/filepath" "regexp" "strings" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/gologger/formatter" "github.com/projectdiscovery/katana/pkg/types" "github.com/projectdiscovery/katana/pkg/utils" "github.com/projectdiscovery/utils/errkit" fileutil "github.com/projectdiscovery/utils/file" "gopkg.in/yaml.v3" ) // validateOptions validates the provided options for crawler func validateOptions(options *types.Options) error { if options.MaxDepth <= 0 && options.CrawlDuration.Seconds() <= 0 { return errkit.New("either max-depth or crawl-duration must be specified") } if len(options.URLs) == 0 && !fileutil.HasStdin() { return errkit.New("no inputs specified for crawler") } // Disabling automatic form fill (-aff) for headless navigation due to incorrect implementation. // Form filling should be handled via headless actions within the page context if options.HeadlessHybrid && options.AutomaticFormFill { options.AutomaticFormFill = false gologger.Info().Msgf("Automatic form fill (-aff) has been disabled for headless navigation.") } // Disallow ambiguous engine selection if options.Headless && options.HeadlessHybrid { return errkit.New("flags -hl (headless) and -hh (hybrid) are mutually exclusive") } if (options.HeadlessOptionalArguments != nil || options.HeadlessNoSandbox || options.SystemChromePath != "") && !options.Headless && !options.HeadlessHybrid { return errkit.New("headless (-hl) or hybrid (-hh) mode is required if -ho, -nos or -scp are set") } if (options.HeadlessOptionalArguments != nil || options.HeadlessNoSandbox || options.SystemChromePath != "") && !options.Headless && !options.HeadlessHybrid { return errkit.New("headless mode (-hl) is required if -ho, -nos or -scp are set") } if options.SystemChromePath != "" { if !fileutil.FileExists(options.SystemChromePath) { return errkit.New("specified system chrome binary does not exist") } } if options.StoreResponseDir != "" && !options.StoreResponse { gologger.Debug().Msgf("store response directory specified, enabling \"sr\" flag automatically\n") options.StoreResponse = true } for _, mr := range options.OutputMatchRegex { cr, err := regexp.Compile(mr) if err != nil { return errkit.Wrap(err, "Invalid value for match regex option") } options.MatchRegex = append(options.MatchRegex, cr) } for _, fr := range options.OutputFilterRegex { cr, err := regexp.Compile(fr) if err != nil { return errkit.Wrap(err, "Invalid value for filter regex option") } options.FilterRegex = append(options.FilterRegex, cr) } if options.KnownFiles != "" && options.MaxDepth < 3 { gologger.Info().Msgf("Depth automatically set to 3 to accommodate the `--known-files` option (originally set to %d).", options.MaxDepth) options.MaxDepth = 3 } gologger.DefaultLogger.SetFormatter(formatter.NewCLI(options.NoColors)) return nil } // readCustomFormConfig reads custom form fill config func readCustomFormConfig(formConfig string) error { file, err := os.Open(formConfig) if err != nil { return errkit.Wrap(err, "could not read form config") } defer func() { if err := file.Close(); err != nil { gologger.Error().Msgf("Error closing file: %v\n", err) } }() var data utils.FormFillData if err := yaml.NewDecoder(file).Decode(&data); err != nil { return errkit.Wrap(err, "could not decode form config") } utils.FormData = data return nil } // parseInputs parses the inputs returning a slice of URLs func (r *Runner) parseInputs() []string { values := make(map[string]struct{}) for _, url := range r.options.URLs { if url == "" { continue } value := normalizeInput(url) if _, ok := values[value]; !ok { values[value] = struct{}{} } } if r.stdin { scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { value := normalizeInput(scanner.Text()) if _, ok := values[value]; !ok { values[value] = struct{}{} } } } final := make([]string, 0, len(values)) for k := range values { final = append(final, k) } return final } func normalizeInput(value string) string { return strings.TrimSpace(value) } func initExampleFormFillConfig() error { homedir, err := os.UserHomeDir() if err != nil { return errkit.Wrap(err, "could not get home directory") } defaultConfig := filepath.Join(homedir, ".config", "katana", "form-config.yaml") if fileutil.FileExists(defaultConfig) { return readCustomFormConfig(defaultConfig) } if err := os.MkdirAll(filepath.Dir(defaultConfig), 0775); err != nil { return err } exampleConfig, err := os.Create(defaultConfig) if err != nil { return errkit.Wrap(err, "could not get home directory") } defer func() { if err := exampleConfig.Close(); err != nil { gologger.Error().Msgf("Error closing example config: %v\n", err) } }() err = yaml.NewEncoder(exampleConfig).Encode(utils.DefaultFormFillData) return err } ================================================ FILE: internal/runner/runner.go ================================================ package runner import ( "encoding/json" "os" "strconv" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/engine" "github.com/projectdiscovery/katana/pkg/engine/headless" "github.com/projectdiscovery/katana/pkg/engine/hybrid" "github.com/projectdiscovery/katana/pkg/engine/standard" "github.com/projectdiscovery/katana/pkg/types" "github.com/projectdiscovery/mapcidr" "github.com/projectdiscovery/mapcidr/asn" "github.com/projectdiscovery/networkpolicy" "github.com/projectdiscovery/utils/errkit" fileutil "github.com/projectdiscovery/utils/file" iputil "github.com/projectdiscovery/utils/ip" mapsutil "github.com/projectdiscovery/utils/maps" updateutils "github.com/projectdiscovery/utils/update" "go.uber.org/multierr" ) // Runner creates the required resources for crawling // and executes the crawl process. type Runner struct { crawlerOptions *types.CrawlerOptions stdin bool crawler engine.Engine options *types.Options state *RunnerState networkpolicy *networkpolicy.NetworkPolicy } type RunnerState struct { InFlightUrls *mapsutil.SyncLockMap[string, struct{}] } // New returns a new crawl runner structure func New(options *types.Options) (*Runner, error) { // create the resume configuration structure if options.ShouldResume() { gologger.Info().Msg("Resuming from save checkpoint") file, err := os.ReadFile(options.Resume) if err != nil { return nil, err } runnerState := &RunnerState{} err = json.Unmarshal(file, runnerState) if err != nil { return nil, err } options.URLs = mapsutil.GetKeys(runnerState.InFlightUrls.GetAll()) } options.ConfigureOutput() showBanner() if options.Version { gologger.Info().Msgf("Current version: %s", version) return nil, nil } if !options.DisableUpdateCheck { latestVersion, err := updateutils.GetToolVersionCallback("katana", version)() if err != nil { if options.Verbose { gologger.Error().Msgf("katana version check failed: %v", err.Error()) } } else { gologger.Info().Msgf("Current katana version %v %v", version, updateutils.GetVersionDescription(version, latestVersion)) } } if err := initExampleFormFillConfig(); err != nil { return nil, errkit.Wrap(err, "could not init default config") } if err := validateOptions(options); err != nil { return nil, errkit.Wrap(err, "could not validate options") } if options.FormConfig != "" { if err := readCustomFormConfig(options.FormConfig); err != nil { return nil, err } } crawlerOptions, err := types.NewCrawlerOptions(options) if err != nil { return nil, errkit.Wrap(err, "could not create crawler options") } var crawler engine.Engine switch { case options.ChromeWSUrl != "": // When connecting to existing browser via WebSocket URL, // use hybrid engine regardless of other flags // (ChromeWSUrl takes precedence over -headless flag) crawler, err = hybrid.New(crawlerOptions) case options.Headless: crawler, err = headless.New(crawlerOptions) case options.HeadlessHybrid: crawler, err = hybrid.New(crawlerOptions) default: crawler, err = standard.New(crawlerOptions) } if err != nil { return nil, errkit.Wrap(err, "could not create standard crawler") } var npOptions networkpolicy.Options for _, exclude := range options.Exclude { switch { case exclude == "cdn": //implement cdn check in netoworkpolicy pkg?? continue case exclude == "private-ips": npOptions.DenyList = append(npOptions.DenyList, networkpolicy.DefaultIPv4Denylist...) npOptions.DenyList = append(npOptions.DenyList, networkpolicy.DefaultIPv4DenylistRanges...) npOptions.DenyList = append(npOptions.DenyList, networkpolicy.DefaultIPv6Denylist...) npOptions.DenyList = append(npOptions.DenyList, networkpolicy.DefaultIPv6DenylistRanges...) case iputil.IsCIDR(exclude): npOptions.DenyList = append(npOptions.DenyList, exclude) case asn.IsASN(exclude): // update this to use networkpolicy pkg once https://github.com/projectdiscovery/networkpolicy/pull/55 is merged ips := expandASNInputValue(exclude) npOptions.DenyList = append(npOptions.DenyList, ips...) case iputil.IsPort(exclude): port, _ := strconv.Atoi(exclude) npOptions.DenyPortList = append(npOptions.DenyPortList, port) default: npOptions.DenyList = append(npOptions.DenyList, exclude) } } np, _ := networkpolicy.New(npOptions) runner := &Runner{ options: options, stdin: fileutil.HasStdin(), crawlerOptions: crawlerOptions, crawler: crawler, state: &RunnerState{InFlightUrls: mapsutil.NewSyncLockMap[string, struct{}]()}, networkpolicy: np, } return runner, nil } // Close closes the runner releasing resources func (r *Runner) Close() error { return multierr.Combine( r.crawler.Close(), r.crawlerOptions.Close(), ) } func (r *Runner) SaveState(resumeFilename string) error { runnerState := r.state data, _ := json.Marshal(runnerState) return os.WriteFile(resumeFilename, data, os.ModePerm) } func expandCIDRInputValue(value string) []string { var ips []string ipsCh, _ := mapcidr.IPAddressesAsStream(value) for ip := range ipsCh { ips = append(ips, ip) } return ips } func expandASNInputValue(value string) []string { var ips []string cidrs, _ := asn.GetCIDRsForASNNum(value) for _, cidr := range cidrs { ips = append(ips, expandCIDRInputValue(cidr.String())...) } return ips } ================================================ FILE: internal/testutils/helper.go ================================================ package testutils func CompareOutput(input, expected []string) bool { if len(input) != len(expected) { return false } for i, v := range input { if v != expected[i] { return false } } return true } ================================================ FILE: internal/testutils/integration.go ================================================ package testutils import ( "fmt" "os/exec" "strings" ) func RunKatanaBinaryAndGetResults(target string, katanaBinary string, debug bool, args []string) ([]string, error) { cmd := exec.Command("bash", "-c") cmdLine := fmt.Sprintf(`echo %s | %s `, target, katanaBinary) cmdLine += strings.Join(args, " ") cmd.Args = append(cmd.Args, cmdLine) data, err := cmd.Output() if err != nil { return nil, err } parts := []string{} items := strings.Split(string(data), "\n") for _, i := range items { if i != "" { parts = append(parts, i) } } return parts, nil } ================================================ FILE: internal/testutils/testutils.go ================================================ package testutils import ( "strings" "github.com/projectdiscovery/utils/errkit" ) type TestCase struct { Name string Target string Args string Expected []string CompareFunc func(target string, got []string) error } var TestCases = []TestCase{ { Name: "Headless Browser Without Incognito", Target: "https://www.hackerone.com/", Expected: nil, Args: "-headless -no-incognito -depth 2 -silent -no-sandbox", CompareFunc: func(target string, got []string) error { for _, res := range got { if strings.Contains(res, target) { return nil } } return errkit.Newf("expected %v target in output, but got %v ", target, strings.Join(got, "\n")) }, }, } ================================================ FILE: pkg/engine/common/base.go ================================================ package common import ( "bytes" "context" "io" "net/http" "net/url" "time" "github.com/PuerkitoBio/goquery" "github.com/go-rod/rod" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/engine/parser/files" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/types" "github.com/projectdiscovery/katana/pkg/utils" "github.com/projectdiscovery/katana/pkg/utils/queue" "github.com/projectdiscovery/retryablehttp-go" "github.com/projectdiscovery/utils/errkit" httputil "github.com/projectdiscovery/utils/http" mapsutil "github.com/projectdiscovery/utils/maps" urlutil "github.com/projectdiscovery/utils/url" "github.com/remeh/sizedwaitgroup" ) // Shared represents the shared state and configuration used across all crawl sessions. // It maintains common resources like HTTP headers, cookie jars, known files database, // and crawler options that are reused for efficiency across multiple crawl operations. type Shared struct { Headers map[string]string KnownFiles *files.KnownFiles Options *types.CrawlerOptions Jar *httputil.CookieJar PathTrie *utils.PathTrie } // NewShared creates a new Shared instance with the provided crawler options. // It initializes the HTTP headers, known files database (if configured), and an empty cookie jar. // Returns an error if the HTTP client or cookie jar creation fails. func NewShared(options *types.CrawlerOptions) (*Shared, error) { shared := &Shared{ Headers: options.Options.ParseCustomHeaders(), Options: options, } if options.Options.KnownFiles != "" { httpclient, _, err := BuildHttpClient(options.Dialer, options.Options, nil) if err != nil { return nil, errkit.Wrap(err, "could not create http client") } shared.KnownFiles = files.New(httpclient, options.Options.KnownFiles) } // create an empty cookie jar, this is used to store cookies during the crawl jar, err := httputil.NewCookieJar() if err != nil { return nil, errkit.Wrap(err, "could not create cookie jar") } shared.Jar = jar if options.Options.FilterSimilar { shared.PathTrie = utils.NewPathTrie(options.Options.FilterSimilarThreshold) } return shared, nil } // Enqueue adds one or more navigation requests to the crawl queue after applying // validation checks. The method performs the following checks in order: // 1. URL format validation // 2. Query parameter handling (if IgnoreQueryParams is enabled) // 3. Depth filtering - skips URLs exceeding MaxDepth before uniqueness check // to prevent caching URLs that would be rejected, allowing them to be // processed if discovered later at valid depths via different paths // 4. Uniqueness filtering - prevents duplicate URL crawling // 5. Cycle detection - identifies URLs stuck in redirect loops // 6. Scope validation - ensures URLs belong to the allowed crawl scope // // For in-scope URLs, the method also handles path climbing when enabled, // extracting and enqueuing parent directory paths. // Out-of-scope URLs are sent to output if DisplayOutScope is enabled. func (s *Shared) Enqueue(queue *queue.Queue, navigationRequests ...*navigation.Request) { for _, nr := range navigationRequests { if nr.URL == "" || !utils.IsURL(nr.URL) { if s.Options.Options.OnSkipURL != nil { s.Options.Options.OnSkipURL(nr.URL) } continue } reqUrl := nr.RequestURL() if s.Options.Options.IgnoreQueryParams { reqUrl = utils.ReplaceAllQueryParam(reqUrl, "") } if s.Options.Options.FilterSimilar { reqUrl = utils.FingerprintURL(reqUrl, s.PathTrie) } // Skip adding to the crawl queue when the maximum depth is exceeded. // Must be done before checking uniqueness to avoid caching item that will be skipped // to handle them if faced on lower depth via another path. if nr.Depth > s.Options.Options.MaxDepth { continue } // Ignore blank URL items and only work on unique items if !s.Options.UniqueFilter.UniqueURL(reqUrl) && len(nr.CustomFields) == 0 { continue } // - URLs stuck in a loop if s.Options.UniqueFilter.IsCycle(nr.RequestURL()) { continue } // skip crawling if the endpoint is not in scope inScope := s.ValidateScope(nr.URL, nr.RootHostname) if !inScope { // if the user requested anyway out of scope items // they are sent to output without visiting if s.Options.Options.DisplayOutScope { s.Output(nr, nil, ErrOutOfScope) } continue } queue.Push(nr, nr.Depth) if s.Options.Options.PathClimb { extractedParentURLs := utils.ExtractParentPaths(nr.URL) for _, extractedParentURL := range extractedParentURLs { if !utils.IsURL(extractedParentURL) { continue } checkURL := extractedParentURL if s.Options.Options.FilterSimilar { checkURL = utils.FingerprintURL(checkURL, s.PathTrie) } if !s.Options.UniqueFilter.UniqueURL(checkURL) { continue } if !s.ValidateScope(extractedParentURL, nr.RootHostname) { continue } parentDepth := nr.Depth if parentDepth > 0 { parentDepth-- } parentReq := &navigation.Request{ Method: nr.Method, URL: extractedParentURL, Depth: parentDepth, RootHostname: nr.RootHostname, Source: nr.Source, Tag: "path-climb", } queue.Push(parentReq, parentDepth) } } } } // ValidateScope checks whether a given URL is within the allowed crawling scope // based on the configured scope rules and the root hostname. // Returns true if the URL passes scope validation, false otherwise. func (s *Shared) ValidateScope(URL string, root string) bool { parsed, err := urlutil.Parse(URL) if err != nil { gologger.Warning().Msgf("failed to parse url while validating scope: %v", err) return false } scopeValidated, err := s.Options.ScopeManager.Validate(parsed.URL, root) return err == nil && scopeValidated } // Output writes a crawl result to the configured output writer. // It creates a Result object containing the navigation request, response (if any), // and error information (if any), then writes it to the output writer. // If an OnResult callback is configured and output writing succeeds, the callback is invoked. func (s *Shared) Output(navigationRequest *navigation.Request, navigationResponse *navigation.Response, err error) { var errData string if err != nil { errData = err.Error() } // Write the found result to output result := &output.Result{ Timestamp: time.Now(), Request: navigationRequest, Response: navigationResponse, Error: errData, } outputErr := s.Options.OutputWriter.Write(result) if s.Options.Options.OnResult != nil && outputErr == nil { s.Options.Options.OnResult(*result) } } // CrawlSession represents an active crawling session for a specific target URL. // It maintains the session context, cancellation function, parsed URL information, // the request queue, and HTTP/browser clients needed for the crawl operation. type CrawlSession struct { Ctx context.Context CancelFunc context.CancelFunc URL *url.URL Hostname string Queue *queue.Queue HttpClient *retryablehttp.Client Browser *rod.Browser } // NewCrawlSessionWithURL creates and initializes a new crawl session for the specified URL. // It performs the following initialization steps: // 1. Creates a context with optional timeout based on CrawlDuration setting // 2. Parses the target URL and extracts the hostname // 3. Initializes the request queue with the configured strategy // 4. Enqueues the initial URL and any known files for the target // 5. Sets up the HTTP client with response parsing callbacks // // Returns the initialized CrawlSession or an error if initialization fails. func (s *Shared) NewCrawlSessionWithURL(URL string) (*CrawlSession, error) { ctx, cancel := context.WithCancel(context.Background()) if s.Options.Options.CrawlDuration.Seconds() > 0 { //nolint ctx, cancel = context.WithTimeout(ctx, s.Options.Options.CrawlDuration) } parsed, err := urlutil.Parse(URL) if err != nil { cancel() return nil, errkit.Wrap(err, "could not parse root URL") } hostname := parsed.Hostname() queue, err := queue.New(s.Options.Options.Strategy, s.Options.Options.Timeout) if err != nil { cancel() return nil, err } queue.Push(&navigation.Request{Method: http.MethodGet, URL: URL, Depth: 0, SkipValidation: true}, 0) if s.KnownFiles != nil { navigationRequests, err := s.KnownFiles.Request(URL) if err != nil { gologger.Warning().Msgf("Could not parse known files for %s: %s\n", URL, err) } s.Enqueue(queue, navigationRequests...) } httpclient, _, err := BuildHttpClient(s.Options.Dialer, s.Options.Options, func(resp *http.Response, depth int) { body, _ := io.ReadAll(resp.Body) reader, _ := goquery.NewDocumentFromReader(bytes.NewReader(body)) var technologyKeys []string if s.Options.Wappalyzer != nil { technologies := s.Options.Wappalyzer.Fingerprint(resp.Header, body) technologyKeys = mapsutil.GetKeys(technologies) } navigationResponse := &navigation.Response{ Depth: depth + 1, RootHostname: hostname, Resp: resp, Body: string(body), Reader: reader, Technologies: technologyKeys, StatusCode: resp.StatusCode, Headers: utils.FlattenHeaders(resp.Header), KnowledgeBase: s.Options.ClassifyPage(string(body)), } navigationRequests := s.Options.Parser.ParseResponse(navigationResponse) s.Enqueue(queue, navigationRequests...) }) if err != nil { cancel() return nil, errkit.Wrap(err, "could not create http client") } crawlSession := &CrawlSession{ Ctx: ctx, CancelFunc: cancel, URL: parsed.URL, Hostname: hostname, Queue: queue, HttpClient: httpclient, } return crawlSession, nil } // DoRequestFunc is a function type for executing navigation requests. // Implementations should perform the actual HTTP request or browser navigation // and return the response or an error. This allows different crawling strategies // (standard HTTP vs. headless browser) to provide their own request logic. type DoRequestFunc func(crawlSession *CrawlSession, req *navigation.Request) (*navigation.Response, error) // Do executes the main crawling loop for the given crawl session. // It processes items from the queue concurrently (respecting the Concurrency limit), // validates each request (URL format, path filters, scope), applies rate limiting // and delays, executes the request using the provided doRequest function, writes // results to output, and enqueues any newly discovered URLs from responses. // // The method returns when the queue is empty or the session context is cancelled // (due to timeout or manual cancellation). Returns an error if the context is cancelled. func (s *Shared) Do(crawlSession *CrawlSession, doRequest DoRequestFunc) error { wg := sizedwaitgroup.New(s.Options.Options.Concurrency) for item := range crawlSession.Queue.Pop() { if ctxErr := crawlSession.Ctx.Err(); ctxErr != nil { return ctxErr } req, ok := item.(*navigation.Request) if !ok { continue } if !utils.IsURL(req.URL) { if s.Options.Options.OnSkipURL != nil { s.Options.Options.OnSkipURL(req.URL) } gologger.Debug().Msgf("`%v` not a url. skipping", req.URL) continue } if !s.Options.ValidatePath(req.URL) { gologger.Debug().Msgf("`%v` filtered path. skipping", req.URL) continue } inScope, scopeErr := s.Options.ValidateScope(req.URL, crawlSession.Hostname) if scopeErr != nil { gologger.Debug().Msgf("Error validating scope for `%v`: %v. skipping", req.URL, scopeErr) continue } if !req.SkipValidation && !inScope { gologger.Debug().Msgf("`%v` not in scope. skipping", req.URL) continue } wg.Add() // gologger.Debug().Msgf("Visiting: %v", req.URL) // not sure if this is needed go func() { defer wg.Done() s.Options.RateLimit.Take() // Delay if the user has asked for it if s.Options.Options.Delay > 0 { time.Sleep(time.Duration(s.Options.Options.Delay) * time.Second) } resp, err := doRequest(crawlSession, req) if inScope { s.Output(req, resp, err) } if err != nil { gologger.Warning().Msgf("Could not request seed URL %s: %s\n", req.URL, err) outputError := &output.Error{ Timestamp: time.Now(), Endpoint: req.RequestURL(), Source: req.Source, Error: err.Error(), } _ = s.Options.OutputWriter.WriteErr(outputError) return } if resp == nil || resp.Resp == nil || resp.Reader == nil { return } if s.Options.Options.DisableRedirects && resp.IsRedirect() { return } navigationRequests := s.Options.Parser.ParseResponse(resp) s.Enqueue(crawlSession.Queue, navigationRequests...) }() } wg.Wait() return nil } ================================================ FILE: pkg/engine/common/error.go ================================================ package common import "errors" var ErrOutOfScope = errors.New("out of scope") ================================================ FILE: pkg/engine/common/http.go ================================================ package common import ( "context" "crypto/tls" "net" "net/http" "net/url" "time" "github.com/projectdiscovery/fastdialer/fastdialer" "github.com/projectdiscovery/fastdialer/fastdialer/ja3/impersonate" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/types" "github.com/projectdiscovery/retryablehttp-go" "github.com/projectdiscovery/utils/errkit" proxyutil "github.com/projectdiscovery/utils/proxy" ) type RedirectCallback func(resp *http.Response, depth int) // BuildHttpClient builds a http client based on a profile func BuildHttpClient(dialer *fastdialer.Dialer, options *types.Options, redirectCallback RedirectCallback) (*retryablehttp.Client, *fastdialer.Dialer, error) { // Single Host retryablehttpOptions := retryablehttp.DefaultOptionsSingle retryablehttpOptions.RetryMax = options.Retries transport := &http.Transport{ DialContext: dialer.Dial, DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) { if options.TlsImpersonate { return dialer.DialTLSWithConfigImpersonate(ctx, network, addr, &tls.Config{InsecureSkipVerify: true, MinVersion: tls.VersionTLS10}, impersonate.Random, nil) } return dialer.DialTLS(ctx, network, addr) }, MaxIdleConns: 100, MaxIdleConnsPerHost: 10, MaxConnsPerHost: 100, TLSClientConfig: &tls.Config{ Renegotiation: tls.RenegotiateOnceAsClient, InsecureSkipVerify: true, }, DisableKeepAlives: false, } // Attempts to overwrite the dial function with the socks proxied version if proxyURL, err := url.Parse(options.Proxy); options.Proxy != "" && err == nil { if ok, err := proxyutil.IsBurp(options.Proxy); err == nil && ok { transport.TLSClientConfig.MaxVersion = tls.VersionTLS12 } transport.Proxy = http.ProxyURL(proxyURL) } client := retryablehttp.NewWithHTTPClient(&http.Client{ Transport: transport, Timeout: time.Duration(options.Timeout) * time.Second, CheckRedirect: func(req *http.Request, via []*http.Request) error { if options.DisableRedirects { return http.ErrUseLastResponse } if len(via) == 10 { return errkit.New("stopped after 10 redirects") } depth, ok := req.Context().Value(navigation.Depth{}).(int) if !ok { depth = 2 } if redirectCallback != nil { redirectCallback(req.Response, depth) } return nil }, }, retryablehttpOptions) client.CheckRetry = retryablehttp.HostSprayRetryPolicy() return client, dialer, nil } ================================================ FILE: pkg/engine/engine.go ================================================ package engine type Engine interface { Crawl(string) error Close() error } ================================================ FILE: pkg/engine/headless/TODOS.md ================================================ # 🗺️ Headless-Crawler Road-Map --- ## 🥇 Core Improvements (High-impact / first passes) - [ ] After clicking on elements there isn't enough wait time to reflect SPA navigation - [ ] **Replace exact DOM hash with perceptual fingerprint** - Stage 1: keep current SHA-256 on stripped DOM for cheap exact-match. - Stage 2: compute 64-bit SimHash/MinHash over 3–4-word shingles of the stripped DOM; treat pages equal when Hamming distance ≤ 3 bits. - Stage 3 (optional): if SimHash inconclusive, take a low-res screenshot and compare pHash/dHash. - Store `ExactHash` & `FuzzyHash`; update graph comparison logic. - [ ] **Robust “page ready” detector** - Inject `MutationObserver` + `requestIdleCallback`. - Resolve when: * no DOM mutation for N ms **and** * `location.href`/`history.length`/`` stable. - Wrap as `page.WaitForRouteChange()` and replace `WaitPageLoadHeuristics`. - [ ] **Lazy-load / infinite-scroll support** - Loop: `scrollBy(0, viewportHeight*0.9)` until `scrollHeight` stops growing. - Fallback: IntersectionObserver on a sentinel div. - [ ] **Capture all secondary resource navigations** - Enable `FetchEnable` + `Network.*` events. - Record: XHR/Fetch URLs, WebSocket, EventSource, Service-Worker scripts. - Feed new, in-scope URLs into the crawl queue (dedup by host/path). --- ## 🥈 Mid-term Enhancements - [ ] **Dynamic form-filling** - Generate values based on `type`, `pattern`, `min`, `max`, `maxlength`, `required`. - Pluggable `ValueProvider` interface for site-specific logic. - [ ] **Site adapters / hooks** - Allow user-supplied Go or JS snippets per hostname (e.g. dismiss cookie wall, auto login, click “load more”). - [ ] **Concurrent tab execution** - Worker pool consuming the action queue. - Use multiple `rod.Page` instances (shared browser) – make `CrawlGraph` concurrency-safe. - [ ] **Smart time-out & retry budgets** - Adaptive timeout: first nav longer, later ones shorter; one automatic reload on stall. - [ ] **Viewport variants** - Crawl again at typical mobile (390×844) & tablet (768×1024) sizes to reveal responsive content. - [ ] **Memory & process recycling** - Close background tabs after use. - If Chrome RSS > threshold, restart browser (persist cookies if needed). - [ ] **Anti-bot hardening** - Spoof fonts, canvas & audio contexts. - Rotate realistic UA strings, languages, `hardwareConcurrency`. - Optional headful mode via XVFB to enable GPU paths. --- ## 🥉 Nice-to-have / Advanced - [ ] **Export crawl sessions** - HAR or WARC output in parallel to existing JSON. - [ ] **JS coverage tracking** - `Profiler.startPreciseCoverage` → know which scripts never executed. - [ ] **Metrics & health** - Prometheus counters (pages, active tabs, JS errors, nav errors). - `/debug/pprof` enabled by default. - [ ] **TLS / proxy flexibility** - Accept custom CA bundle, client certs, upstream proxy rotation. - [ ] **Sandboxing & security** - Run Chrome under seccomp / user-namespaces or separate UID/GID automatically. - [ ] **Graceful crash recovery** - Detect `Page.crashed` / `Browser.disconnected`; re-spawn browser, resume queue. ---------------------------------- Claude OPUS info below: ## 🎯 Critical Bug Fixes & Edge Cases - [ ] **Handle iframe content extraction** - Cross-origin iframe detection and flagging - Same-origin iframe DOM traversal - Nested iframe support (up to N levels) - [ ] **WebComponent & Shadow DOM support** - Detect custom elements with shadow roots - Traverse open shadow DOMs for form/link discovery - Handle slot-based content projection - [ ] **Multi-window/tab detection** - Track `window.open()` calls that bypass current hooks - Handle popup windows that close parent - Manage tab focus for proper event firing ## 🔐 Authentication & Session Management - [ ] **Auth state detection** - Detect login/logout UI patterns - Monitor cookie changes for session tracking - Implement auth health checks between actions - [ ] **Multi-step auth flows** - OAuth redirect handling - 2FA/MFA detection and waiting - SAML/SSO flow support - [ ] **Session persistence** - Save/restore cookies between crawls - Handle JWT token refresh - Detect and handle session timeouts ## 🎪 Advanced Interaction Patterns - [ ] **Complex UI interactions** - Drag & drop detection and execution - File upload with generated test files - Multi-select and combo-box handling - Date/time picker interaction - [ ] **Keyboard navigation support** - Tab-order based discovery - Keyboard shortcut detection (Ctrl+K, etc.) - Access key enumeration - [ ] **Touch/mobile gestures** - Swipe detection for mobile views - Long-press context menus - Pinch-to-zoom aware navigation ## 📊 Analytics & Monitoring - [ ] **Performance metrics** - Page load time tracking - JavaScript execution overhead - Memory usage per page state - Network request waterfalls - [ ] **Crawl quality metrics** - Code coverage per domain - Unique vs duplicate state ratio - Action success/failure rates - Depth distribution analysis - [ ] **Error tracking** - JavaScript console error capture - Network error categorization - CSP violation logging - Failed action root cause analysis ## 🧠 Smart Crawling Features - [ ] **ML-based duplicate detection** - Train model on visual similarity - Semantic HTML structure comparison - Learn site-specific patterns - [ ] **Priority queue optimization** - High-value path prediction - Anomaly detection for interesting states - Dynamic depth adjustment based on yield - [ ] **State space reduction** - Identify and prune redundant actions - Detect pagination patterns - Group similar forms (search variations) ## 🛡️ Security & Compliance - [ ] **CAPTCHA handling** - Detection of common CAPTCHA providers - Integration points for solving services - Graceful degradation strategies - [ ] **Rate limiting & politeness** - Per-domain request throttling - Respect robots.txt for headless - Adaptive delays based on response times - [ ] **Privacy compliance** - PII detection in forms - GDPR banner interaction - Data retention policies ## 🔌 Integration Features - [ ] **API extraction** - GraphQL query/mutation detection - REST endpoint parameter learning - WebSocket message format detection - [ ] **Export formats** - OpenAPI spec generation from discoveries - Postman collection export - Burp Suite state file compatibility - [ ] **Workflow recording** - Playwright/Puppeteer script generation - Selenium IDE format export - Custom DSL for replay ## 🚀 Performance Optimizations - [ ] **Rendering optimizations** - Disable images/fonts for text-only analysis - Viewport-based lazy rendering - CPU throttling for battery saving - [ ] **Caching layer** - DOM diff caching - Screenshot perceptual hashes - JavaScript execution results - [ ] **Distributed crawling** - Work queue distribution - State synchronization protocol - Result aggregation pipeline ## 🔧 Developer Experience - [ ] **Debug tooling** - Live crawl visualization - State graph explorer UI - Action replay debugger - [ ] **Configuration management** - Per-site config profiles - A/B testing different strategies - Hot-reload of site adapters - [ ] **Testing infrastructure** - Headless crawler unit tests - Integration tests with test sites - Regression detection suite state.go is the crawler’s “state-manager”. Everything else in the headless package (browser wrappers, normalizer, graph, diagnostics) either feeds data into it or asks it to restore a known state. To make the crawler scalable, reliable and de-dupe friendly the file should be responsible for exactly three things: 1. Build a reproducible fingerprint (“state ID”) for the current page. 2. Persist the surrounding metadata that we need to replay that state later. 3. Provide deterministic, cheapest-first logic to get back to any recorded state. Below is a complete design that meets those goals and leaves room for future TODOs. ──────────────────────────────────────────────────────────────────────────── 1. Fingerprint strategy (page → id) ──────────────────────────────────────────────────────────────────────────── A. Canonical DOM extraction • Use the existing domNormalizer (strip scripts, styles, dynamic IDs etc.). • Remove all transient event-attributes (`onclick`, `onmouseover`, …). • Collapse whitespace → single space. B. Two-tier hash • ExactHash = SHA-256(strippedDOM). • FuzzyHash = SimHash64(4-word shingles of strippedDOM). • Treat states equal if - ExactHash matches, or - Hamming(FuzzyHash, other.FuzzyHash) ≤ 3 bits. • Persist both; the graph layer deduplicates on (ExactHash || close-enough FuzzyHash). C. Optional visual fallback • If comparison is inconclusive (≥ 4 bit distance but DOM len < 1 MiB) → low-res screenshot, pHash/dHash → same threshold logic. • Executed lazily to avoid perf hit. Resulting struct: type PageState struct { ExactHash string // always present FuzzyHash uint64 // present if SimHash computed URL string Title string Depth int StrippedDOM string NavigationAction *Action // edge that produced this state Timestamp time.Time } –––– Advantages • SimHash makes minor DOM variations (ads, CSRF tokens) resolve to the same state, reducing graph size. • Screenshot hash catches SPA view switches that don’t touch the DOM tree much but look different. ──────────────────────────────────────────────────────────────────────────── 2. Metadata collection (page → PageState) ──────────────────────────────────────────────────────────────────────────── Algorithm newPageState(page, causingAction): 1. Grab `page.Info()`; bail out if URL is empty or about:blank. 2. outerHTML := page.HTML(). 3. stripped := domNormalizer.Apply(outerHTML). 4. Build PageState as above. 5. Compute hashes as described. 6. Diagnostics hook (save stripped DOM, screenshots, etc.). 7. Return the fully populated PageState. Edge cases handled: • Empty page → custom ErrEmptyPage (already present). • Non-deterministic DOM normalizer failure → bubbled up with context. ──────────────────────────────────────────────────────────────────────────── 3. Return-to-origin algorithm (current page, targetOriginID) → (pageID, error) ──────────────────────────────────────────────────────────────────────────── Keep the existing three-level approach but hard-code their priority and exit conditions. Step 0 Fast-fail: if currentID == target → done. Step 1 Element re-use • If `action.Element` is non-nil, locate by XPath, ensure Visible & Interactable, *plus* DOM equality check under the canonicalizer to avoid false positives. • If match, return targetOriginID. Step 2 Browser history • page.GetNavigationHistory() • Walk back until (url == origin.URL && title == origin.Title). • Limit: max 10 steps to avoid long loops. • After each back() call wait with WaitForRouteChange() (new detector described below). • Recompute fingerprint; if equal (exact or fuzzy) → success. Step 3 Graph shortest path • crawlerGraph.ShortestPath(currentID, targetID). • If unreachable, retry from emptyPageHash (fresh tab). • Execute each Action; after each, WaitForRouteChange(). • After final step verify state (same equality logic as Step 2). • Failure → ErrNoNavigationPossible. Enhancements • Cache the computed “distance” between two states; next call can skip graph search. • Record statistics (#navigationBackSuccessByMethod) to tune the priority order. ──────────────────────────────────────────────────────────────────────────── 4. “Page ready” detector (WaitForRouteChange) ──────────────────────────────────────────────────────────────────────────── Replace the brittle WaitPageLoadHeuristics with: Injected JS once per tab: const idle = () => new Promise(res => { const done = () => { obs.disconnect(); res(); }; let t; const reset = () => { clearTimeout(t); t = setTimeout(done, 300); }; const obs = new MutationObserver(reset); obs.observe(document, {subtree: true, childList: true, attributes: true}); reset(); }); window.__katanaReady = () => Promise.all([ idle(), new Promise(r => requestIdleCallback(r, {timeout: 5000})) ]); Go side: func (p *BrowserPage) WaitForRouteChange() error { ctx, cancel := context.WithTimeout(p.ctx, 15*time.Second) defer cancel() return rod.Try(func() { p.Eval(ctx, `await window.__katanaReady()`) }) } Detects route changes, SPA navigations, AJAX content, infinite scroll “settling”, etc. ──────────────────────────────────────────────────────────────────────────── 5. Extensibility hooks ──────────────────────────────────────────────────────────────────────────── • FingerprintStrategy interface so users can plug in custom SimHash/Screenshot logic. • ValueProvider & SiteAdapter interfaces already planned can depend on PageState to decide actions. • Diagnostics sink gets PageState + serialized Action graph for offline visualizer. ──────────────────────────────────────────────────────────────────────────── 6. Migration plan ──────────────────────────────────────────────────────────────────────────── 1. Stage 1 (quick): keep old sha256 flow, introduce struct fields & interface but stub SimHash. 2. Stage 2: integrate open-source SimHash library, enable fuzzy comparator in graph. 3. Stage 3: optional pHash path guarded by feature flag. 4. Replace WaitPageLoadHeuristics with WaitForRouteChange(). 5. Add metrics around navigation-back success. This architecture keeps state.go focused, removes hidden coupling, and sets up the crawler for future road-map items (concurrent tabs, adapters, ML dedup, etc.) while remaining incremental enough to merge in small PRs. ================================================ FILE: pkg/engine/headless/browser/browser.go ================================================ package browser import ( "bytes" "context" "encoding/base64" "fmt" "io" "log/slog" "net/http" "net/http/httputil" "os" "os/user" "strconv" "strings" "sync" "time" "github.com/PuerkitoBio/goquery" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/launcher/flags" "github.com/go-rod/rod/lib/proto" rodutils "github.com/go-rod/rod/lib/utils" "github.com/pkg/errors" "github.com/projectdiscovery/katana/pkg/engine/headless/browser/cookie" "github.com/projectdiscovery/katana/pkg/engine/headless/browser/stealth" "github.com/projectdiscovery/katana/pkg/engine/headless/js" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/utils" "github.com/rs/xid" ) // Launcher is a high level controller to launch browsers // and do the execution on them. type Launcher struct { browserPool rod.Pool[BrowserPage] opts LauncherOptions } // LauncherOptions contains options for the launcher type LauncherOptions struct { ChromiumPath string MaxBrowsers int PageMaxTimeout time.Duration ShowBrowser bool NoSandbox bool Proxy string SlowMotion bool Trace bool CookieConsentBypass bool ChromeUser *user.User // optional chrome user to use ScopeValidator ScopeValidator RequestCallback func(*output.Result) } type ScopeValidator func(string) bool // NewLauncher returns a new launcher instance func NewLauncher(opts LauncherOptions) (*Launcher, error) { l := &Launcher{ opts: opts, browserPool: rod.NewPool[BrowserPage](opts.MaxBrowsers), } return l, nil } func (l *Launcher) ScopeValidator() ScopeValidator { return l.opts.ScopeValidator } func (l *Launcher) launchBrowserWithDataDir(userDataDir string) (*rod.Browser, error) { chromeLauncher := launcher.New(). Leakless(true). Set("disable-gpu", "true"). Set("ignore-certificate-errors", "true"). Set("disable-crash-reporter", "true"). Set("disable-notifications", "true"). Set("hide-scrollbars", "true"). Set("window-size", fmt.Sprintf("%d,%d", 1080, 1920)). Set("mute-audio", "true"). Set("incognito", "true"). Delete("use-mock-keychain"). Delete("disable-ipc-flooding-protection"). Headless(true) for _, flag := range headlessFlags { splitted := strings.TrimPrefix(flag, "--") values := strings.Split(splitted, "=") if len(values) == 2 { chromeLauncher = chromeLauncher.Set(flags.Flag(values[0]), strings.Split(values[1], ",")...) } else { chromeLauncher = chromeLauncher.Set(flags.Flag(splitted), "true") } } if l.opts.Proxy != "" { chromeLauncher = chromeLauncher.Proxy(l.opts.Proxy) } if l.opts.NoSandbox { chromeLauncher = chromeLauncher.NoSandbox(true) } if l.opts.ShowBrowser { chromeLauncher = chromeLauncher.Headless(false) } if l.opts.ChromiumPath != "" { chromeLauncher = chromeLauncher.Bin(l.opts.ChromiumPath) } if userDataDir != "" { chromeLauncher = chromeLauncher.UserDataDir(userDataDir) } launcherURL, err := chromeLauncher.Launch() if err != nil { return nil, err } browser := rod.New(). ControlURL(launcherURL) if l.opts.Trace { browser = browser.Trace(true) } if l.opts.SlowMotion { browser = browser.SlowMotion(1 * time.Second) } if browserErr := browser.Connect(); browserErr != nil { return nil, browserErr } return browser, nil } // Close closes the launcher func (l *Launcher) Close() { l.browserPool.Cleanup(func(b *BrowserPage) { b.cancel() b.CloseBrowserPage() }) close(l.browserPool) } // BrowserPage is a combination of a browser and a page type BrowserPage struct { *rod.Page Browser *rod.Browser cancel context.CancelFunc userDataDir string launcher *Launcher } // WaitOptions controls how WaitPageLoadHeurisitics determines navigation completion. // All durations are conservative defaults and can be tuned later via package-level variables // or future setter methods (kept simple here to avoid breaking public API). type WaitOptions struct { URLPollInterval time.Duration // interval between successive URL polls URLPollTimeout time.Duration // how long to keep polling before giving up on URL change PostChangeWait time.Duration // small grace period after URL change for late requests IdleWait time.Duration // network-idle window when no URL change happened DOMStableWait time.Duration // DOM-stable window (used after idle) MaxTimeout time.Duration // absolute upper bound for all waiting } // defaultWaitOptions are derived from empirical measurements on modern SPA pages. var defaultWaitOptions = WaitOptions{ URLPollInterval: 100 * time.Millisecond, URLPollTimeout: 2 * time.Second, PostChangeWait: 300 * time.Millisecond, IdleWait: 1 * time.Second, DOMStableWait: 1 * time.Second, MaxTimeout: 15 * time.Second, } // WaitPageLoadHeurisitics waits for the page to load using multiple heuristics. // Strategy order: // 1. Wait for initial load event (covers classic navigation & first paint). // 2. Poll for a URL change – the strongest signal on SPAs with client-side routing. // 3. If URL changes, wait a short grace period + network-idle window. // 4. If URL doesn't change, fall back to network-idle + DOM-stable windows. // // This keeps fast pages fast while still succeeding on noisy, long-running SPAs. func (b *BrowserPage) WaitPageLoadHeurisitics() error { opts := defaultWaitOptions chained := b.Timeout(opts.MaxTimeout) // 1. Wait for the basic load event (DOMContentLoaded / load). _ = chained.WaitLoad() // 2. Capture the current URL so we can detect route changes. urlVal, _ := b.Eval("() => window.location.href") startURL := "" if urlVal != nil { startURL = urlVal.Value.Str() } // 3. Poll for a different URL for up to URLPollTimeout. urlChanged := false if startURL != "" { pollCount := int(opts.URLPollTimeout / opts.URLPollInterval) for i := 0; i < pollCount; i++ { time.Sleep(opts.URLPollInterval) cur, err := b.Eval("() => window.location.href") if err == nil && cur != nil && cur.Value.Str() != startURL { urlChanged = true break } } } if urlChanged { // 4a. URL changed – short grace period then network idle & done. _ = chained.WaitIdle(opts.PostChangeWait) return nil } // 4b. URL didn't change – fall back to broader heuristics. _ = chained.WaitIdle(opts.IdleWait) _ = b.WaitNewStable(opts.DOMStableWait) return nil } // WaitPageLoadHeuristicsFallback provides the enhanced timeouts for complex navigation func (b *BrowserPage) WaitPageLoadHeuristicsFallback() error { chainedTimeout := b.Timeout(20 * time.Second) _ = chainedTimeout.WaitLoad() _ = chainedTimeout.WaitIdle(4 * time.Second) _ = b.WaitNewStable(2 * time.Second) return nil } // WaitStable waits until the page is stable for d duration. func (p *BrowserPage) WaitNewStable(d time.Duration) error { // Enforce an upper-bound on how long we will wait for the page to become // stable. We simply reuse the heuristic window (d) and give the combined // operation 2× that duration. This guarantees that callers will be // released after a finite time instead of blocking forever when a page // keeps a long-lived connection open (analytics beacons, WebSockets, etc.). chained := p.Timeout(2 * d) var err error setErr := sync.Once{} rodutils.All(func() { e := chained.WaitLoad() setErr.Do(func() { err = e }) }, func() { chained.WaitRequestIdle(d, nil, []string{}, nil)() }, func() { e := chained.WaitDOMStable(d, 0) setErr.Do(func() { err = e }) })() return err } func (l *Launcher) createBrowserPageFunc() (*BrowserPage, error) { // Create unique temp userDataDir for this browser instance var tempDir string shouldCleanup := true // Deferred cleanup function that will be set after tempDir creation defer func() { if shouldCleanup && tempDir != "" { _ = os.RemoveAll(tempDir) } }() if l.opts.ChromeUser != nil { var err error tempDir, err = os.MkdirTemp(l.opts.ChromeUser.HomeDir, "chrome-data-*") if err != nil { return nil, errors.Wrap(err, "could not create temporary chrome data directory") } uid, err := strconv.Atoi(l.opts.ChromeUser.Uid) if err != nil { return nil, errors.Wrap(err, "invalid user ID") } gid, err := strconv.Atoi(l.opts.ChromeUser.Gid) if err != nil { return nil, errors.Wrap(err, "invalid group ID") } if err := os.Chown(tempDir, uid, gid); err != nil { return nil, errors.Wrap(err, "could not change ownership of chrome data directory") } } else { var err error tempDir, err = os.MkdirTemp("", "katana-chrome-data-*") if err != nil { return nil, errors.Wrap(err, "could not create temporary chrome data directory") } } browser, err := l.launchBrowserWithDataDir(tempDir) if err != nil { return nil, err } page, err := browser.Page(proto.TargetCreateTarget{}) if err != nil { return nil, errors.Wrap(err, "could not create new page") } successfulPageCreation := false defer func() { if !successfulPageCreation { _ = page.Close() _ = browser.Close() } }() page = page.Sleeper(func() rodutils.Sleeper { return backoffCountSleeper(100*time.Millisecond, 1*time.Second, 3, func(d time.Duration) time.Duration { return d * 1 }) }) ctx := page.GetContext() cancelCtx, cancel := context.WithCancel(ctx) page = page.Context(cancelCtx) browserPage := &BrowserPage{ Page: page, Browser: browser, launcher: l, cancel: cancel, userDataDir: tempDir, } if err := browserPage.handlePageDialogBoxes(); err != nil { return nil, err } // Add stealth evasion JS _, err = page.EvalOnNewDocument(stealth.JS) if err != nil { return nil, errors.Wrap(err, "could not initialize stealth") } err = js.InitJavascriptEnv(page) if err != nil { return nil, errors.Wrap(err, "could not initialize javascript env") } // Success - cancel the deferred cleanup successfulPageCreation = true shouldCleanup = false return browserPage, nil } // GetPageFromPool returns a page from the pool func (l *Launcher) GetPageFromPool() (*BrowserPage, error) { browserPage, err := l.browserPool.Get(l.createBrowserPageFunc) if err != nil { return nil, err } // TODO: should we check if the browser is alive because sometimes it // might die? return browserPage, nil } // backoffCountSleeper returns a sleeper that uses backoff strategy but stops after max attempts. // It combines the functionality of BackoffSleeper and CountSleeper. func backoffCountSleeper(initInterval, maxInterval time.Duration, maxAttempts int, algorithm func(time.Duration) time.Duration) rodutils.Sleeper { backoff := rodutils.BackoffSleeper(initInterval, maxInterval, algorithm) count := rodutils.CountSleeper(maxAttempts) return rodutils.EachSleepers(backoff, count) } func (b *BrowserPage) handlePageDialogBoxes() error { err := proto.FetchEnable{ Patterns: []*proto.FetchRequestPattern{ { URLPattern: "*", RequestStage: proto.FetchRequestStageResponse, }, }, }.Call(b.Page) if err != nil { return errors.Wrap(err, "could not enable fetch domain") } go b.EachEvent( func(e *proto.PageJavascriptDialogOpening) { _ = proto.PageHandleJavaScriptDialog{ Accept: true, PromptText: xid.New().String(), }.Call(b.Page) }, func(e *proto.FetchRequestPaused) { if b.launcher.opts.CookieConsentBypass { // Check if request should be blocked by cookie consent rules var originStr string if origin, ok := e.Request.Headers["Origin"]; ok { originStr = origin.Str() } if cookie.ShouldBlockRequest(e.Request.URL, e.ResourceType, originStr) { _ = proto.FetchFailRequest{ RequestID: e.RequestID, ErrorReason: proto.NetworkErrorReasonBlockedByClient, }.Call(b.Page) return } } if e.ResponseStatusCode == nil || e.ResponseErrorReason != "" || (*e.ResponseStatusCode >= 301 && *e.ResponseStatusCode <= 308) { if err := fetchContinueRequest(b.Page, e); err != nil { slog.Warn("fetchContinueRequest failed", "error", err) } return } body, err := fetchGetResponseBody(b.Page, e) if err != nil { // Continue the request even if we can't get the body if err := fetchContinueRequest(b.Page, e); err != nil { slog.Warn("fetchContinueRequest failed", "error", err) } return } if err := fetchContinueRequest(b.Page, e); err != nil { slog.Warn("fetchContinueRequest failed", "error", err) } httpreq, err := netHTTPRequestFromProto(e.Request) if err != nil { return } rawBytesRequest, _ := httputil.DumpRequestOut(httpreq, true) req := navigation.Request{ Method: httpreq.Method, URL: httpreq.URL.String(), Body: e.Request.PostData, Headers: utils.FlattenHeaders(httpreq.Header), Raw: string(rawBytesRequest), } httpresp := netHTTPResponseFromProto(e, body) httpresp.Request = httpreq rawBytesResponse, _ := httputil.DumpResponse(httpresp, true) doc, err := goquery.NewDocumentFromReader(bytes.NewReader(body)) if err != nil { slog.Warn("could not parse response body", "error", err) } resp := &navigation.Response{ Body: string(body), StatusCode: httpresp.StatusCode, Headers: utils.FlattenHeaders(httpresp.Header), Raw: string(rawBytesResponse), ContentLength: httpresp.ContentLength, Resp: httpresp, Reader: doc, } if b.launcher.opts.RequestCallback != nil { b.launcher.opts.RequestCallback(&output.Result{ Timestamp: time.Now(), Request: &req, Response: resp, }) } }, )() return nil } func fetchContinueRequest(page *rod.Page, e *proto.FetchRequestPaused) error { return proto.FetchContinueRequest{ RequestID: e.RequestID, }.Call(page) } // fetchGetResponseBody get request body. func fetchGetResponseBody(page *rod.Page, e *proto.FetchRequestPaused) ([]byte, error) { m := proto.FetchGetResponseBody{ RequestID: e.RequestID, } r, err := m.Call(page) if err != nil { return nil, err } if !r.Base64Encoded { return []byte(r.Body), nil } bs, err := base64.StdEncoding.DecodeString(r.Body) if err != nil { return nil, err } return bs, nil } func netHTTPRequestFromProto(e *proto.NetworkRequest) (*http.Request, error) { req, err := http.NewRequest(e.Method, e.URL, nil) if err != nil { return nil, errors.Wrap(err, "could not create new request") } for k, v := range e.Headers { req.Header.Set(k, v.Str()) } if e.PostData != "" { req.Body = io.NopCloser(strings.NewReader(e.PostData)) req.ContentLength = int64(len(e.PostData)) } return req, nil } func netHTTPResponseFromProto(e *proto.FetchRequestPaused, body []byte) *http.Response { httpresp := &http.Response{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, Header: make(http.Header), StatusCode: *e.ResponseStatusCode, Status: e.ResponseStatusText, Body: io.NopCloser(bytes.NewReader(body)), ContentLength: int64(len(body)), } for _, header := range e.ResponseHeaders { httpresp.Header.Set(header.Name, header.Value) } return httpresp } func (l *Launcher) PutBrowserToPool(browser *BrowserPage) { // Discard pages that hit a deadline or were cancelled to avoid immediately // returning a poisoned page that will fail every subsequent call. if cerr := browser.Page.GetContext().Err(); cerr != nil { browser.cancel() browser.CloseBrowserPage() return } // If the browser is not connected, close it if !isBrowserConnected(browser.Browser) { browser.cancel() browser.CloseBrowserPage() return } pages, err := browser.Browser.Pages() if err != nil { browser.cancel() browser.CloseBrowserPage() return } currentPageID := browser.TargetID for _, page := range pages { if page.TargetID != currentPageID { _ = page.Close() } } l.browserPool.Put(browser) } func isBrowserConnected(browser *rod.Browser) bool { getVersionResult, err := proto.BrowserGetVersion{}.Call(browser) if err != nil { return false } if getVersionResult == nil || getVersionResult.Product == "" { return false } return true } func (b *BrowserPage) CloseBrowserPage() { _ = b.Close() _ = b.Browser.Close() if b.userDataDir != "" { _ = os.RemoveAll(b.userDataDir) } } // taken from playwright var headlessFlags = []string{ "--disable-field-trial-config", // https://source.chromium.org/chromium/chromium/src/+/main:testing/variations/README.md "--disable-background-networking", "--enable-features=NetworkService,NetworkServiceInProcess", "--disable-background-timer-throttling", "--disable-backgrounding-occluded-windows", "--disable-back-forward-cache", // Avoids surprises like main request not being intercepted during page.goBack(). "--disable-breakpad", "--disable-client-side-phishing-detection", "--disable-component-extensions-with-background-pages", "--disable-component-update", // Avoids unneeded network activity after startup. "--no-default-browser-check", "--disable-default-apps", "--disable-dev-shm-usage", "--disable-extensions", // AvoidUnnecessaryBeforeUnloadCheckSync - https://github.com/microsoft/playwright/issues/14047 // Translate - https://github.com/microsoft/playwright/issues/16126 // HttpsUpgrades - https://github.com/microsoft/playwright/pull/27605 // PaintHolding - https://github.com/microsoft/playwright/issues/28023 "--disable-features=ImprovedCookieControls,LazyFrameLoading,GlobalMediaControls,DestroyProfileOnBrowserClose,MediaRouter,DialMediaRouteProvider,AcceptCHFrame,AutoExpandDetailsElement,CertificateTransparencyComponentUpdater,AvoidUnnecessaryBeforeUnloadCheckSync,Translate,HttpsUpgrades,PaintHolding", "--allow-pre-commit-input", "--disable-hang-monitor", "--disable-popup-blocking", "--disable-prompt-on-repost", "--disable-renderer-backgrounding", "--force-color-profile=srgb", "--metrics-recording-only", "--no-first-run", "--enable-automation", "--password-store=basic", "--use-mock-keychain", // See https://chromium-review.googlesource.com/c/chromium/src/+/2436773 "--no-service-autorun", "--export-tagged-pdf", // https://chromium-review.googlesource.com/c/chromium/src/+/4853540 "--disable-search-engine-choice-screen", // https://issues.chromium.org/41491762 "--unsafely-disable-devtools-self-xss-warnings", } ================================================ FILE: pkg/engine/headless/browser/cookie/cookie.go ================================================ // Package cookie implements cookie consent handling for the // crawler. Its a partial port of I-Still-Dont-Care-About-Cookies. package cookie import ( _ "embed" "encoding/json" "sort" "strings" "github.com/go-rod/rod/lib/proto" ) type CookieConsentBlockRequest struct { ID int `json:"id"` Priority int `json:"priority"` Condition Condition `json:"condition,omitempty"` } type Action struct { Type string `json:"type"` } type Condition struct { URLFilter string `json:"urlFilter"` ResourceTypes []string `json:"resourceTypes"` InitiatorDomains []string `json:"initiatorDomains"` ExcludedInitiatorDomains []string `json:"excludedInitiatorDomains"` } //go:embed rules.json var rules []byte var cookieConsentBlockRequests []CookieConsentBlockRequest func init() { err := json.Unmarshal(rules, &cookieConsentBlockRequests) if err != nil { panic(err) } sort.SliceStable(cookieConsentBlockRequests, func(i, j int) bool { return cookieConsentBlockRequests[i].Priority > cookieConsentBlockRequests[j].Priority }) } // ShouldBlockRequest determines if a request should be blocked based on cookie consent rules func ShouldBlockRequest(url string, resourceType proto.NetworkResourceType, initiatorDomain string) bool { resourceTypeStr := getResourceType(resourceType) for _, rule := range cookieConsentBlockRequests { if matchesRule(rule, url, resourceTypeStr, initiatorDomain) { return true } } return false } // matchesRule checks if a request matches a specific cookie consent block rule func matchesRule(rule CookieConsentBlockRequest, url string, resourceType string, initiatorDomain string) bool { if !strings.Contains(url, rule.Condition.URLFilter) { return false } if len(rule.Condition.ResourceTypes) > 0 { matched := false for _, rt := range rule.Condition.ResourceTypes { if rt == resourceType { matched = true break } } if !matched { return false } } if len(rule.Condition.InitiatorDomains) > 0 { matched := false for _, domain := range rule.Condition.InitiatorDomains { if strings.Contains(initiatorDomain, domain) { matched = true break } } if !matched { return false } } if len(rule.Condition.ExcludedInitiatorDomains) > 0 { for _, domain := range rule.Condition.ExcludedInitiatorDomains { if strings.Contains(initiatorDomain, domain) { return false } } } return true } func getResourceType(resourceType proto.NetworkResourceType) string { switch resourceType { case proto.NetworkResourceTypeStylesheet: return "stylesheet" case proto.NetworkResourceTypeScript: return "script" case proto.NetworkResourceTypeImage: return "image" case proto.NetworkResourceTypeFont: return "font" case proto.NetworkResourceTypeXHR: return "xmlhttprequest" case proto.NetworkResourceTypePing: return "ping" case proto.NetworkResourceTypeCSPViolationReport: return "csp_report" case proto.NetworkResourceTypeMedia: return "media" case proto.NetworkResourceTypeWebSocket: return "websocket" case proto.NetworkResourceTypeOther: return "other" } return string(resourceType) } ================================================ FILE: pkg/engine/headless/browser/cookie/cookie_test.go ================================================ package cookie import ( "testing" "github.com/go-rod/rod/lib/proto" ) func TestShouldBlockRequest(t *testing.T) { tests := []struct { name string url string resourceType proto.NetworkResourceType initiatorDomain string want bool }{ { name: "should block trustarc consent notice", url: "https://consent.trustarc.com/notice?domain=hackerone.com", resourceType: proto.NetworkResourceTypeScript, initiatorDomain: "hackerone.com", want: true, }, { name: "should not block trustarc for excluded domain", url: "https://consent.trustarc.com/notice", resourceType: proto.NetworkResourceTypeScript, initiatorDomain: "forbes.com", want: false, }, { name: "should not block non-matching URL", url: "https://example.com/other-path", resourceType: proto.NetworkResourceTypeScript, initiatorDomain: "hackerone.com", want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ShouldBlockRequest(tt.url, tt.resourceType, tt.initiatorDomain) if got != tt.want { t.Errorf("ShouldBlockRequest() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: pkg/engine/headless/browser/cookie/rules.json ================================================ [ { "id": 1, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/iubenda_cs", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "radiomontecarlo.net", "video.repubblica.it", "video.lastampa.it", "nablawave.com", "buondi.it", "tgcom24.mediaset.it", "mediasetinfinity.mediaset.it", "mediaset.it", "telerama.fr", "skuola.net" ] } }, { "id": 2, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccm19_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["ccm19.de"] } }, { "id": 3, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "static.clickskeks.at", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 4, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/amp-user-notification-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 5, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sn.sanoma.fi/js/sccm", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 6, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/api/rest/settings/public?fields=endUserAgreement", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 7, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "congstar-media.de/fileadmin/cpolicy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 8, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "connect.danone.es", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 9, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/components/es6/PrivacyPolicy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 10, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "codice.shinystat.it/cgi-bin/getcod.cgi", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 11, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "whitepress.pl/common/pltk/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 12, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "js.hs-analytics.net/analytics", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 13, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jsdelivr.net/wp/wp-slimstat", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 14, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/dsvgo.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 15, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Flurrybox_EnhancedPrivacy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 16, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "static.axept.io/sdk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 17, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tarteaucitron.css", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 18, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "notice.sp-prod.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 19, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "htmedia.in/analytics-js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 20, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "campact.containers.piwik.pro", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 21, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "containers.piwik.pro", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 22, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wpcc.io/lib", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 23, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tag.goadopt.io", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 24, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/supi/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 25, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "omnigrafitalia.it/policy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 26, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/edgecastcdn.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 27, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/koriolis_gtm/theshield", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 28, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "fourmizz.fr/rgpd", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 29, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "hu-manity.co/hu-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 30, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ConsentManager,Sticky", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 31, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/rgpd/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 32, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/module-rgpd/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 33, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rcs_cpmt/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["video.corriere.it", "video.gazzetta.it"] } }, { "id": 34, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bloc/django/ckcsfrg", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 35, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ccm19.vucx.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 36, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/avia-bootstrap/js/klaro/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 37, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "exm-medien.de/cman", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 38, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "e2d-cms.de/cman", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 39, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/usercentrics-sdk/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 40, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cc.dalten.cz/ccJs", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 41, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccm19os/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 42, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "transcend.io/cm/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 43, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/avisopcdidomi", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 44, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccm19/public/app", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 45, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "webcache.datareporter.eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 46, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "webcache-eu.datareporter.eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 47, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.realpeoplemedia.co.uk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 48, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets-usercentrics/uc-version", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 49, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Sticky2,ConsentManager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 50, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cnilCookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 51, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "basketballbelieve.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 52, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "app.usercentrics.eu/browser-ui/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["kicker.de", "kicker.ch"] } }, { "id": 53, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cc.anytrack.de/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 54, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag/ctm/business-insurance/prod/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 55, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/GDPRPanelComponent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 56, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "aida.de/assets/coobann/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 57, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ModalEngage,ConsentManager,Sticky", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 58, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/coolkies-walkies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 59, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tagcommander/tc_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 60, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/media/plg_system_cookieconfirm", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 61, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie_meldung.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 62, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiewarning.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 63, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiewarning4.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 64, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "catalogocoop.it/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 65, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/mu-plugins/cookie_notifier", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 66, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "freegamehosting.eu/js/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 67, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jp2w.pl/a/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 68, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "https://cdn.serviceform.com/serviceform-tools/privacy/sf-privacy-partner.js?v=nethit", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["https://www.lumise.se/"] } }, { "id": 69, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "talktalk.co.uk", "blackboard.com", "kayak.pl", "gamersgate.com" ] } }, { "id": 70, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/inc/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 71, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "metpartner.pl/cookie/info_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 72, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "catalogo.pusc.it/pusc_jquery.cookie.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 73, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie_law.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 74, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "lebendiges-aachen.de/includes/javascript/cookiechoices/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 75, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.reedbusiness.nl/script/cookiechecker/cookiejs.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 76, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/javascript/garante-privacy/js-cookie-master", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 77, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cookie-compliance", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 78, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.webgenerator.nl/_NoCDN/Javascript/CookieBar/cookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 79, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiesamtykke.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 80, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/mod_pescookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 81, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesdirective", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 82, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/webapps/bb-cookie-disclosure", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 83, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-use-policy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 84, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.orangegames.com/assets/js/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 85, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-policy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["newpharma.be"] } }, { "id": 86, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ico_cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 87, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/comun/cookie_comercial.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 88, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "hogarutil.com/js/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 89, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookielaw.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 90, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bootstrap-cookie-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 91, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieDisclaimer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 92, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/public/scripts/cookie/popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 93, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gartneriraadgivningen.dk/skinCss/website/js/cookie/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 94, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "blackboard.com", "kayak.pl", "portal.easygreenenergy.at", "oekostrom.at", "gin-rummy-online.com", "gamersgate.com", "spielemax.de", "tonershop.at", "plus-gp-joule.de" ] } }, { "id": 95, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsentpopup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 96, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiePolicyEUPopin", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 97, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookielawscript.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 98, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiepolicy-client.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 99, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-disclaimer.lemm.de/cd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 100, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiebar-init.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 101, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie_consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["check24.de"] } }, { "id": 102, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiewarning2.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 103, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "phufart.pl/info_o_cookie_utf8.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 104, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieInfo.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 105, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "oleificiozucchi.it/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 106, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieAccept.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 107, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "videoland.com/external/cookiewall", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 108, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesDirective.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 109, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-law-plugin_en.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 110, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/libernazione-utils/js/libcookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 111, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "aegon.nl/data/cookiewall", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 112, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cultizm.com/templates/c2012light/javascript/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 113, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieprivacygenerator.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 114, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogcindario.com/cookie.php", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 115, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "promoracing.it/cookies/popup.cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 116, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "harazd.net/info_o_cookie_utf8.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 117, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie_acceptance_modal.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 118, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "eurail_responsive_law_cookie_banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 119, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dometic/dist/components/cookiescomponent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 120, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "basiszinssatz.info/cookiescript.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 121, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "leporc.com/sites/all/modules/custom/info_cookies/info_cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 122, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "static.origos.hu/s/js/custom/origo/accept-cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 123, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jquery-easy-eu-cookie-law.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 124, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "shop.szakalmetal.hu//js/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 125, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.nwmgroups.hu/s/js/custom/origo/accept-cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 126, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bootsticks.npage.de/assets/js/cookieconsent.latest.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 127, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.it/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 128, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.at/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 129, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.es/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 130, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.ee/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 131, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.pl/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 132, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.cz/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 133, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.dk/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 134, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.ie/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 135, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.fr/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 136, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.si/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 137, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.hu/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 138, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.sk/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 139, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.se/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 140, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.fi/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 141, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.lt/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 142, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.gr/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 143, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.ro/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 144, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.bg/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 145, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.be/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 146, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.hr/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 147, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.de/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 148, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.pt/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 149, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.nl/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 150, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.no/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 151, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.is/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 152, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.cl/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 153, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.lv/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 154, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.ch/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 155, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.ba/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 156, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.lk/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 157, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.ru/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 158, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.com/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 159, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.co.uk/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 160, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "blogspot.ca/js/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 161, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ext/zt_popupcookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 162, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "iti.si/deljene_datoteke/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 163, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/responsive-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 164, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "skm.warszawa.pl/js/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 165, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookie-info.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 166, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_law/plugin.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 167, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jslibrary/cookiewarning?", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 168, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cookie-notice", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["kapitalkontor.com", "barzahlen.de"] } }, { "id": 169, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "airnewzealand.co.nz/vbook/actions/cookieconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 170, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cg_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 171, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "grsuk.com/scripts/cookie-info.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 172, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-consent.es5.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 173, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookielaw_mip.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 174, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "informacja_cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 175, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiefix.dynamicline.hu/fixcookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 176, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 177, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-policy.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 178, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/eu-cookie-law-wp-cookie-law/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 179, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "eurocookie.galilcloud.wixapps.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 180, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-terms.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 181, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookieStatement.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 182, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wawona.hu/cookie-box/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 183, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/components/cookiescomponent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 184, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesinfo/cookieinfo.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 185, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent/cookie-consent.umd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 186, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebanner.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "huk24.de", "huk.de", "adtipp.de", "nectar.com", "neckermann.de", "exali.de", "raiplaysound.it", "check24.de" ] } }, { "id": 187, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/common/cpol/cookiepolicy.php", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 188, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-widget/latest/cookiewidget.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 189, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "static.talparadio.nl/js/cookiecheck.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 190, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bmedonline.es/js/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 191, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consentcookie.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 192, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie.consent.is", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 193, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-notification.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 194, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "filer_-_cookie_disclaimer_ny/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 195, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cpresources/cookieconsent/js/cookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 196, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "layer22.net/scripts/cookies-no-conflict-naxa", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 197, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiecuttr-eu-cookie-law-compliance/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 198, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jquery.eu-cookie-consent.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 199, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebar.jquery.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 200, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "trashmail.com/js/cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 201, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gratiz.nl/cookie-script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 202, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-service/js/client.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 203, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/euc_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 204, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/web/components/cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 205, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.cookiesdirective.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["servizionline.gruppoascopiave.it"] } }, { "id": 206, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-consent.azureedge.net/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 207, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jquery.cookie.policy.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 208, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/prettycookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 209, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/accept-ad-targeting/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 210, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "api.cookielaw.eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 211, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "api.cookielaw-script.it", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 212, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiealert.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["viessmann.com.pl"] } }, { "id": 213, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/external/ico.cookie.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 214, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/script/ico.cookie.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 215, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiewall.es5.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 216, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiecompliance.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 217, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/remote/v1/cookie-notification", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 218, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-warning.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 219, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookienotice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["betterworldbooks.com"] } }, { "id": 220, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "private.dmscookie.com/scripts/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 221, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tgory.sr.gov.pl/js/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 222, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eu-cookie-law.js?browserId", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 223, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.innershed.co.uk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 224, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/mu-plugins/cookie_notifier/cn.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 225, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebar_tls.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 226, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/smart-cookie-kit/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 227, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-notice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 228, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookiePolicy/resources/scripts/cookie.policy.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 229, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/cookieconsent/js/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 230, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jquery.cookiepol.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 231, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-bar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 232, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie/cc_min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 233, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookielaw/piskotkar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 234, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "myportalcms.com/template/js/mp_acookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 235, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieplugin/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 236, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-message.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 237, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 238, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "doitonlinemedia.nl/global-js/avg-cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["palazzogroep.nl"] } }, { "id": 239, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy.digimedia.com/check_cookie_country_code.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 240, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fortune_cookie_popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 241, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiecsnt2/js/cookie.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 242, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-note.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 243, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "policy.app.cookieinformation.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "kartor.eniro.se", "kart.gulesider.no", "kort.degulesider.dk", "map.krak.dk", "altibox.no", "babysam.dk", "elkjop.no", "minaftale.dk", "skousen.dk", "skousen.no", "whiteaway.com", "whiteaway.no", "whiteaway.se", "dvdoo.dk" ] } }, { "id": 244, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookienotice/Js/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 245, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/fortune-cookie-consent-policy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 246, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdl-cookiebar.tnt-digital.com/js/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 247, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/nl-cookie-law.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 248, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieaccept.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 249, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ercookiebar.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 250, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiewarn.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 251, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-hinweis/script-v2.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 252, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookieMessage.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 253, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets/js/sera/web/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 254, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiebanner.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 255, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/z7_cookiemanager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 256, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent/js/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["kawalekkodu.pl"] } }, { "id": 257, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieconsentnotice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 258, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/tnzcookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 259, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/gdpr-cookie-compliance", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 260, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/components/cookie-modal/cookie-modal.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 261, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-window.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 262, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiewall-inline-for-popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 263, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cc-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 264, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiePolicyV4.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 265, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/cookiechoices.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 266, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-widget/bootstrap.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 267, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.tipsy.cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 268, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie_choices.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 269, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/wp-cookie-allow/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["learndutch.org"] } }, { "id": 270, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cookiemedia/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 271, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Resources/Public/js/cookiebar.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 272, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plugins/system/cookiespolicynotificationbar", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 273, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieassistant.com/widget.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 274, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/helper-scripts/cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 275, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/msifestiwal/Scripts/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 276, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-overlay-pl.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 277, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.notify.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 278, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cpol/cookiepolicy.php", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 279, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-manager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 280, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "dtt.taleo.net/careersection/theme/381362/1547022019000/en/theme/js/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 281, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/cookies/cookiesLayer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 282, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookiePolicyGa/cookiepolicyga.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 283, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sdc/cookie_consent.html", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 284, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tmo_cookies/Resources/Public/Javascript/CookieSettings.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 285, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "deltaxmultimedia.com/cookielaw", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 286, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/web/components/cookieusage", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 287, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/eagerly-tools-cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 288, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/scripts/cookiebar", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 289, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/resources/CookieConsent/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 290, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "buro-3.nl/cookie/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 291, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ll_cookie_bar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 292, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "api.cookiemonster.is/embed", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 293, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/g361-cookies-consents", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 294, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Scripts/Cookies/cookieacceptance.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 295, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/extimages/scripts/ukcookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 296, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.aptelink.pl/nc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 297, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "eu_cookie_banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 298, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eu_cookie_compliance.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 299, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dc-cookie-privacy-settings.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 300, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesPolicy/static/lib.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 301, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiewarning-nosql.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 302, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.bpcookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 303, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/resources/js/cookie/cookie-bar", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 304, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieConsentDialog.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 305, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/bc-cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 306, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/idxcookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 307, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/javascript/cookieser.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 308, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesWidget/widget.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 309, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.cookiebot.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "storyhouseegmont.dk", "storyhouseegmont.no", "storyhouseegmont.se", "egmont.com", "stadtmobil.de", "wwf.fi", "login.nos.pt", "asambeauty.com", "cineplex.de", "berlingske.dk", "digimon.kochfilms.de", "kino.dk", "hoyavision.com", "linak.es", "linak.de", "cajamar.es", "digitaltrends.com", "mein-grundeinkommen.de", "werder.de", "finanzmarktwelt.de", "danbolig.dk", "bt.dk", "scubadiving.com", "biomarkt.de", "harzwasserwerke.de", "stern.de", "dasinvestment.com", "derivate.bnpparibas.com", "werkenbijlidl.nl", "swspremberg.de", "nngroup.com", "bankia.es", "bergbauernmilch.de", "spiele-kostenlos-online.de", "ekstrabladet.dk", "epochtimes.de" ] } }, { "id": 310, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccp-sites/components/structure/cookie-notification/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 311, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-info.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 312, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.algo.at", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 313, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiehub.net/c", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 314, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/widgets/eu-cookie-law/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 315, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "app.cookieyes.com/client_data", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 316, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/skinCss/website/js/cookie/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 317, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "freeprivacypolicy.com/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 318, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-module/dist", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 319, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieOptIn.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 320, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookie_banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 321, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery-cookies-alert.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 322, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookielaw.emea.fcagroup.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 323, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/tnk-cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 324, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiepopup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 325, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.cookiefirst.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "swietawdomu.pl", "deutschesapothekenportal.de" ] } }, { "id": 326, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-policy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 327, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/liquorice-cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 328, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-switch.viminds.de/api", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 329, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "skyscnr.com/sttc/oc-registry/components/cookie-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 330, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/cookiesplus/views/js/cookiesplus.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 331, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mws-cookie-solution.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 332, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ajax/libs/cookieconsent2", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "membersuite.com", "la-becanerie.com", "rijbewijskeuringennederland.nl", "download.pixelexperience.org", "ceramtec-group.com" ] } }, { "id": 333, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "talktalk.co.uk", "codexis.cz", "lacoste.com", "huuray.se", "indiearenabooth.com" ] } }, { "id": 334, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookienotice-bootstrap.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 335, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cookieEuGH", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 336, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 337, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/cookie-notice", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 338, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiechoices.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 339, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 340, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieconsent.popupsmart.com/src/js/popper.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 341, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "flixbus.com/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 342, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/jscookieconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 343, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.cookie-policy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 344, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-alert.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 345, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "up-cookiemon.wavecdn.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 346, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieBanner.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "adtipp.de", "nectar.com", "neckermann.de", "exali.de", "raiplaysound.it", "check24.de" ] } }, { "id": 347, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/politica-cookies/leyCookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 348, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/trwcookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 349, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dc_components/site/cookie-control/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 350, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-manager.de/cookie-manager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 351, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pxpcookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 352, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie/uccb-main", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 353, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_consent_min_js.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 354, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/onlinemanufaktur-cookie-notice/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 355, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mq_cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 356, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/static/cookies/cookie-layer-wc-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 357, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-bar/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["vectra.pl", "urbanista.de"] } }, { "id": 358, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebar/scripts.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 359, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fileadmin/cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 360, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieopt-min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 361, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/micado.web.dsgvo.cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 362, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wip.pl/js/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 363, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/docroot/js/app/modules/cookies-selection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 364, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/cookieman/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 365, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiecode.dist.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 366, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_agreement_dialogue.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 367, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "googleapis.com/55_cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 368, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/nsd-cookie-banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 369, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/responsive-eu-cookie-notice", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 370, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/master-popups-cookieplus", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 371, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-banner-one.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 372, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiemanagement.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 373, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/mod_eu_cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 374, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.cookiefy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 375, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "websitepolicies.com/lib/cookieconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 376, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/aip_cookie_law", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 377, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie.gg/c", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 378, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plg_system_vpcookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 379, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/p8-cookie-bar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 380, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-banner/cb.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 381, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/echonetcookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 382, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-policy-en.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 383, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bb_cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 384, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/?tx_z7cookiemanager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 385, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/kmacookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 386, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/_hw_cookie_dialog.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 387, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-settings-ui/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 388, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookietrust.eu/script", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 389, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bs-cookie.staging.springbok.agency", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 390, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-banner.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["nice.org.uk"] } }, { "id": 391, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/WebUI/Cookies/allowcookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 392, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/SharedComponents/bundle-scripts/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 393, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent.klaro.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 394, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookies_bar", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 395, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modal/cookie-wall/?modal_view=true", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 396, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/better_cookie_consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 397, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/jquery-eu-cookie-law-popup", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 398, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "flipdish-cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 399, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookie-law-ansi", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 400, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieconsent.syreta.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 401, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/deferred/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 402, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/rodo-cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 403, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/hw-cookie-dialog.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 404, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/accept-cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 405, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_warning.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 406, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plugins/cookies/tinybox.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 407, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/web/components/cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 408, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-bar.salessquad.co.uk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 409, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieMunchr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 410, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "nicmanager.com/static/cookie_guideline", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 411, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "traderfox.de/lib/tfcookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 412, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/amadeus-plugin-cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 413, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rh-cookieconsent-microsites.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 414, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/components/content/cookie-overlay/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 415, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiemon.atcom.gr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 416, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "nbcsports.com/cookie-ack.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 417, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/gdpr/gdpr-cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 418, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.ihavecookies.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["wisla-plock.pl"] } }, { "id": 419, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/b2b-market/src/addons/cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 420, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/vue/components/cookie-monster", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 421, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/do-you-want-cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 422, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bascom.nl/cookies/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 423, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fluvius-eu-cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 424, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/lyxor-cookies-disclaimer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 425, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/grt-cookie-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 426, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bundles/cookiebar", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 427, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/avia-snippet-cookieconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 428, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/user-cookie-banner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 429, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/we_cookie_consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 430, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/vp-cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 431, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/system/modules/cookiecontrol/assets/js/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 432, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieConsentNotif.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 433, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/ne_cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 434, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-bar.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 435, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ilovecookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 436, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/luxcookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 437, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiehint/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 438, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieOptIn.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 439, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sg_cookie_optin/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 440, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebox.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["footroll.pl"] } }, { "id": 441, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.privacy.protection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 442, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/new-cookie-policy.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 443, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiehint.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 444, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/public-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 445, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mod_jt_cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 446, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/dist/cookieAcceptance", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 447, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebanner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 448, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/acc.cookienotification.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 449, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiepolicy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 450, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie_api/cccframe", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 451, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookienote-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 452, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/metro?sc_device=webcomponent&components=cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 453, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_msg.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 454, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_info.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 455, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "js.cookietagmanager.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 456, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets/mnd-cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 457, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js_cookies_legal.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 458, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ys_cookie_consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 459, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-v2/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 460, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/beautiful-and-responsive-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 461, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie/banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 462, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/widget-module-cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 463, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/lightweight-cookie-notice/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 464, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ocean-cookie-notice/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 465, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jst_eu_cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 466, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "procookie.by.nf", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 467, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 468, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jquery-eu-cookie-law-popup", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 469, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cp_cookieconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 470, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.ae-webdesign.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 471, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ws5_eucookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 472, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieConsent/cookieModal.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 473, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/consent-cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 474, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.fo/qookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 475, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/hw-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 476, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["labs.strava.com", "crossfit.com"] } }, { "id": 477, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/w4.cookiebar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 478, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cookielay/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 479, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "plugin-cookie-consent/build", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 480, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie/onetrust/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 481, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-settings-modal/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 482, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/components/consent-cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 483, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-service.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 484, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent.bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 485, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/lgpd-cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 486, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cookies-and-content-security-policy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 487, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiebox_uc_build.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 488, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gen/cookie-notification", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 489, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesjsr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["lcp.fr"] } }, { "id": 490, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/eu-cookies-bar/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 491, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieOverlay-view.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 492, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/Widgets/cookieWidget", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["losteria.de"] } }, { "id": 493, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/minified/cookiecontrol.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 494, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/ab1d_cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 495, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/module/kabimba_cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 496, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/woody-addon-cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 497, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/__enzuzo-cookiebar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 498, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_policy/ack.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 499, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["northernpowergrid.com"] } }, { "id": 500, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/zedwcookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 501, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiedisclosure/core.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 502, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie.thynk.media/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 503, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/usercentrics/cookiebox", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 504, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-tool/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 505, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-notification.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 506, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "onetrust.com/cookieconsentpub", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["fujifilm-x.com"] } }, { "id": 507, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/dp_cookieconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 508, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 509, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/om_cookie_main.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 510, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.rgpd-cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 511, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bandeau_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 512, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dist/js/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 513, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "sonnenverlauf.de", "mondverlauf.de", "leanlibrary.com", "retrogames.cc", "exagon.de", "sozialversicherung-kompetent.de", "varcevanje-energije.si", "khl.ru", "bauen-und-heimwerken.de", "carport-diagnose.de", "tricount.com" ] } }, { "id": 514, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieConsentApp.js.gz", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 515, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fileadmin/templates/cookie/fc_thin.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 516, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.cookiecode.nl", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 517, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/nd-cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 518, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent-all.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 519, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieBanner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 520, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-wall.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 521, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.giant.cz/assets/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 522, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.praguebest.cz/dist", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 523, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-prompt/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["gestiontv.vodafone.es"] } }, { "id": 524, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bootstrap-cookie-consent-settings.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 525, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rails_cookie_consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 526, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rgpd/js/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 527, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieBannerLoader.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 528, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieform.pl/assets/js/plugin", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 529, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets/components/cookiemanager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 530, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ewcookienoteext.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 531, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/clientlibs/components/content/cookie/cookielayer", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 532, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rm-cookieconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 533, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-banner-vue", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 534, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiecdn.com/cwc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 535, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/om-gdpr-cookie-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 536, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn-cookieyes.com/client_data", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 537, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "digitalsternemarketing.de/dscookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 538, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modal-acceptare-cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 539, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-bar/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 540, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookieconsent.blob.core.windows.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 541, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/all-cookieconsent-js.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 542, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-browser-alert-ui.fragment.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 543, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tcf/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 544, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bsgdprcookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 545, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": ".cookiehub.eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 546, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "traveljuice-engines.prod.traveljuice.fr/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 547, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eu_cookie_compliance_override.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 548, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/smart-eu-cookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 549, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 550, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Pictorium/scripts/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 551, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jsLib/gdpr/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 552, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieBar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 553, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cc.controlcookies.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 554, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieNotice.build.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 555, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ux.ui-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 556, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesPopUp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 557, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/in2cookiemodal", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 558, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-banner/cookie-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 559, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/polityka-prywatnosci/js/cookie-overlay", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 560, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-portlet/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 561, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/simple-gdpr-cookie-compliance/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 562, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-consents.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["posta.sk"] } }, { "id": 563, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "theme-cookie/app/cookie.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 564, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "plugins/ShprCookieFavour/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 565, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.ptj.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 566, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieModal/clientlib", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 567, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-banner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 568, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dxcookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 569, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/real-cookie-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 570, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sohoshopcookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 571, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "am-static.com/cookie-banner/sdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 572, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/marked-cookie-consent-web/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 573, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.cookielaw.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["cnn.com"] } }, { "id": 574, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/borlabs-cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 575, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/gdpr-cookie-compliance/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 576, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/etagen_cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 577, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_overlay.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 578, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plugins/cookies-and-content-security-policy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 579, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieBar.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 580, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiedialog/cookieutility.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 581, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/creare-eu-cookie-law-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 582, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jsdelivr.net/npm/@finsweet/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 583, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieseubox.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 584, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie/cc_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 585, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieConsent.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["fransdewitte.nl"] } }, { "id": 586, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/category-assets/experiences/recurring/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 587, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/storage/ui/cookies-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 588, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiedisturber/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 589, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/lightweight-cookie-notice", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 590, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/dm_cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 591, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-dialogue.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 592, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dist/scripts/cookie-control", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 593, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "am-static.com/cookie-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 594, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/marked-cookie-consent-web/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 595, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "app.falconcookie.de/storage", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 596, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/brd_cookies_consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 597, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/hicookielaw/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 598, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/kbv-cookieconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 599, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-mycity.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 600, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/components/src/cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 601, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/webtoffee-gdpr-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["altheaprovence.com"] } }, { "id": 602, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.neuca24.pl/nc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 603, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies/manage-cookies-runtime", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 604, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/ppw_cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 605, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-widget/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 606, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/pages/js/cookie-dialog.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 607, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3temp/assets/compressed/cookieman", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 608, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiethough.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 609, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "beyond-cookiebanner.de/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 610, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets/js/cookie_consent_manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 611, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/viva-cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 612, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieMessage.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 613, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/michnhokn/cookie-banner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 614, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ddjnocookie.com/prod_public", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 615, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/cookie_policy/frontend/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 616, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 617, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpsystem.eu/pl/cookiesprivacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 618, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fcc-cookie-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 619, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-cookie-consent.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 620, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieConsentScript", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 621, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookienoticepro.script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 622, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dsgvo/js/cookiemonster", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 623, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_consent_js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 624, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent-request.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 625, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wko.at/gcm/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 626, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wko.at/css-js/scripts/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 627, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/frontend-core/js/cookieBox.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 628, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiemanager.dk/js/cm.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 629, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mod_pixim_cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 630, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tmgonlinemedia.nl/consent/script/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 631, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy.ariadneathome.nl/script/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 632, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy.vtwonen.nl/script/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 633, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wpjslib-chunk-consent-form.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 634, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/consentbar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 635, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdprpro/views/js/gdpr-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 636, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/consent-banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 637, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "s.clickiocdn.com/t/consent_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 638, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jppol-consent/js/bootstrap.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 639, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/opbox-rodo-consent-modal/index", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 640, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "optanon.blob.core.windows.net/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 641, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "libertatea.ro/consent/config.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 642, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent-notice.magix.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 643, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/taunton-user-consent-eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 644, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consensu.org/t/consent_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["softzone.es"] } }, { "id": 645, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consentmanager.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "sourceforge.net", "webfail.com", "sudoku-aktuell.de" ] } }, { "id": 646, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consently.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 647, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "s.adroll.com/j/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 648, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-panel-vue.chunk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 649, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.theneweuropean.co.uk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 650, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy-consent-banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 651, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "thezen.garden/projects/zenconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 652, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "smartconsent.ro/js/smart-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 653, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/webflow-consent-manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 654, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "app.consentassist.com/widget.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 655, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "s.luxupcdnc.com/t/consent_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 656, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.appconsent.io", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["lefigaro.fr"] } }, { "id": 657, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.23g.io", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 658, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "browser-consent-front.coco.s-cloud.fi", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 659, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets/consent-manager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 660, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.weersvoorspelling.nl/v1", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 661, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.webmasterplan.com/v2", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 662, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/adsconsent.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 663, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.truste.com/get?name=notice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 664, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.trustarc.com/notice", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "tripit.com", "concursolutions.com", "fortune.com", "formula1.com", "forbes.com" ] } }, { "id": 665, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent-manager.metomic.io", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 666, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.uniconsent.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["photopea.com"] } }, { "id": 667, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-banner-bootstrap", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 668, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tmgrup.com.tr/tmd-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 669, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/consent-modal.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 670, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/api/snippets/js/consent-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 671, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sixfifty.com/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 672, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 673, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "choices.consentframework.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 674, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cache.consentframework.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 675, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consentserve.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 676, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "analytics-consent-manager.azureedge.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 677, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mgm_consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 678, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "hoffmann-group.com/common/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 679, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dv_t3_consent_management/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 680, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gsso_consent_manager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 681, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "avandor.com/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 682, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/data_consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 683, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "smart.idmnet.pl/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 684, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "kalevakonserni.fi/consent/gravito", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 685, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bundle-consent-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 686, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modal-consent-component.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 687, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-management/_include/init_consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 688, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "pitcom-webanalyse.de/dsgvo/consent-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 689, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wrd-aws.com/consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 690, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/ag-consentmanager-no-bootstrap/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 691, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/static/consent-dialog.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 692, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/opbox-gdpr-consents/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 693, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/consent-manager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 694, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ampproject.org/v0/amp-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["smartdroid.de", "lelum.pl"] } }, { "id": 695, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.trustarc.com/v2/notice", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 696, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/resources/onetrust/js/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 697, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-manager/lazy-consent-manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 698, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/addons/consent_manager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 699, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/npm/ez-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 700, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/piwik-consent-banner-script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 701, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fileadmin/template/js/cconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 702, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/kconsent/kconsent29.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 703, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-layer/js/consent-layer-loader.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 704, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tm-gdpr-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 705, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "snigelweb.com/adconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 706, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sncmp.com/adconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 707, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consentmanager.net/delivery/js/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["winfuture.de", "infranken.de"] } }, { "id": 708, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent-manager.confirmic.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 709, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consentbanner.de/public/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 710, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "classistatic.de/consent-statics", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 711, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consentcheck.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 712, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-consent-management-platform/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 713, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/drdsgvo-consent-script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 714, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-consent.bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 715, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent/civic-bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 716, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "totalweb.gr/gdpr/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 717, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/epaas.consentdrawer.bundle", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 718, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consentbanner-fragment/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 719, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "altravia.com/connector/consent_mode.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 720, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consentmanager.net/delivery/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["winfuture.de", "infranken.de"] } }, { "id": 721, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/taunton-user-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 722, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fileadmin/templates/js/cconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 723, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/okf-euconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 724, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.daa.net/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 725, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-consent-banner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 726, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-management-app/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 727, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.hr/delivery/js/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 728, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bundles/consent-init?v=", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 729, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/acc.consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 730, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.scm-verlagsgruppe.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 731, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/consent-magic-pro", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 732, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "7gra.us/consentbar", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 733, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dgp-cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 734, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets/as24-cmp/consent-banner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 735, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/log-user-consents/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 736, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent-bist.de/public/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 737, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.digiapi.com/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 738, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "idealo.com/storage/cmp/consent-management.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 739, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.prointernet.com/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 740, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/coockieconsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 741, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consentmanager.net/delivery", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "winfuture.de", "echo-online.de", "stern.de", "spar.hu", "spar.hr", "spar.at", "spar.si", "interspar.at" ] } }, { "id": 742, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.comply-app.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 743, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "kesko.fi/kconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 744, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "unpkg.com/@segment/consent-manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 745, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wko.at/static/ct/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 746, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consenttool.haendlerbund.de/app.js?apiKey", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 747, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.extrazimut.net/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 748, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cs_consent_modal.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 749, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/gdpr_footer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 750, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/gdpr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 751, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/comun/avisopcgdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 752, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 753, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpr.internetbrands.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 754, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": ".be/api/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 755, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": ".nl/api/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 756, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "-gdpr-min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 757, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-transparency-apnxs/latest/gdpr-bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 758, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sitemaps.services.zam.com/gdpr_optout.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 759, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ibeugdpr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 760, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consensu.org/gdpr/cmp/gdpr-cmp-ui.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 761, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "s3.amazonaws.com/sitemaps.services.zam.com/gdpr_standalone.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 762, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sizzlegdpr.snippet.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 763, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/gdpr/messaging.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 764, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mdp.javascript.gdpr.min", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 765, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/emg-framework/public/js/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 766, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/emg-framework/assets/js/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 767, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/assets/js/gdpr.js?ver", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 768, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/injectable.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 769, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ishop-plugins/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 770, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "secure-cdn.mplxtms.com/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 771, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 772, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/am2-gdpr-public.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 773, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdprscript.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 774, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plugins/surbma-gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 775, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-banner.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 776, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "libraries.wmgartistservices.com/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 777, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdprmain/prod/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 778, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.thisisdone.com/gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 779, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpr-banner.awsmpsa.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 780, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "securebrainpull.com/gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 781, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpr500.com/widget/pandawidget/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 782, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bundle-gdpr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 783, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/CookieConsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["kayak.pl"] } }, { "id": 784, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/2018-gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 785, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-cmp-ui.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 786, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/gdpr.min.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 787, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plugins/gdpr/gdpr_ncoi.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 788, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/static/new/js/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 789, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/libs/gdpr/cmp/cmp.bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 790, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pgdpr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 791, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/policies/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 792, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/spd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 793, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "prizelogic.com/gdpr/third-party-optin.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 794, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 795, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wordpress-ptchrgdprplugin/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 796, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpr-wrapper.privacymanager.io", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "kinuskikissa.fi", "geenstijl.nl", "dailybuzz.nl", "rtvnoord.nl", "omroepbrabant.nl", "futurezone.at", "profil.at", "kurier.at", "weeronline.nl", "lablue.de", "vesti.bg" ] } }, { "id": 797, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "evidon.com/pub/gdprnotice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 798, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/ct-ultimate-gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 799, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpr.mandarin-medien.de/manager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 800, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpr-tcfv2.sp-prod.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "computerbild.de", "programme-tv.net", "bold.dk", "welt.de", "sport1.de", "ostsee-zeitung.de", "sky.com", "lvz.de", "mein-schoener-garten.de", "autobild.de", "bike-bild.de", "bz-berlin.de", "travelbook.de", "si.com", "capital.fr", "t3n.de", "is.fi" ] } }, { "id": 801, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "yastatic.net/s3/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 802, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/zgdpr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 803, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/pp_agreement.pc.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 804, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/cmp/cmpBundle.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 805, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/webmd.gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 806, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/motus-gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 807, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plugins/gdprprivacysetup/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 808, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc-gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 809, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "metronom.com/library/scripts/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 810, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/lin_gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 811, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tronic-gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 812, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-banner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 813, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/typo3conf/ext/dbb_gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 814, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-appliance.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 815, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "pdcc.gdpr.es", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 816, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-redirect.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 817, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/shared/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 818, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sdk-gdpr.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 819, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/gdpr-component", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 820, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdprDialog.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 821, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bluelabs-gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 822, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdprScriptComponent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 823, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "mediavine.com/tags/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 824, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "pubnation.com/tags/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 825, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ecos.am/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 826, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modal-gdpr.umd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 827, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr_notice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 828, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/lexon-gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 829, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/popin-gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 830, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modules/gdprpro/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 831, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gdpr-api.sharethis.com/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 832, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/media/plg_system_eprivacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["reshade.me"] } }, { "id": 833, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "recepttar.hu/js/privacy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 834, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.dmgmediaprivacy.co.uk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["metro.co.uk", "gbnews.uk"] } }, { "id": 835, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jssdk.privacy.pre.schibsted.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 836, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "secureprivacy.ai/secureprivacy-plugin/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 837, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/vendor/weka/privacykit/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 838, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy.clym.io/js/clym-widget.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 839, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/privacypolicy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 840, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bosch-privacy-settings-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 841, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/general/avada-privacy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 842, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "public.message-business.com/Javascript/mb.privacyManager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 843, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.reeltime.no/pm_assets/privacy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 844, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ftcguardian.com/privacy-update", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 845, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacypolicy.trgr.be/widget", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 846, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacyopt.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 847, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacypolicy/styles/all/template/remove_url.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 848, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "psi.schibsted.com/api/v2/privacy/notification", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 849, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/privacy-policy-info/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 850, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/e-privacy.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 851, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacyConsentBar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 852, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tagcommander.com/privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["labanquepostale.fr"] } }, { "id": 853, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "isgprivacy.cbsi.com/dist/optanon", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "cbsnews.com", "startrek.com", "insideedition.com", "cbslocal.com", "etonline.com" ] } }, { "id": 854, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/contao-privacy-center.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 855, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-policy.u-lab.nl", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 856, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "trustcommander.net/privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["tf1info.fr", "tf1.fr"] } }, { "id": 857, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "app.eprivacy-keeper.eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 858, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/en-privacy-notification/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 859, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy.claytonhomes.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 860, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "shopify.com/shopifycloud/privacy-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 861, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/enua-privacy-policy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 862, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "demotywatory.pl/res/js/privacy_policy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 863, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eprivacy/js/eprivacy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 864, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sf-tagomo-privacy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 865, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/s4s-privacy-module/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 866, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/shopsshort/privacy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 867, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "heg-cp.com/upm/privacy-manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 868, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tag=ui/privacy/CookiesConsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 869, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dock-privacy-settings.esm.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 870, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy.wum.rocks/public/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 871, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tagcommander/privacy_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 872, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/serviceform-tools/privacy/sf-privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 873, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy-dialog-tracking.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 874, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy/providers/CookiesDataProvider", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 875, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/contao-privacy-center", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 876, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/myagileprivacy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 877, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.lemonde.fr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 878, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.quantcast.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 879, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.nextday.media/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["vi.nl", "omroepwest.nl"] } }, { "id": 880, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.dreamlab.pl", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 881, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/media/cmp/int_cmp_banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 882, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consensu.org/delivery/cmp.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 883, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sipaof.mgr.consensu.org/sipacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 884, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/sourcepoint/sp-msg.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 885, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.cdntrf.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 886, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.mediavine.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 887, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmpCookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 888, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/library/cmp/cmp.bundle-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 889, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp-loader.choice.faktor.io", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 890, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/diyscmp.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 891, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bilsyndication.com/js/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 892, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bilsyndication.com/plugins/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 893, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.osano.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 894, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "vlitag.com/plugins/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 895, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.md-nx.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 896, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.opencmp.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["foraum.de", "idowa.de"] } }, { "id": 897, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.faktor.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["omroepwest.nl", "consent.talpanetwork.com"] } }, { "id": 898, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gravito.net/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 899, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdncmp.richaudience.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 900, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "heymatic.com/assets/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 901, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/js/vendors~cmpUi", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 902, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ustatik.com/public/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 903, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "mrf.io/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 904, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.cls.pm", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 905, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gravito.net/lightcmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 906, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "onetag-sys.com/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 907, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/truendo_cmp.pid.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 908, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.optad360.io", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 909, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "snigelweb.com/sncmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 910, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "seznam.cz/js/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 911, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pubtech-cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 912, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.pafo.fairbung.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 913, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sibbo.net/v2/sibbo-cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 914, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "24net.cz/resources/js/cmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 915, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/quorn-cmp.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 916, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmps.o2.cz/delivery", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 917, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tcf-cmp.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 918, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "fastcmp.com/fast-cmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 919, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "apps.ludostation.com/cmp/v2/cmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 920, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "mrdev-cmp/assets/js/script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 921, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.setupcmp.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 922, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.meteored.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 923, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "veodys.fr/api/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 924, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sibbo-cmp-core.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["atresplayer.com"] } }, { "id": 925, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gravito.net/alehdetcmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 926, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "fastcmp.com/fast-cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 927, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sibbo-cmp-loader.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 928, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.springernature.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 929, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "determinator.service-cmp.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 930, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "seznam.cz/js/cmp2/scmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 931, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "seznam.cz/js/cmp2/scmp-external.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 932, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gofundme.com/js/3.0/visitorCookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 933, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookiesDirective", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 934, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "CookieAccept/affirmation.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 935, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ariba.com/assets/scripts/classes/Ariba.Compliance.CookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 936, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "aldermore.co.uk/Scripts/Logic/CookieDisclaimer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 937, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tweeboo.com/r/js/CookieDirective", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 938, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jqueryCookieGuard", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 939, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jquery.smartCookie.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 940, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc-bar/cCookiesH.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 941, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieDirective.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 942, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/setPrivacyCookie.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 943, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/clientlib-webpack-publish/js/CookiesApp-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 944, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/CookieManager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 945, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bundle_CookieLegalNotice.prod.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 946, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/acceptCookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 947, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/i_CookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 948, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Amasty_GdprCookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "planet-cards.co.uk", "sunkost.no", "wondrium.com", "nova-motors.de", "moleonline.com", "durstexpress.de", "littlelunch.com", "twinpack.nl", "eckeroline.fi", "eilles.de", "xt500parts.com", "cupper-teas.de" ] } }, { "id": 949, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ts/components/CookieConsent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 950, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/SDG_CookieLayer.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 951, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/slr_js/allowCookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 952, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/javascript/component-CookieConsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 953, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/whCookieManager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 954, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Cookie-GetCookieModal", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 955, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/1990KB-CookieConsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 956, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Magento_Cookie/js/notices", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 957, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/iwCookieBanner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 958, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "blackboard.com", "kayak.pl", "gamersgate.com" ] } }, { "id": 959, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/CookieConsentNew", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 960, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieManagerUi.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 961, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/widgets/global/vendors~LegalCookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 962, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/getCookieConsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 963, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/kssCookieManager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 964, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ModalCookiesPrivacy.php", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 965, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookiesManager/CookiesManager.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 966, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/components/CookieManager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 967, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/SiteElements/Scripts/CookieBanner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 968, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/plugins/CookiePop/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 969, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/IntegerNet_CookieConsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 970, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/new_Cookiebanner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 971, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/feoCookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 972, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Plumrocket_CookieConsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["8020.net"] } }, { "id": 973, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/static/CookieManager/js/app", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 974, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/optInCookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 975, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ja/controlCookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 976, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utils/CookiePrompter", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 977, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Wamoco_CookieConsentUi/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 978, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/standaloneModalCookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 979, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Detco_CookieBanner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 980, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/vfConsentCookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 981, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Cookie-Interfrog/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 982, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "quantcast.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "sourceforge.net", "tumblr.com", "wpolityce.pl", "player.livespotting.com", "indy100.com", "vi.nl", "independent.co.uk", "express.co.uk", "joe.ie", "joe.co.uk", "standard.co.uk", "avsforum.com", "pcgamer.com", "nfl.com", "filmvandaag.nl", "gamesradar.com", "iol.pt" ] } }, { "id": 983, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "gemius.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 984, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sddan.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 985, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "digitrust.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 986, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sharethis.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 987, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "webads.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 988, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "etarget.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 989, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "optad360.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["infogliwice.pl"] } }, { "id": 990, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "shinystat.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 991, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "dan.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 992, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ogury.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 993, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "inforpl.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 994, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "sibboventures.mgr.consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 995, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consensu.infor.pl", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 996, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ciasteczkowapolityka.pl/getscript", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 997, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bsl.nl/extern/smbv-incl/script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 998, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ssl.synovite-scripts.com/ut", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 999, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "zoneadsl.com/clientscript/cnil.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1000, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "angrybirdsmovie.net/site/scripts/nettracking4.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1001, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bobulous.org.uk/javascript-head-sitewide.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1002, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wroclaw.pl/portal/themes/js/script-rodo.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1003, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/scripts/pookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1004, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "metasrc.com/assets/javascripts/compliance.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1005, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "geo.fr/assets/scripts/sourcepoint.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1006, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/rodo/rodo_script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1007, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.praivacy.eu/scripts", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1008, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "script.getcomplied.com/scripts/complyWidget/assets/getCompliedListWidget.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1009, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Gpdr/assets/ccc-script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1010, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "kookiecheck.cz/static/script", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1011, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "legalblink.it/api/scripts/lb_cs.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1012, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dgsvo/script.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1013, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/cc/js/cc.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1014, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "common.i12.de/cms/file/plugin/dp/dp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1015, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/shapepress-dsgvo/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1016, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/iticonseil-rgpd/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1017, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/pixelmate/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1018, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/pixelmate-opt-in/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1019, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "static.trbo.com/plugin", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1020, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/fb-pixel-dsgvo/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1021, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "id-ward.com/static/idw_plugin", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1022, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ishop-plugins/ishop-cp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1023, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/rrze-legal", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1024, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/rrze-legal", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1025, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wp-content/plugins/jjarolim-tracking/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1026, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/acton/bn/tracker", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1027, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "etracker.de/optin_overlay.php", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1028, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/libraries/google/do-not-track.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1029, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tracking-permission-dialog.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1030, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "origin-images.wikia.com/fandom-ae-assets/tracking-opt-in", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1031, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tracking-opt-in.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1032, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/contaotrackingmanager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1033, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/peg_utils/tracking/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1034, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/kixsimpletrack/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1035, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rodo.js?pp_pr=", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1036, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/templates/rodo/rodo.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1037, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/rodo/rodo.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1038, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/rodo.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1039, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "rodo.agora.pl/agreement/check", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1040, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/min-js?f=js/rodo.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1041, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rodo_rmf", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1042, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/popup/rodo.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1043, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rodo/rodo.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1044, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rodo-agreement-popup.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1045, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "www.capitol.fr/streaming/cnil/cnil.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1046, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "prismamediadigital.com/cnil.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1047, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "newsletters.ftv-preprod.fr/cnil/js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1048, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Dock/DockContent/Cards/GDPRCard/index.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1049, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "nanoGDPR.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1050, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/GDPR/GDPR.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["3ds.com"] } }, { "id": 1051, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/GDPRPanelComponent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1052, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tdn.r42tag.com/lib/ut", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1053, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "c.betrad.com/pub/third.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1054, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "fundingchoices.google.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1055, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "fundingchoicesmessages.google.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": [ "rtl.hr", "fernsehserien.de", "bbc.com", "iol.pt", "stooq.pl", "stooq.com" ] } }, { "id": 1056, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "spiffymachine.com/v2/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1057, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.userdatatrust.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1058, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.pubguru.com/pg.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1059, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "qualtrics.com/WRSiteInterceptEngine", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["logmeininc.com"] } }, { "id": 1060, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "a.svtrd.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["ns.nl"] } }, { "id": 1061, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "foolcdn.com/mms/resources/js/international-visitor-notice-js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1062, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "heraeus.com/media/system_files/special_applications/heraues_datapolicy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1063, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "libraries.wmgartistservices.com/pplightbox", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1064, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "assets.ubembed.com/universal", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1065, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "pi.pardot.com/analytics", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1066, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "api.useinsider.com/ins.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1067, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "a.optmnstr.com/app/js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1068, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "borderfree.com/v1/dist/cbt.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1069, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.justuno.com/mwgt", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1070, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "caringzinc.com/v2", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1071, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "dam.bbcchannels.com/m/2fmpg/js/outside-iframe.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1072, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "dam.bbcchannels.com/m/2fmph/js/outside-iframe.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1073, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.truendo.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1074, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "intelligentscissors.com/v2", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1075, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.adnuntius.com/adn.dmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1076, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cct-pubweb.com/ccpa", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1077, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "widgets.legalmonster.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1078, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "nexus.ensighten.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1079, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "einfachonline.com/sid", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1080, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/railwayreason.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1081, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/teenytinycellar.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1082, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/lovelydrum.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1083, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/hatefulrequest.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1084, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/aloofvest.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1085, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacidade.api.milvus.com.br", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1086, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "global.ketchcdn.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1087, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "e-i.com/SITW", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1088, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pleasantpump.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1089, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fearlessfaucet.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1090, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "unpkg.com/orejime", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1091, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/superficialeyes.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1092, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cassiecloud.com/loader.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1093, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/childlikeform.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1094, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "baycloud.com/tgcl.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1095, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/chickensstation.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1096, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/handsomelyhealth.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1097, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "s2.getsitecontrol.com/widgets", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1098, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "prismamediadigital.com/cnil.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1099, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.efilli.com/efl.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1100, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "api.brookiebot.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1101, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tiqcdn.com/utag/tui/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1102, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/basketballbelieve.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1103, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/colossalchance.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1104, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/aliasanvil.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1105, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "api.byscuit.com/data/client", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1106, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.lawwwing.com/widgets", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1107, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/hallowedinvention.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1108, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "pcz.pl/static/js/moo-cooker.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1109, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eucd/eucd.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1110, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cinabre.sudoc.abes.fr/psi_gui/js/bandeau.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1111, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/euopties.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1112, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cknotiz.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1113, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/common/disclaimer/load.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1114, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wpjslib-chunk-notification.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1115, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cpb.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1116, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.tagsOptOut.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1117, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "app.termly.io/embed.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1118, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "static.wpolityce.pl/rhododendron/js/terms_of_service.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1119, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/info_cook.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1120, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/datenschutz.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1121, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "c.sd1.fr/cn/cn.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1122, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "zgody.infor.pl/build/assets/js/main.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1123, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "static.s-sfr.fr/stats/sbtF.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1124, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dsgvo_2018.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1125, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/dsgvo.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1126, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "partner.vxcp.de/_js/vxcp_Common.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1127, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc.js?renew=false&referer=www.rs2.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1128, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/twcdisclaimer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1129, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jqueryCL.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1130, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/trustArcHelper.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1131, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "widget.clym.io/clym.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1132, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "htmedia.in/analytics-js/dap.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1133, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ufti/uftiLoader.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1134, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/includes/pltk/pltk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1135, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/onstuimig-tag-manager/base/adf-tm-base-min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1136, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/GDRP_banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1137, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ads/rgpd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1138, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "samtykker.agm.as/agent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1139, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "samtykker.agdermedia.no/agent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1140, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/PopUpPriva/PopPrivacymin.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1141, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "dialogue.sp-prod.net/messagingWithoutDetection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "excludedInitiatorDomains": ["globalplayer.com"] } }, { "id": 1142, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/datenschutz.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1143, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccm19.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1144, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/app.dsgvo.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1145, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/trigoAboveBox.jquery.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1146, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dsgvoinit.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1147, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/styleguide/mxqasqco.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1148, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tvp-tcfapi.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1149, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ccm19.de/app/public/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1150, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ccm.ceasy.de/public/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1151, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/orejime/js/orejime.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1152, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccm/public/app.js?apiKey", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1153, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ccm19.de/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1154, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wwwschutz.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1155, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccm19/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1156, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dsvgobanner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1157, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wecoma-lite.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1158, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "resourcesurw.azureedge.net/js/cc55.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1159, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccnst/ccbundle.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1160, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cc.mpa-web.de/public/app.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1161, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/klaro/klaro-pe.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1162, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "klaro-no-css.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1163, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ionic-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1164, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ckpl-webc.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1165, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "wagtail_tag_manager/wtm.bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1166, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "www.capitol.fr/streaming/cnil/cnil.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1167, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Dock/DockContent/Cards/GDPRCard/index.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1168, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "nanoGDPR.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1169, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "newsletters.ftv-preprod.fr/cnil/js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1170, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/components/es6/PrivacyPolicy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1171, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "js.hs-analytics.net/analytics", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1172, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jsdelivr.net/wp/wp-slimstat", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1173, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/dsvgo.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1174, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/GDPR/GDPR.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1175, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "htmedia.in/analytics-js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1176, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/rgpd/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1177, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/avia-bootstrap/js/klaro/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1178, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/tarteaucitron", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1179, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pandectes-core.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1180, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/orejime.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1181, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "jsdelivr.net/npm/cookify", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1182, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cbgCConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1183, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ckpl-webc.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1184, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/klaro-no-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1185, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tcfapi/tcfapi.umd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1186, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "mein.clickskeks.at/app.js?apiKey", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"] } }, { "id": 1187, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consumer/cookie/client", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bt.com"] } }, { "id": 1188, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/frontend/ajax/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["romstal.ro"] } }, { "id": 1189, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rodo-agreement-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gremimedia.pl"] } }, { "id": 1190, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rodo-agreement-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zw.com.pl"] } }, { "id": 1191, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gtm.js?id=GTM-TCT2RJ", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fullrate.dk"] } }, { "id": 1192, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["paruvendu.fr"] } }, { "id": 1193, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "datenschutz.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wumpus-gollum-forum.de"] } }, { "id": 1194, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bho_infobar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ruecken-zentrum.de"] } }, { "id": 1195, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent/message.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ksta.de"] } }, { "id": 1196, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.truste.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["oracle.com"] } }, { "id": 1197, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cui-cookie-policy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["congstar.de"] } }, { "id": 1198, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bitly.com"] } }, { "id": 1199, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy/popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["cdaction.pl"] } }, { "id": 1200, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["knaufdoehetzelf.nl"] } }, { "id": 1201, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["louisvuitton.com"] } }, { "id": 1202, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/spmsg_addetection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tvspielfilm.de"] } }, { "id": 1203, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/firebox-gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["thelittleboxoffice.com"] } }, { "id": 1204, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.css", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["scholieren.com"] } }, { "id": 1205, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiescript.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["basiszinssatz.de"] } }, { "id": 1206, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies/bundle", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lucozadeenergy.com"] } }, { "id": 1207, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["guitarbackingtrack.com"] } }, { "id": 1208, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ip-consent.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["weersverwachting.nl"] } }, { "id": 1209, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiebot.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fluke.com"] } }, { "id": 1210, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tui-cookie-bar.html", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tuicruises.com"] } }, { "id": 1211, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-option.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["der-warnemuender.de"] } }, { "id": 1212, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["sexmarkt.nl"] } }, { "id": 1213, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ato-cookiebanner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["haag-streit.com"] } }, { "id": 1214, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["cummins.com"] } }, { "id": 1215, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/spd/spd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["woman.bg"] } }, { "id": 1216, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/spd/spd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["novini.bg"] } }, { "id": 1217, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/spd.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["sportal.bg"] } }, { "id": 1218, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieman.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["erfurter-bahn.de"] } }, { "id": 1219, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rg-gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lombardafiltri.it"] } }, { "id": 1220, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ibox.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aqualip.de"] } }, { "id": 1221, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cb-scripts", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["convertir-une-image.com"] } }, { "id": 1222, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.teraz.sk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["teraz.sk"] } }, { "id": 1223, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-policy.css", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["independent.com.mt"] } }, { "id": 1224, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/yello-cookie-layer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["yello.de"] } }, { "id": 1225, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["cashconverters.es"] } }, { "id": 1226, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/zsmessagebar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["christian.education"] } }, { "id": 1227, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pmc-pp-tou/privacy.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["rollingstone.com"] } }, { "id": 1228, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["outage.report"] } }, { "id": 1229, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/meWantCookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gamigo.de"] } }, { "id": 1230, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/meWantCookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gamigo.com"] } }, { "id": 1231, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bonniermag.se"] } }, { "id": 1232, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccpa/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lakesideparkmodels.com"] } }, { "id": 1233, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.umdaac.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["yoyogames.com"] } }, { "id": 1234, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/vendor/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["norma-connect.de"] } }, { "id": 1235, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookiePolicy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["test-aankoop.be"] } }, { "id": 1236, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "aliveachiever.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["dailydot.com"] } }, { "id": 1237, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-bar.salessquad.co.uk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["blochworld.com"] } }, { "id": 1238, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/coobann", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aida.de"] } }, { "id": 1239, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dsgvoCC.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["strabag-pfs.de"] } }, { "id": 1240, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.euck.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["avon-insurance.co.uk"] } }, { "id": 1241, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/cookie-popup", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["simyo.nl"] } }, { "id": 1242, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bottom.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["herokuapp.com"] } }, { "id": 1243, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["melaniemartinezmusic.com"] } }, { "id": 1244, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cm-body.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tarnkappe.info"] } }, { "id": 1245, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/idgy_gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["metallumnovum.lt"] } }, { "id": 1246, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jw.cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["jacquelinewilson.co.uk"] } }, { "id": 1247, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["iban-rechner.de"] } }, { "id": 1248, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/kameleoon.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["hanseaticbank.de"] } }, { "id": 1249, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["carriereonline.com"] } }, { "id": 1250, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eloqua", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["visma.fi"] } }, { "id": 1251, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eloqua", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["visma.no"] } }, { "id": 1252, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eloqua", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["visma.se"] } }, { "id": 1253, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eloqua", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["visma.nl"] } }, { "id": 1254, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eloqua", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["visma.com"] } }, { "id": 1255, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_banner_test.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["shinedown.com"] } }, { "id": 1256, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/streamify-gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["streamingbolaget.se"] } }, { "id": 1257, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies_alert.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lineadombra.it"] } }, { "id": 1258, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kidstaff.com.ua"] } }, { "id": 1259, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["catan.com"] } }, { "id": 1260, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["winbrok.es"] } }, { "id": 1261, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eurogdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["jcbeurope.eu"] } }, { "id": 1262, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ig_cookie_frontend", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["volksbund.de"] } }, { "id": 1263, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "bounceexchange.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lumberliquidators.com"] } }, { "id": 1264, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["keltendorf-mitterkirchen.at"] } }, { "id": 1265, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mitterkirchen.at"] } }, { "id": 1266, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vils.at"] } }, { "id": 1267, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tarteaucitron/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ratp.fr"] } }, { "id": 1268, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "CheckCookiePolicy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["remixshop.com"] } }, { "id": 1269, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/japfg/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["myfridgefood.com"] } }, { "id": 1270, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["raggal.at"] } }, { "id": 1271, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["haugsdorf.at"] } }, { "id": 1272, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["umhausen.at"] } }, { "id": 1273, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookienote/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wittetools.com"] } }, { "id": 1274, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr-idgy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vejutechnika.lt"] } }, { "id": 1275, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pltk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["goodcontent.pl"] } }, { "id": 1276, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tarteaucitron/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["arena-now.de"] } }, { "id": 1277, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/surbma-yes-no-popup/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bysarahkhan.com"] } }, { "id": 1278, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tryinteract.com"] } }, { "id": 1279, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consentMgmt/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["comdirect.de"] } }, { "id": 1280, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["pasztor.at"] } }, { "id": 1281, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tealium-external/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["weltverbesserer.de"] } }, { "id": 1282, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiesettings.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aarhusmotion.dk"] } }, { "id": 1283, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/oil", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tv2.dk"] } }, { "id": 1284, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieajx", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["goettgen.de"] } }, { "id": 1285, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.parkers.co.uk", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["parkers.co.uk"] } }, { "id": 1286, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "lovelydrum.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["insidermonkey.com"] } }, { "id": 1287, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/intuCookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["intu.co.uk"] } }, { "id": 1288, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tags.tiqcdn.com/utag", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["123-reg.co.uk"] } }, { "id": 1289, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "stormyachiever.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["thewindowsclub.com"] } }, { "id": 1290, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gcb/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gallup.com"] } }, { "id": 1291, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/native-message", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wiwo.de"] } }, { "id": 1292, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "koekje.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["baustellenabsicherung24.de"] } }, { "id": 1293, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/getdisclaimer", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wikifolio.com"] } }, { "id": 1294, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/echonetcookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["muenchenticket.de"] } }, { "id": 1295, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies-anekis.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["premiosopenbank.com"] } }, { "id": 1296, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["latoisondor.com"] } }, { "id": 1297, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cbgCConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["carlsberggroup.com"] } }, { "id": 1298, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["jugendregion.at"] } }, { "id": 1299, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp-v2/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["youmath.it"] } }, { "id": 1300, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/permission/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["1und1.de"] } }, { "id": 1301, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "js.driftt.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zenroom.org"] } }, { "id": 1302, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/bwx-cookie-consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nosta.com"] } }, { "id": 1303, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie_settings", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aerotime.aero"] } }, { "id": 1304, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["stadt-bobingen.de"] } }, { "id": 1305, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "troubledtail.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["interestingengineering.com"] } }, { "id": 1306, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tracking.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["arsys.es"] } }, { "id": 1307, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tracking.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["arsys.fr"] } }, { "id": 1308, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tracking.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["arsys.pt"] } }, { "id": 1309, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tracking.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["arsys.net"] } }, { "id": 1310, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy/Bootstrap", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tescobank.com"] } }, { "id": 1311, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["adlkofen.de"] } }, { "id": 1312, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["klinikwersbach.de"] } }, { "id": 1313, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieWall/clb.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["innogy.pl"] } }, { "id": 1314, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["e-pages.dk"] } }, { "id": 1315, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/easycmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lagerhaus.at"] } }, { "id": 1316, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookielab/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ol.fr"] } }, { "id": 1317, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/thcookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["megadruck.de"] } }, { "id": 1318, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["catan.de"] } }, { "id": 1319, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["qcnet.com"] } }, { "id": 1320, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/msmCookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["moneysupermarket.com"] } }, { "id": 1321, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eon-com-tracking-consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["eon.com"] } }, { "id": 1322, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/website-cookie-preferences", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nationaltrust.org.uk"] } }, { "id": 1323, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-bundle", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zoopla.co.uk"] } }, { "id": 1324, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-wall", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["xebia.com"] } }, { "id": 1325, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["dhl.com"] } }, { "id": 1326, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cp01.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["jzzo.com"] } }, { "id": 1327, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/klaro/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["salesviewer.com"] } }, { "id": 1328, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr.bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mediacourant.nl"] } }, { "id": 1329, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-management/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["plasteurope.com"] } }, { "id": 1330, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["dragnsurvey.com"] } }, { "id": 1331, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/snap-popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["maggiore.it"] } }, { "id": 1332, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/snap-popup.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["amicoblu.it"] } }, { "id": 1333, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/apprise", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["podrozerowerowe.info"] } }, { "id": 1334, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/data-consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["stadtwerke-luebz.de"] } }, { "id": 1335, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/neo-cookie-layer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["united-domains.de"] } }, { "id": 1336, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieWidget.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["peter-bringts.de"] } }, { "id": 1337, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lux-residence.com"] } }, { "id": 1338, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-manager-v", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["df.eu"] } }, { "id": 1339, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mobilejob.com"] } }, { "id": 1340, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["strato.de"] } }, { "id": 1341, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["strato.nl"] } }, { "id": 1342, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["strato.fr"] } }, { "id": 1343, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["strato.es"] } }, { "id": 1344, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["strato-hosting.co.uk"] } }, { "id": 1345, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["renault.de"] } }, { "id": 1346, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/checkCookieConsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["andrewssykes.fr"] } }, { "id": 1347, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-manager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["cornelsen.de"] } }, { "id": 1348, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["synopsys.com"] } }, { "id": 1349, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["total.com"] } }, { "id": 1350, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tcf2.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["0calc.com"] } }, { "id": 1351, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tcf2.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["0rechner.de"] } }, { "id": 1352, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tcf2.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["0calc.fr"] } }, { "id": 1353, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gusto.at"] } }, { "id": 1354, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dsgvo-opt-in.css", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kosta.at"] } }, { "id": 1355, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["total.nl"] } }, { "id": 1356, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dywc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["radioschwaben.de"] } }, { "id": 1357, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/microsite-consent-disclaimer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lyxoretf.com"] } }, { "id": 1358, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/eea", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["psychcentral.com"] } }, { "id": 1359, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sm-policy-banner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["storage-mart.com"] } }, { "id": 1360, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/unitb-cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["juedische-allgemeine.de"] } }, { "id": 1361, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["casanetwork.hu"] } }, { "id": 1362, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/legal/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kamkabel.ru"] } }, { "id": 1363, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/accept.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["oreluniver.ru"] } }, { "id": 1364, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/doria.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lactease.com"] } }, { "id": 1365, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "js.driftt.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ikonltd.co.uk"] } }, { "id": 1366, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/esb-privacy.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["erstebank.hr"] } }, { "id": 1367, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mediamus-cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["swl-unser-stadtwerk.de"] } }, { "id": 1368, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/vinegar.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["stadtwerke-herne.de"] } }, { "id": 1369, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/vhs-assets-cookie-control-js.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["planet-beruf.de"] } }, { "id": 1370, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ob_rgpd/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["orangebank.fr"] } }, { "id": 1371, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mintos.com"] } }, { "id": 1372, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/syno_cookie_element", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["synology.com"] } }, { "id": 1373, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jcookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["javhd.com"] } }, { "id": 1374, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["sparkasse.at"] } }, { "id": 1375, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/PoliticaCookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fincaraiz.com.co"] } }, { "id": 1376, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Team23_SimpleCookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kleiderhelden.com"] } }, { "id": 1377, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["esprit.de"] } }, { "id": 1378, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieCutter", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["thebureauinvestigates.com"] } }, { "id": 1379, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ing.es"] } }, { "id": 1380, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tenable.com"] } }, { "id": 1381, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["thetrainline.com"] } }, { "id": 1382, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["op.fi"] } }, { "id": 1383, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.bundle.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fastbill.com"] } }, { "id": 1384, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gaming-style.com"] } }, { "id": 1385, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["transparenzregister.de"] } }, { "id": 1386, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/uc/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mcmakler.de"] } }, { "id": 1387, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-policy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ubuntu.com"] } }, { "id": 1388, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zitatezumnachdenken.com"] } }, { "id": 1389, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["metoffice.gov.uk"] } }, { "id": 1390, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["churchofengland.org"] } }, { "id": 1391, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cnst.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tauschticket.de"] } }, { "id": 1392, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["liberbank.es"] } }, { "id": 1393, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/iCookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["h-supertools.com"] } }, { "id": 1394, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/he-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["dennree.de"] } }, { "id": 1395, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/uc_cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gastro24.de"] } }, { "id": 1396, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bopla.de"] } }, { "id": 1397, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["telerama.fr"] } }, { "id": 1398, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["courrierinternational.com"] } }, { "id": 1399, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vodkaster.com"] } }, { "id": 1400, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "brsimg.com/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["boursorama.com"] } }, { "id": 1401, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "trcking.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["canalplus.com"] } }, { "id": 1402, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["hauptbahnhofcity.wien"] } }, { "id": 1403, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["pons.com"] } }, { "id": 1404, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiefly/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["internet-rockstars.com"] } }, { "id": 1405, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["stepstone.de"] } }, { "id": 1406, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["stepstone.at"] } }, { "id": 1407, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["santander.pl"] } }, { "id": 1408, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "trcking.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["canal-plus.com"] } }, { "id": 1409, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aftenposten.no"] } }, { "id": 1410, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["coca-cola-deutschland.de"] } }, { "id": 1411, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-layer/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bahn.de"] } }, { "id": 1412, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["yodel.co.uk"] } }, { "id": 1413, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["digit-photo.com"] } }, { "id": 1414, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/checkcookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["iracing.com"] } }, { "id": 1415, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["avast.com"] } }, { "id": 1416, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vg.no"] } }, { "id": 1417, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tek.no"] } }, { "id": 1418, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["finn.no"] } }, { "id": 1419, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lifesycle.co.uk"] } }, { "id": 1420, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["next-immo.com"] } }, { "id": 1421, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fnGdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["financer.com"] } }, { "id": 1422, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-layer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mercedes-benz.io"] } }, { "id": 1423, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/messagingNoTcfApi.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["svd.se"] } }, { "id": 1424, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/sygnal42-gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["blitzrechner.de"] } }, { "id": 1425, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieLayer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["multipower.com"] } }, { "id": 1426, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_management.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["meteoradar.ch"] } }, { "id": 1427, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-preferences", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["halebop.se"] } }, { "id": 1428, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nif.no"] } }, { "id": 1429, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/klaro-no-css.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["frag-einen-anwalt.de"] } }, { "id": 1430, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["stema.de"] } }, { "id": 1431, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieBox.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vinos.de"] } }, { "id": 1432, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["swisscom.ch"] } }, { "id": 1433, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["schwaebische.de"] } }, { "id": 1434, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["guldsmykket.dk"] } }, { "id": 1435, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mieterengel.de"] } }, { "id": 1436, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/uc_cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gastro-hero.de"] } }, { "id": 1437, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kurnik.pl"] } }, { "id": 1438, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dg-governance/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nytimes.com"] } }, { "id": 1439, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dg-governance/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["inyt.com"] } }, { "id": 1440, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zukunftsheizen.de"] } }, { "id": 1441, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["truphone.com"] } }, { "id": 1442, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["oka.be"] } }, { "id": 1443, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["prada.com"] } }, { "id": 1444, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["online-spellcheck.com"] } }, { "id": 1445, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/it-cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["geze.de"] } }, { "id": 1446, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/it-cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["geze.pl"] } }, { "id": 1447, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ConsentInit.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["smartphonehoesjes.nl"] } }, { "id": 1448, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieser.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aco.com.pl"] } }, { "id": 1449, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bluelightcard.co.uk"] } }, { "id": 1450, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/vfConsentCookiesCs.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vodafone.cz"] } }, { "id": 1451, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bt.no"] } }, { "id": 1452, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nwb-jobboerse.de"] } }, { "id": 1453, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/almacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ampparit.com"] } }, { "id": 1454, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["avg.com"] } }, { "id": 1455, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["washingtonpost.com"] } }, { "id": 1456, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aftenbladet.no"] } }, { "id": 1457, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_flyout.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["festo.com"] } }, { "id": 1458, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/klaro.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["beckhoff.com"] } }, { "id": 1459, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Manolo_CookieConsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["manoloblahnik.com"] } }, { "id": 1460, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/LdCookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["landkreis-eichstaett.de"] } }, { "id": 1461, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/legal-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["meineapotheke.de"] } }, { "id": 1462, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["inishpharmacy.com"] } }, { "id": 1463, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-main", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["godaddy.com"] } }, { "id": 1464, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ultraleicht-trekking.com"] } }, { "id": 1465, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/base-nf.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lecanardenchaine.fr"] } }, { "id": 1466, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "toestemmingen.snp.nl", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["snp.nl"] } }, { "id": 1467, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mendeley.com"] } }, { "id": 1468, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cm/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["howatherm.de"] } }, { "id": 1469, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/osb-cmp.min.mjs", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tvgids.nl"] } }, { "id": 1470, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["01net.com"] } }, { "id": 1471, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.maerkischekiste.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["maerkischekiste.de"] } }, { "id": 1472, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["coca-cola.pl"] } }, { "id": 1473, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["coca-cola.co.uk"] } }, { "id": 1474, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["coca-cola.dk"] } }, { "id": 1475, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/it_nsc.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nonsolocap.it"] } }, { "id": 1476, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["totalwar.com"] } }, { "id": 1477, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gameswelt.ch"] } }, { "id": 1478, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gameswelt.de"] } }, { "id": 1479, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gameswelt.at"] } }, { "id": 1480, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "h1.versicherungsjournal.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["versicherungsjournal.de"] } }, { "id": 1481, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["topannonces.fr"] } }, { "id": 1482, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ellisphere.fr"] } }, { "id": 1483, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mdv.cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["biehler-cycling.com"] } }, { "id": 1484, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["here.com"] } }, { "id": 1485, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gizchina.com"] } }, { "id": 1486, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["consorsbank.de"] } }, { "id": 1487, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/alert-info.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["asus.com"] } }, { "id": 1488, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/alert-info_cn.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["asus.com.cn"] } }, { "id": 1489, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/almacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["etuovi.com"] } }, { "id": 1490, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/almacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vuokraovi.com"] } }, { "id": 1491, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/almacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["arvopaperi.fi"] } }, { "id": 1492, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp.genesis", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vodafone.de"] } }, { "id": 1493, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/newCookieChoice.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vape-phone.fr"] } }, { "id": 1494, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-settings-manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["aok.de"] } }, { "id": 1495, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Ronis_Cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["slaters.co.uk"] } }, { "id": 1496, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Hmm24_Cookiebanner/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zaehlerschrank24.de"] } }, { "id": 1497, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consensu.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["shinden.pl"] } }, { "id": 1498, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "js_privacy_cmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["e24.no"] } }, { "id": 1499, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieThough.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wochenblitz.com"] } }, { "id": 1500, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["beethoven.de"] } }, { "id": 1501, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieWall/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["eon.pl"] } }, { "id": 1502, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["technics.com"] } }, { "id": 1503, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookiebanner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["unibo.it"] } }, { "id": 1504, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["guloggratis.dk"] } }, { "id": 1505, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "trustarc.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["avantiwestcoast.co.uk"] } }, { "id": 1506, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["stickypassword.com"] } }, { "id": 1507, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_layer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["billiger-mietwagen.de"] } }, { "id": 1508, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/c22.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["cd.cz"] } }, { "id": 1509, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["o2.cz"] } }, { "id": 1510, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tarteaucitron/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["flaine.com"] } }, { "id": 1511, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/almacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nettikaravaani.com"] } }, { "id": 1512, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/stylecc", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["immozentral.com"] } }, { "id": 1513, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieConsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["loesdau.de"] } }, { "id": 1514, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zugtouren.de"] } }, { "id": 1515, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["t-mobile.cz"] } }, { "id": 1516, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gfps.com"] } }, { "id": 1517, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jquery.cookiekit.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["dgeg.gov.pt"] } }, { "id": 1518, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["minmote.no"] } }, { "id": 1519, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tc_privacy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["matmut.fr"] } }, { "id": 1520, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie_modal/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["shirtee.com"] } }, { "id": 1521, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/borlabs-cookie", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["finance-magazin.de"] } }, { "id": 1522, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["elle.se"] } }, { "id": 1523, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fyens.dk"] } }, { "id": 1524, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cf-analytics/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["chatfuel.com"] } }, { "id": 1525, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cc.labu24.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["labu24.de"] } }, { "id": 1526, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.ptj.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ptj.de"] } }, { "id": 1527, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.autopflege24.net", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["autopflege24.net"] } }, { "id": 1528, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["natune.net"] } }, { "id": 1529, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-banner", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["studio3t.com"] } }, { "id": 1530, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies_2020", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["pensionlisboa.com"] } }, { "id": 1531, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ensighten/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["britishairways.com"] } }, { "id": 1532, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ensighten/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ba.com"] } }, { "id": 1533, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["finanz-forum.de"] } }, { "id": 1534, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["konsultacjejst.pl"] } }, { "id": 1535, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/klaro", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tarif4you.de"] } }, { "id": 1536, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ccm.carpassion.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["carpassion.com"] } }, { "id": 1537, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["godt.no"] } }, { "id": 1538, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["pent.no"] } }, { "id": 1539, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["rtr.at"] } }, { "id": 1540, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-policy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["hotmart.com"] } }, { "id": 1541, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["widforss.no"] } }, { "id": 1542, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/acmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ameblo.jp"] } }, { "id": 1543, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["regentcentre.co.uk"] } }, { "id": 1544, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dkmb_gdpr.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fightful.com"] } }, { "id": 1545, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pandectes-core.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["arktis.de"] } }, { "id": 1546, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pandectes-core.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["apfelband.de"] } }, { "id": 1547, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/pandectes-core.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["schvarz.com"] } }, { "id": 1548, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/uc-cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["santander.de"] } }, { "id": 1549, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookpop.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["electromenager-compare.com"] } }, { "id": 1550, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["klart.se"] } }, { "id": 1551, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tv.nu"] } }, { "id": 1552, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["hemnet.se"] } }, { "id": 1553, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/aw-cookie.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["soprema.fr"] } }, { "id": 1554, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/fc_cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bosch-stiftung.de"] } }, { "id": 1555, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zurich.it"] } }, { "id": 1556, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.lavie.fr", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lavie.fr"] } }, { "id": 1557, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["twisto.pl"] } }, { "id": 1558, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["koestritzer.de"] } }, { "id": 1559, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiestarter", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["postfinance.ch"] } }, { "id": 1560, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/b2c.cookie-consent@latest/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["santanderconsumer.se"] } }, { "id": 1561, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/melindres/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["televes.com"] } }, { "id": 1562, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gdpr/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["broedersgezondheidswinkel.nl"] } }, { "id": 1563, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/DisclaimerControl.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["vontobel.com"] } }, { "id": 1564, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/gtcookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fixtout.fr"] } }, { "id": 1565, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["le-jackpot-des-medailles-safti.fr"] } }, { "id": 1566, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/RR_KE_ccm19/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kefalonia-griechenland.com"] } }, { "id": 1567, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/udg-uc-sdk.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["porsche.com"] } }, { "id": 1568, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mann.tv"] } }, { "id": 1569, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-page.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wz.de"] } }, { "id": 1570, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tarteaucitron.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["intex.fr"] } }, { "id": 1571, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mtb-news.de"] } }, { "id": 1572, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["rennrad-news.de"] } }, { "id": 1573, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["emtb-news.de"] } }, { "id": 1574, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["cyberghostvpn.com"] } }, { "id": 1575, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["playok.com"] } }, { "id": 1576, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "api.usercentrics.eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ing.de"] } }, { "id": 1577, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/iceCookie.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mamacar.cz"] } }, { "id": 1578, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/otBannerSdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["springerprofessional.de"] } }, { "id": 1579, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tui.se"] } }, { "id": 1580, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tui.fi"] } }, { "id": 1581, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/index.php?consent_manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["papierkram.de"] } }, { "id": 1582, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/user-consent-management/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["americanexpress.com"] } }, { "id": 1583, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["privatebanking.hsbc.com"] } }, { "id": 1584, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["hsbc.pl"] } }, { "id": 1585, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/smedia_cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["renzgroup.de"] } }, { "id": 1586, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.tanke-guenstig.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tanke-guenstig.de"] } }, { "id": 1587, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["histoire-et-civilisations.com"] } }, { "id": 1588, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/mfe-cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["santander.com.br"] } }, { "id": 1589, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ccm1.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["dlr.de"] } }, { "id": 1590, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent-manager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["pdf24.org"] } }, { "id": 1591, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lawgazette.co.uk"] } }, { "id": 1592, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "ccm.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["abload.de"] } }, { "id": 1593, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies_utils.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["uca.es"] } }, { "id": 1594, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookie-overlay.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bensanitair.nl"] } }, { "id": 1595, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/boldCookie_custom.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["investice.cz"] } }, { "id": 1596, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmapp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ccm19.de"] } }, { "id": 1597, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cdn.civiccomputing.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gfms.com"] } }, { "id": 1598, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ppp/js/permission-client", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["meinaccount.gmx.net"] } }, { "id": 1599, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ppp/js/permission-client", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["hilfe.gmx.net"] } }, { "id": 1600, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ppp/js/permission-client", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mein.web.de"] } }, { "id": 1601, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "tags.tiqcdn.com/utag", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lineadirecta.com"] } }, { "id": 1602, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ujam_tracking/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ujam.com"] } }, { "id": 1603, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ugp-api/webcontent/v1/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ae.com"] } }, { "id": 1604, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["jobsireland.ie"] } }, { "id": 1605, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "app.usercentrics.eu", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mainpost.de"] } }, { "id": 1606, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "pdcookiepro//views/js/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["czasnaherbate.net"] } }, { "id": 1607, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dtc-fe/policy-control", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["zs-watch.com"] } }, { "id": 1608, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["loveholidays.ie"] } }, { "id": 1609, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/inc/cookie_modal_ajax.php", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["georg.at"] } }, { "id": 1610, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/_next/static/chunks/cookieOverlay", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["galaxus.fr"] } }, { "id": 1611, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.werner-mertz.de", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["werner-mertz.de"] } }, { "id": 1612, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/dist/js/cmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["corporate.modivo.com"] } }, { "id": 1613, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/jimmsconsent", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["jimms.fi"] } }, { "id": 1614, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "chunk-cookie-consent-modal", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["jobup.ch"] } }, { "id": 1615, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Cookie/ccm19/public/index.php/app.js?", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["condair.de"] } }, { "id": 1616, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["eurostar.com"] } }, { "id": 1617, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/static/ct/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wko.at"] } }, { "id": 1618, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ui/common/scripts/cookies/cookieModalComponent-797ec8a07a.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["oem.no"] } }, { "id": 1619, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["politico.eu"] } }, { "id": 1620, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lovelybooks.de"] } }, { "id": 1621, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["idealo.de"] } }, { "id": 1622, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["idealo.at"] } }, { "id": 1623, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["idealo.es"] } }, { "id": 1624, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["idealo.fr"] } }, { "id": 1625, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["idealo.it"] } }, { "id": 1626, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["idealo.co.uk"] } }, { "id": 1627, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["femmeactuelle.fr"] } }, { "id": 1628, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["hausjournal.net"] } }, { "id": 1629, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["lepoint.fr"] } }, { "id": 1630, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["chefkoch.de"] } }, { "id": 1631, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["boerse.de"] } }, { "id": 1632, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["liberation.fr"] } }, { "id": 1633, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["caradisiac.com"] } }, { "id": 1634, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wrapperMessagingWithoutDetection", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["anderes-wort.de"] } }, { "id": 1635, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CookieBanner.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bauer-baumschulen.ch"] } }, { "id": 1636, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["strato.se"] } }, { "id": 1637, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["imusic.de"] } }, { "id": 1638, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["imusic.dk"] } }, { "id": 1639, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["imusic.no"] } }, { "id": 1640, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["imusic.se"] } }, { "id": 1641, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cc.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["imusic.co"] } }, { "id": 1642, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["comparethemarket.com"] } }, { "id": 1643, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["malservice.aftonbladet.se"] } }, { "id": 1644, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/brabo-cookie/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["brandpreventiewinkel.nl"] } }, { "id": 1645, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/almacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nettimoto.com"] } }, { "id": 1646, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["futura-sciences.com"] } }, { "id": 1647, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-box", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["schmalz.com"] } }, { "id": 1648, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/popupConsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["the-fence.com"] } }, { "id": 1649, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-files/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["paradoxwikis.com"] } }, { "id": 1650, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookieconsent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["expert.es"] } }, { "id": 1651, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.quantcast.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gettr.com"] } }, { "id": 1652, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "coco.we-online.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["we-online.com"] } }, { "id": 1653, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/modulos/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["villagrancanaria.com"] } }, { "id": 1654, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["carhartt.com"] } }, { "id": 1655, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/almacmp", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kotikokki.net"] } }, { "id": 1656, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rcs_cpmt/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["open.online"] } }, { "id": 1657, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rcs_cpmt/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["style.corriere.it"] } }, { "id": 1658, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ph-cookie-helper-mu/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["uwartsonline.nl"] } }, { "id": 1659, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": ".consent-", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["pcgames.de"] } }, { "id": 1660, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.quantcast.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["deviantart.com"] } }, { "id": 1661, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["omni.se"] } }, { "id": 1662, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Libs/cookies", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nadeta.cz"] } }, { "id": 1663, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/standard-cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["standard.sk"] } }, { "id": 1664, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Disclaimer.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["private.com"] } }, { "id": 1665, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["seeandso.com"] } }, { "id": 1666, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/privacy/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["patente.it"] } }, { "id": 1667, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/utag.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["tk-aerztefuehrer.de"] } }, { "id": 1668, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/rcs_cpmt/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["giroditalia.it"] } }, { "id": 1669, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["behindthename.com"] } }, { "id": 1670, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "it-cc.index.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["boellhoff.com"] } }, { "id": 1671, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiecontrol.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["rembutiken.se"] } }, { "id": 1672, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["deutsches-schulportal.de"] } }, { "id": 1673, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Custom_clubhinweis", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["ofdb.de"] } }, { "id": 1674, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["elpais.com"] } }, { "id": 1675, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.quantcast.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["pr0gramm.com"] } }, { "id": 1676, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "cmp.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["helthjem.no"] } }, { "id": 1677, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["webkamery.online"] } }, { "id": 1678, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/infrasdk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["nofluffjobs.com"] } }, { "id": 1679, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["diariodejerez.es"] } }, { "id": 1680, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["leonoticias.com"] } }, { "id": 1681, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookies_master.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["bayern.de"] } }, { "id": 1682, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["france24.com"] } }, { "id": 1683, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/CKCookieConsent.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["caseking.de"] } }, { "id": 1684, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gea-waldviertler.at"] } }, { "id": 1685, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookie-consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["gea-waldviertler.de"] } }, { "id": 1686, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ccm_", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["kro-ncrv.nl"] } }, { "id": 1687, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["retinatendencias.com"] } }, { "id": 1688, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/tac.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["avenuedelabrique.com"] } }, { "id": 1689, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["eldiadecordoba.es"] } }, { "id": 1690, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/ConsentManager/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["flvw.de"] } }, { "id": 1691, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "analytics-consent-manager", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["terveystalo.com"] } }, { "id": 1692, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "seznam.cz", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["firmy.cz"] } }, { "id": 1693, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-center.org", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["fameplay.tv"] } }, { "id": 1694, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "poool.fr/access.min.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["spektrum.de"] } }, { "id": 1695, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/wimc_gtm_consent/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["wingo.ch"] } }, { "id": 1696, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp_puk.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["commerzbank.de"] } }, { "id": 1697, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/scmp-int.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["mapy.cz"] } }, { "id": 1698, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "privacy-mgmt.com", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["immobilien.derstandard.at"] } }, { "id": 1699, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/js/privacy", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["atu.de"] } }, { "id": 1700, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/consent.", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["onlinestempel.ch"] } }, { "id": 1701, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cookiewall/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["saeco.de"] } }, { "id": 1702, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/Cookies.js", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["sage.co.uk"] } }, { "id": 1703, "priority": 1, "action": { "type": "block" }, "condition": { "urlFilter": "/cmp/", "resourceTypes": ["script", "stylesheet", "xmlhttprequest", "image"], "initiatorDomains": ["liebherr.com"] } } ] ================================================ FILE: pkg/engine/headless/browser/element.go ================================================ package browser import ( "net/url" "strconv" "strings" "github.com/pkg/errors" "github.com/projectdiscovery/katana/pkg/engine/headless/types" ) const ( // buttonsCSSSelector is the css selector for all buttons buttonsCSSSelector = "button, input[type='button'], input[type='submit']" // linksCSSSelector is the css selector for all anchor tags linksCSSSelector = "a" ) // isElementDisabled checks if a button element is disabled func isElementDisabled(element *types.HTMLElement) bool { if element.Attributes == nil { return false } // Standard HTML disabled attribute if _, disabled := element.Attributes["disabled"]; disabled { return true } // Tailwind or framework class-based detection if classAttr, ok := element.Attributes["class"]; ok { classList := strings.Fields(classAttr) for _, class := range classList { if class == "cursor-not-allowed" || class == "pointer-events-none" { return true } } } // Optionally support ARIA disabled if aria, ok := element.Attributes["aria-disabled"]; ok && (aria == "true" || aria == "1") { return true } return false } // FindNavigation attempts to find more navigations on the page which could // be done to find more links and pages. // // This includes the following - // 1. Forms // 2. Buttons // 3. Links // 4. Elements with event listeners // // The navigations found are unique across the page. The caller // needs to ensure they are unique globally before doing further actions with details. func (b *BrowserPage) FindNavigations() ([]*types.Action, error) { unique := make(map[string]struct{}) navigations := make([]*types.Action, 0) forms, err := b.GetAllForms() if err != nil { return nil, errors.Wrap(err, "could not get forms") } for _, form := range forms { for _, element := range form.Elements { if element.TagName != "BUTTON" { continue } // TODO: Check if this button is already in the unique map // and if so remove it unique[element.Hash()] = struct{}{} } hash := form.Hash() if _, found := unique[hash]; found { continue } unique[hash] = struct{}{} navigations = append(navigations, &types.Action{ Type: types.ActionTypeFillForm, Form: form, }) } buttons, err := b.GetAllElements(buttonsCSSSelector) if err != nil { return nil, errors.Wrap(err, "could not get buttons") } for _, button := range buttons { if isElementDisabled(button) { continue } hash := button.Hash() button.MD5Hash = hash if _, found := unique[hash]; found { continue } unique[hash] = struct{}{} navigations = append(navigations, &types.Action{ Type: types.ActionTypeLeftClick, Element: button, }) } scopeValidator := b.launcher.ScopeValidator() links, err := b.GetAllElements(linksCSSSelector) if err != nil { return nil, errors.Wrap(err, "could not get links") } info, err := b.Info() if err != nil { return nil, errors.Wrap(err, "could not get page info") } for _, link := range links { href := link.Attributes["href"] if href == "" { continue } resolvedHref, err := resolveURL(info.URL, href) if err != nil { continue } u, err := url.Parse(resolvedHref) if err != nil { continue } if u.Scheme != "http" && u.Scheme != "https" { continue } if !scopeValidator(resolvedHref) { continue } hash := link.Hash() link.MD5Hash = hash if _, found := unique[hash]; found { continue } unique[hash] = struct{}{} navigations = append(navigations, &types.Action{ Type: types.ActionTypeLeftClick, Element: link, }) } eventListeners, err := b.GetEventListeners() if err != nil { return nil, errors.Wrap(err, "could not get event listeners") } for _, listener := range eventListeners { if _, found := relevantEventListeners[listener.Type]; !found { continue } if listener.Element == nil { continue } hash := listener.Element.Hash() listener.Element.MD5Hash = hash if _, found := unique[hash]; found { continue } unique[hash] = struct{}{} navigations = append(navigations, types.ActionFromEventListener(listener)) } return navigations, nil } func (b *BrowserPage) GetAllElements(selector string) ([]*types.HTMLElement, error) { objects, err := b.Eval(`() => window.getAllElements(` + strconv.Quote(selector) + `)`) if err != nil { return nil, err } elements := make([]*types.HTMLElement, 0) if err := objects.Value.Unmarshal(&elements); err != nil { return nil, err } return elements, nil } func (b *BrowserPage) GetElementFromXpath(xpath string) (*types.HTMLElement, error) { object, err := b.Eval(`() => window.getElementFromXPath(` + strconv.Quote(xpath) + `)`) if err != nil { return nil, err } element := &types.HTMLElement{} if err := object.Value.Unmarshal(element); err != nil { return nil, err } return element, nil } func (b *BrowserPage) GetAllForms() ([]*types.HTMLForm, error) { objects, err := b.Eval(`() => window.getAllForms()`) if err != nil { return nil, err } elements := make([]*types.HTMLForm, 0) if err := objects.Value.Unmarshal(&elements); err != nil { return nil, err } return elements, nil } // GetEventListeners returns all event listeners on the page func (b *BrowserPage) GetEventListeners() ([]*types.EventListener, error) { listeners := make([]*types.EventListener, 0) eventlisteners, err := b.Eval(`() => window.__eventListeners`) if err == nil { _ = eventlisteners.Value.Unmarshal(&listeners) } // Also get inline event listeners var inlineEventListeners []struct { Element *types.HTMLElement `json:"element"` Listeners []struct { Type string `json:"type"` Listener string `json:"listener"` } `json:"listeners"` } inlineListeners, err := b.Eval(`() => window.getAllElementsWithEventListeners()`) if err != nil { return nil, err } if err := inlineListeners.Value.Unmarshal(&inlineEventListeners); err != nil { return nil, err } for _, inlineListener := range inlineEventListeners { for _, listener := range inlineListener.Listeners { listenerType := strings.TrimPrefix(listener.Type, "on") listeners = append(listeners, &types.EventListener{ Type: listenerType, Listener: listener.Listener, Element: inlineListener.Element, }) } } return listeners, nil } // NavigatedLink is a link navigated collected from one of the // navigation hooks. type NavigatedLink struct { URL string `json:"url"` Source string `json:"source"` } // GetNavigatedLinks returns all navigated links on the page func (b *BrowserPage) GetNavigatedLinks() ([]*NavigatedLink, error) { navigatedLinks, err := b.Eval(`() => window.__navigatedLinks`) if err != nil { return nil, err } listeners := make([]*NavigatedLink, 0) if err := navigatedLinks.Value.Unmarshal(&listeners); err != nil { return nil, err } return listeners, nil } // Define the map to hold event types var relevantEventListeners = map[string]struct{}{ // Focus and Blur events "focusin": {}, "focus": {}, "blur": {}, "focusout": {}, // Click and Mouse events "click": {}, "auxclick": {}, "mousedown": {}, "mouseup": {}, "dblclick": {}, "mouseover": {}, "mouseenter": {}, "mouseleave": {}, "mouseout": {}, "wheel": {}, "contextmenu": {}, // Key events "keydown": {}, "keypress": {}, "keyup": {}, // Form events "submit": {}, "input": {}, "change": {}, } // resolveURL resolves a potentially relative URL against a base URL func resolveURL(baseURLStr, href string) (string, error) { baseURL, err := url.Parse(baseURLStr) if err != nil { return "", err } resolvedURL, err := baseURL.Parse(href) if err != nil { return "", err } return resolvedURL.String(), nil } ================================================ FILE: pkg/engine/headless/browser/stealth/assets.go ================================================ // from stealth go-rod package stealth // JS for stealth const JS = `;(() => { (({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:'utils => {\n if (!window.chrome) {\n // Use the exact property descriptor found in headful Chrome\n // fetch it via ` + "`" + `Object.getOwnPropertyDescriptor(window, \'chrome\')` + "`" + `\n Object.defineProperty(window, \'chrome\', {\n writable: true,\n enumerable: true,\n configurable: false, // note!\n value: {} // We\'ll extend that later\n })\n }\n\n // That means we\'re running headful and don\'t need to mock anything\n if (\'app\' in window.chrome) {\n return // Nothing to do here\n }\n\n const makeError = {\n ErrorInInvocation: fn => {\n const err = new TypeError(` + "`" + `Error in invocation of app.${fn}()` + "`" + `)\n return utils.stripErrorWithAnchor(\n err,\n ` + "`" + `at ${fn} (eval at <anonymous>` + "`" + `\n )\n }\n }\n\n // There\'s a some static data in that property which doesn\'t seem to change,\n // we should periodically check for updates: ` + "`" + `JSON.stringify(window.app, null, 2)` + "`" + `\n const STATIC_DATA = JSON.parse(\n ` + "`" + `\n{\n "isInstalled": false,\n "InstallState": {\n "DISABLED": "disabled",\n "INSTALLED": "installed",\n "NOT_INSTALLED": "not_installed"\n },\n "RunningState": {\n "CANNOT_RUN": "cannot_run",\n "READY_TO_RUN": "ready_to_run",\n "RUNNING": "running"\n }\n}\n ` + "`" + `.trim()\n )\n\n window.chrome.app = {\n ...STATIC_DATA,\n\n get isInstalled() {\n return false\n },\n\n getDetails: function getDetails() {\n if (arguments.length) {\n throw makeError.ErrorInInvocation(` + "`" + `getDetails` + "`" + `)\n }\n return null\n },\n getIsInstalled: function getDetails() {\n if (arguments.length) {\n throw makeError.ErrorInInvocation(` + "`" + `getIsInstalled` + "`" + `)\n }\n return false\n },\n runningState: function getDetails() {\n if (arguments.length) {\n throw makeError.ErrorInInvocation(` + "`" + `runningState` + "`" + `)\n }\n return \'cannot_run\'\n }\n }\n utils.patchToStringNested(window.chrome.app)\n }',_args:[]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"utils => {\n if (!window.chrome) {\n // Use the exact property descriptor found in headful Chrome\n // fetch it via ` + "`" + `Object.getOwnPropertyDescriptor(window, 'chrome')` + "`" + `\n Object.defineProperty(window, 'chrome', {\n writable: true,\n enumerable: true,\n configurable: false, // note!\n value: {} // We'll extend that later\n })\n }\n\n // That means we're running headful and don't need to mock anything\n if ('csi' in window.chrome) {\n return // Nothing to do here\n }\n\n // Check that the Navigation Timing API v1 is available, we need that\n if (!window.performance || !window.performance.timing) {\n return\n }\n\n const { timing } = window.performance\n\n window.chrome.csi = function() {\n return {\n onloadT: timing.domContentLoadedEventEnd,\n startE: timing.navigationStart,\n pageT: Date.now() - timing.navigationStart,\n tran: 15 // Transition type or something\n }\n }\n utils.patchToString(window.chrome.csi)\n }",_args:[]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, { opts }) => {\n if (!window.chrome) {\n // Use the exact property descriptor found in headful Chrome\n // fetch it via ` + "`" + `Object.getOwnPropertyDescriptor(window, 'chrome')` + "`" + `\n Object.defineProperty(window, 'chrome', {\n writable: true,\n enumerable: true,\n configurable: false, // note!\n value: {} // We'll extend that later\n })\n }\n\n // That means we're running headful and don't need to mock anything\n if ('loadTimes' in window.chrome) {\n return // Nothing to do here\n }\n\n // Check that the Navigation Timing API v1 + v2 is available, we need that\n if (\n !window.performance ||\n !window.performance.timing ||\n !window.PerformancePaintTiming\n ) {\n return\n }\n\n const { performance } = window\n\n // Some stuff is not available on about:blank as it requires a navigation to occur,\n // let's harden the code to not fail then:\n const ntEntryFallback = {\n nextHopProtocol: 'h2',\n type: 'other'\n }\n\n // The API exposes some funky info regarding the connection\n const protocolInfo = {\n get connectionInfo() {\n const ntEntry =\n performance.getEntriesByType('navigation')[0] || ntEntryFallback\n return ntEntry.nextHopProtocol\n },\n get npnNegotiatedProtocol() {\n // NPN is deprecated in favor of ALPN, but this implementation returns the\n // HTTP/2 or HTTP2+QUIC/39 requests negotiated via ALPN.\n const ntEntry =\n performance.getEntriesByType('navigation')[0] || ntEntryFallback\n return ['h2', 'hq'].includes(ntEntry.nextHopProtocol)\n ? ntEntry.nextHopProtocol\n : 'unknown'\n },\n get navigationType() {\n const ntEntry =\n performance.getEntriesByType('navigation')[0] || ntEntryFallback\n return ntEntry.type\n },\n get wasAlternateProtocolAvailable() {\n // The Alternate-Protocol header is deprecated in favor of Alt-Svc\n // (https://www.mnot.net/blog/2016/03/09/alt-svc), so technically this\n // should always return false.\n return false\n },\n get wasFetchedViaSpdy() {\n // SPDY is deprecated in favor of HTTP/2, but this implementation returns\n // true for HTTP/2 or HTTP2+QUIC/39 as well.\n const ntEntry =\n performance.getEntriesByType('navigation')[0] || ntEntryFallback\n return ['h2', 'hq'].includes(ntEntry.nextHopProtocol)\n },\n get wasNpnNegotiated() {\n // NPN is deprecated in favor of ALPN, but this implementation returns true\n // for HTTP/2 or HTTP2+QUIC/39 requests negotiated via ALPN.\n const ntEntry =\n performance.getEntriesByType('navigation')[0] || ntEntryFallback\n return ['h2', 'hq'].includes(ntEntry.nextHopProtocol)\n }\n }\n\n const { timing } = window.performance\n\n // Truncate number to specific number of decimals, most of the ` + "`" + `loadTimes` + "`" + ` stuff has 3\n function toFixed(num, fixed) {\n var re = new RegExp('^-?\\\\d+(?:.\\\\d{0,' + (fixed || -1) + '})?')\n return num.toString().match(re)[0]\n }\n\n const timingInfo = {\n get firstPaintAfterLoadTime() {\n // This was never actually implemented and always returns 0.\n return 0\n },\n get requestTime() {\n return timing.navigationStart / 1000\n },\n get startLoadTime() {\n return timing.navigationStart / 1000\n },\n get commitLoadTime() {\n return timing.responseStart / 1000\n },\n get finishDocumentLoadTime() {\n return timing.domContentLoadedEventEnd / 1000\n },\n get finishLoadTime() {\n return timing.loadEventEnd / 1000\n },\n get firstPaintTime() {\n const fpEntry = performance.getEntriesByType('paint')[0] || {\n startTime: timing.loadEventEnd / 1000 // Fallback if no navigation occurred (` + "`" + `about:blank` + "`" + `)\n }\n return toFixed(\n (fpEntry.startTime + performance.timeOrigin) / 1000,\n 3\n )\n }\n }\n\n window.chrome.loadTimes = function() {\n return {\n ...protocolInfo,\n ...timingInfo\n }\n }\n utils.patchToString(window.chrome.loadTimes)\n }",_args:[{opts:{}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, { opts, STATIC_DATA }) => {\n if (!window.chrome) {\n // Use the exact property descriptor found in headful Chrome\n // fetch it via ` + "`" + `Object.getOwnPropertyDescriptor(window, 'chrome')` + "`" + `\n Object.defineProperty(window, 'chrome', {\n writable: true,\n enumerable: true,\n configurable: false, // note!\n value: {} // We'll extend that later\n })\n }\n\n // That means we're running headful and don't need to mock anything\n const existsAlready = 'runtime' in window.chrome\n // ` + "`" + `chrome.runtime` + "`" + ` is only exposed on secure origins\n const isNotSecure = !window.location.protocol.startsWith('https')\n if (existsAlready || (isNotSecure && !opts.runOnInsecureOrigins)) {\n return // Nothing to do here\n }\n\n window.chrome.runtime = {\n // There's a bunch of static data in that property which doesn't seem to change,\n // we should periodically check for updates: ` + "`" + `JSON.stringify(window.chrome.runtime, null, 2)` + "`" + `\n ...STATIC_DATA,\n // ` + "`" + `chrome.runtime.id` + "`" + ` is extension related and returns undefined in Chrome\n get id() {\n return undefined\n },\n // These two require more sophisticated mocks\n connect: null,\n sendMessage: null\n }\n\n const makeCustomRuntimeErrors = (preamble, method, extensionId) => ({\n NoMatchingSignature: new TypeError(\n preamble + ` + "`" + `No matching signature.` + "`" + `\n ),\n MustSpecifyExtensionID: new TypeError(\n preamble +\n ` + "`" + `${method} called from a webpage must specify an Extension ID (string) for its first argument.` + "`" + `\n ),\n InvalidExtensionID: new TypeError(\n preamble + ` + "`" + `Invalid extension id: '${extensionId}'` + "`" + `\n )\n })\n\n // Valid Extension IDs are 32 characters in length and use the letter ` + "`" + `a` + "`" + ` to ` + "`" + `p` + "`" + `:\n // https://source.chromium.org/chromium/chromium/src/+/master:components/crx_file/id_util.cc;drc=14a055ccb17e8c8d5d437fe080faba4c6f07beac;l=90\n const isValidExtensionID = str =>\n str.length === 32 && str.toLowerCase().match(/^[a-p]+$/)\n\n /** Mock ` + "`" + `chrome.runtime.sendMessage` + "`" + ` */\n const sendMessageHandler = {\n apply: function(target, ctx, args) {\n const [extensionId, options, responseCallback] = args || []\n\n // Define custom errors\n const errorPreamble = ` + "`" + `Error in invocation of runtime.sendMessage(optional string extensionId, any message, optional object options, optional function responseCallback): ` + "`" + `\n const Errors = makeCustomRuntimeErrors(\n errorPreamble,\n ` + "`" + `chrome.runtime.sendMessage()` + "`" + `,\n extensionId\n )\n\n // Check if the call signature looks ok\n const noArguments = args.length === 0\n const tooManyArguments = args.length > 4\n const incorrectOptions = options && typeof options !== 'object'\n const incorrectResponseCallback =\n responseCallback && typeof responseCallback !== 'function'\n if (\n noArguments ||\n tooManyArguments ||\n incorrectOptions ||\n incorrectResponseCallback\n ) {\n throw Errors.NoMatchingSignature\n }\n\n // At least 2 arguments are required before we even validate the extension ID\n if (args.length < 2) {\n throw Errors.MustSpecifyExtensionID\n }\n\n // Now let's make sure we got a string as extension ID\n if (typeof extensionId !== 'string') {\n throw Errors.NoMatchingSignature\n }\n\n if (!isValidExtensionID(extensionId)) {\n throw Errors.InvalidExtensionID\n }\n\n return undefined // Normal behavior\n }\n }\n utils.mockWithProxy(\n window.chrome.runtime,\n 'sendMessage',\n function sendMessage() {},\n sendMessageHandler\n )\n\n /**\n * Mock ` + "`" + `chrome.runtime.connect` + "`" + `\n *\n * @see https://developer.chrome.com/apps/runtime#method-connect\n */\n const connectHandler = {\n apply: function(target, ctx, args) {\n const [extensionId, connectInfo] = args || []\n\n // Define custom errors\n const errorPreamble = ` + "`" + `Error in invocation of runtime.connect(optional string extensionId, optional object connectInfo): ` + "`" + `\n const Errors = makeCustomRuntimeErrors(\n errorPreamble,\n ` + "`" + `chrome.runtime.connect()` + "`" + `,\n extensionId\n )\n\n // Behavior differs a bit from sendMessage:\n const noArguments = args.length === 0\n const emptyStringArgument = args.length === 1 && extensionId === ''\n if (noArguments || emptyStringArgument) {\n throw Errors.MustSpecifyExtensionID\n }\n\n const tooManyArguments = args.length > 2\n const incorrectConnectInfoType =\n connectInfo && typeof connectInfo !== 'object'\n\n if (tooManyArguments || incorrectConnectInfoType) {\n throw Errors.NoMatchingSignature\n }\n\n const extensionIdIsString = typeof extensionId === 'string'\n if (extensionIdIsString && extensionId === '') {\n throw Errors.MustSpecifyExtensionID\n }\n if (extensionIdIsString && !isValidExtensionID(extensionId)) {\n throw Errors.InvalidExtensionID\n }\n\n // There's another edge-case here: extensionId is optional so we might find a connectInfo object as first param, which we need to validate\n const validateConnectInfo = ci => {\n // More than a first param connectInfo as been provided\n if (args.length > 1) {\n throw Errors.NoMatchingSignature\n }\n // An empty connectInfo has been provided\n if (Object.keys(ci).length === 0) {\n throw Errors.MustSpecifyExtensionID\n }\n // Loop over all connectInfo props an check them\n Object.entries(ci).forEach(([k, v]) => {\n const isExpected = ['name', 'includeTlsChannelId'].includes(k)\n if (!isExpected) {\n throw new TypeError(\n errorPreamble + ` + "`" + `Unexpected property: '${k}'.` + "`" + `\n )\n }\n const MismatchError = (propName, expected, found) =>\n TypeError(\n errorPreamble +\n ` + "`" + `Error at property '${propName}': Invalid type: expected ${expected}, found ${found}.` + "`" + `\n )\n if (k === 'name' && typeof v !== 'string') {\n throw MismatchError(k, 'string', typeof v)\n }\n if (k === 'includeTlsChannelId' && typeof v !== 'boolean') {\n throw MismatchError(k, 'boolean', typeof v)\n }\n })\n }\n if (typeof extensionId === 'object') {\n validateConnectInfo(extensionId)\n throw Errors.MustSpecifyExtensionID\n }\n\n // Unfortunately even when the connect fails Chrome will return an object with methods we need to mock as well\n return utils.patchToStringNested(makeConnectResponse())\n }\n }\n utils.mockWithProxy(\n window.chrome.runtime,\n 'connect',\n function connect() {},\n connectHandler\n )\n\n function makeConnectResponse() {\n const onSomething = () => ({\n addListener: function addListener() {},\n dispatch: function dispatch() {},\n hasListener: function hasListener() {},\n hasListeners: function hasListeners() {\n return false\n },\n removeListener: function removeListener() {}\n })\n\n const response = {\n name: '',\n sender: undefined,\n disconnect: function disconnect() {},\n onDisconnect: onSomething(),\n onMessage: onSomething(),\n postMessage: function postMessage() {\n if (!arguments.length) {\n throw new TypeError(` + "`" + `Insufficient number of arguments.` + "`" + `)\n }\n throw new Error(` + "`" + `Attempting to use a disconnected port object` + "`" + `)\n }\n }\n return response\n }\n }",_args:[{opts:{runOnInsecureOrigins:!1},STATIC_DATA:{OnInstalledReason:{CHROME_UPDATE:"chrome_update",INSTALL:"install",SHARED_MODULE_UPDATE:"shared_module_update",UPDATE:"update"},OnRestartRequiredReason:{APP_UPDATE:"app_update",OS_UPDATE:"os_update",PERIODIC:"periodic"},PlatformArch:{ARM:"arm",ARM64:"arm64",MIPS:"mips",MIPS64:"mips64",X86_32:"x86-32",X86_64:"x86-64"},PlatformNaclArch:{ARM:"arm",MIPS:"mips",MIPS64:"mips64",X86_32:"x86-32",X86_64:"x86-64"},PlatformOs:{ANDROID:"android",CROS:"cros",LINUX:"linux",MAC:"mac",OPENBSD:"openbsd",WIN:"win"},RequestUpdateCheckStatus:{NO_UPDATE:"no_update",THROTTLED:"throttled",UPDATE_AVAILABLE:"update_available"}}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"utils => {\n /**\n * Input might look funky, we need to normalize it so e.g. whitespace isn't an issue for our spoofing.\n *\n * @example\n * video/webm; codecs=\"vp8, vorbis\"\n * video/mp4; codecs=\"avc1.42E01E\"\n * audio/x-m4a;\n * audio/ogg; codecs=\"vorbis\"\n * @param {String} arg\n */\n const parseInput = arg => {\n const [mime, codecStr] = arg.trim().split(';')\n let codecs = []\n if (codecStr && codecStr.includes('codecs=\"')) {\n codecs = codecStr\n .trim()\n .replace(` + "`" + `codecs=\"` + "`" + `, '')\n .replace(` + "`" + `\"` + "`" + `, '')\n .trim()\n .split(',')\n .filter(x => !!x)\n .map(x => x.trim())\n }\n return {\n mime,\n codecStr,\n codecs\n }\n }\n\n const canPlayType = {\n // Intercept certain requests\n apply: function(target, ctx, args) {\n if (!args || !args.length) {\n return target.apply(ctx, args)\n }\n const { mime, codecs } = parseInput(args[0])\n // This specific mp4 codec is missing in Chromium\n if (mime === 'video/mp4') {\n if (codecs.includes('avc1.42E01E')) {\n return 'probably'\n }\n }\n // This mimetype is only supported if no codecs are specified\n if (mime === 'audio/x-m4a' && !codecs.length) {\n return 'maybe'\n }\n\n // This mimetype is only supported if no codecs are specified\n if (mime === 'audio/aac' && !codecs.length) {\n return 'probably'\n }\n // Everything else as usual\n return target.apply(ctx, args)\n }\n }\n\n /* global HTMLMediaElement */\n utils.replaceWithProxy(\n HTMLMediaElement.prototype,\n 'canPlayType',\n canPlayType\n )\n }",_args:[]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, { opts }) => {\n utils.replaceGetterWithProxy(\n Object.getPrototypeOf(navigator),\n 'hardwareConcurrency',\n utils.makeHandler().getterValue(opts.hardwareConcurrency)\n )\n }",_args:[{opts:{hardwareConcurrency:4}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, { opts }) => {\n const languages = opts.languages.length\n ? opts.languages\n : ['en-US', 'en']\n utils.replaceGetterWithProxy(\n Object.getPrototypeOf(navigator),\n 'languages',\n utils.makeHandler().getterValue(Object.freeze([...languages]))\n )\n }",_args:[{opts:{languages:[]}}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, opts) => {\n const isSecure = document.location.protocol.startsWith('https')\n\n // In headful on secure origins the permission should be \"default\", not \"denied\"\n if (isSecure) {\n utils.replaceGetterWithProxy(Notification, 'permission', {\n apply() {\n return 'default'\n }\n })\n }\n\n // Another weird behavior:\n // On insecure origins in headful the state is \"denied\",\n // whereas in headless it's \"prompt\"\n if (!isSecure) {\n const handler = {\n apply(target, ctx, args) {\n const param = (args || [])[0]\n\n const isNotifications =\n param && param.name && param.name === 'notifications'\n if (!isNotifications) {\n return utils.cache.Reflect.apply(...arguments)\n }\n\n return Promise.resolve(\n Object.setPrototypeOf(\n {\n state: 'denied',\n onchange: null\n },\n PermissionStatus.prototype\n )\n )\n }\n }\n // Note: Don't use ` + "`" + `Object.getPrototypeOf` + "`" + ` here\n utils.replaceWithProxy(Permissions.prototype, 'query', handler)\n }\n }",_args:[{}]}),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, { fns, data }) => {\n fns = utils.materializeFns(fns)\n\n // That means we're running headful\n const hasPlugins = 'plugins' in navigator && navigator.plugins.length\n if (hasPlugins) {\n return // nothing to do here\n }\n\n const mimeTypes = fns.generateMimeTypeArray(utils, fns)(data.mimeTypes)\n const plugins = fns.generatePluginArray(utils, fns)(data.plugins)\n\n // Plugin and MimeType cross-reference each other, let's do that now\n // Note: We're looping through ` + "`" + `data.plugins` + "`" + ` here, not the generated ` + "`" + `plugins` + "`" + `\n for (const pluginData of data.plugins) {\n pluginData.__mimeTypes.forEach((type, index) => {\n plugins[pluginData.name][index] = mimeTypes[type]\n\n Object.defineProperty(plugins[pluginData.name], type, {\n value: mimeTypes[type],\n writable: false,\n enumerable: false, // Not enumerable\n configurable: true\n })\n Object.defineProperty(mimeTypes[type], 'enabledPlugin', {\n value:\n type === 'application/x-pnacl'\n ? mimeTypes['application/x-nacl'].enabledPlugin // these reference the same plugin, so we need to re-use the Proxy in order to avoid leaks\n : new Proxy(plugins[pluginData.name], {}), // Prevent circular references\n writable: false,\n enumerable: false, // Important: ` + "`" + `JSON.stringify(navigator.plugins)` + "`" + `\n configurable: true\n })\n })\n }\n\n const patchNavigator = (name, value) =>\n utils.replaceProperty(Object.getPrototypeOf(navigator), name, {\n get() {\n return value\n }\n })\n\n patchNavigator('mimeTypes', mimeTypes)\n patchNavigator('plugins', plugins)\n\n // All done\n }",_args:[{fns:{generateMimeTypeArray:"(utils, fns) => mimeTypesData => {\n return fns.generateMagicArray(utils, fns)(\n mimeTypesData,\n MimeTypeArray.prototype,\n MimeType.prototype,\n 'type'\n )\n}",generatePluginArray:"(utils, fns) => pluginsData => {\n return fns.generateMagicArray(utils, fns)(\n pluginsData,\n PluginArray.prototype,\n Plugin.prototype,\n 'name'\n )\n}",generateMagicArray:"(utils, fns) =>\n function(\n dataArray = [],\n proto = MimeTypeArray.prototype,\n itemProto = MimeType.prototype,\n itemMainProp = 'type'\n ) {\n // Quick helper to set props with the same descriptors vanilla is using\n const defineProp = (obj, prop, value) =>\n Object.defineProperty(obj, prop, {\n value,\n writable: false,\n enumerable: false, // Important for mimeTypes & plugins: ` + "`" + `JSON.stringify(navigator.mimeTypes)` + "`" + `\n configurable: true\n })\n\n // Loop over our fake data and construct items\n const makeItem = data => {\n const item = {}\n for (const prop of Object.keys(data)) {\n if (prop.startsWith('__')) {\n continue\n }\n defineProp(item, prop, data[prop])\n }\n return patchItem(item, data)\n }\n\n const patchItem = (item, data) => {\n let descriptor = Object.getOwnPropertyDescriptors(item)\n\n // Special case: Plugins have a magic length property which is not enumerable\n // e.g. ` + "`" + `navigator.plugins[i].length` + "`" + ` should always be the length of the assigned mimeTypes\n if (itemProto === Plugin.prototype) {\n descriptor = {\n ...descriptor,\n length: {\n value: data.__mimeTypes.length,\n writable: false,\n enumerable: false,\n configurable: true // Important to be able to use the ownKeys trap in a Proxy to strip ` + "`" + `length` + "`" + `\n }\n }\n }\n\n // We need to spoof a specific ` + "`" + `MimeType` + "`" + ` or ` + "`" + `Plugin` + "`" + ` object\n const obj = Object.create(itemProto, descriptor)\n\n // Virtually all property keys are not enumerable in vanilla\n const blacklist = [...Object.keys(data), 'length', 'enabledPlugin']\n return new Proxy(obj, {\n ownKeys(target) {\n return Reflect.ownKeys(target).filter(k => !blacklist.includes(k))\n },\n getOwnPropertyDescriptor(target, prop) {\n if (blacklist.includes(prop)) {\n return undefined\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n }\n })\n }\n\n const magicArray = []\n\n // Loop through our fake data and use that to create convincing entities\n dataArray.forEach(data => {\n magicArray.push(makeItem(data))\n })\n\n // Add direct property access based on types (e.g. ` + "`" + `obj['application/pdf']` + "`" + `) afterwards\n magicArray.forEach(entry => {\n defineProp(magicArray, entry[itemMainProp], entry)\n })\n\n // This is the best way to fake the type to make sure this is false: ` + "`" + `Array.isArray(navigator.mimeTypes)` + "`" + `\n const magicArrayObj = Object.create(proto, {\n ...Object.getOwnPropertyDescriptors(magicArray),\n\n // There's one ugly quirk we unfortunately need to take care of:\n // The ` + "`" + `MimeTypeArray` + "`" + ` prototype has an enumerable ` + "`" + `length` + "`" + ` property,\n // but headful Chrome will still skip it when running ` + "`" + `Object.getOwnPropertyNames(navigator.mimeTypes)` + "`" + `.\n // To strip it we need to make it first ` + "`" + `configurable` + "`" + ` and can then overlay a Proxy with an ` + "`" + `ownKeys` + "`" + ` trap.\n length: {\n value: magicArray.length,\n writable: false,\n enumerable: false,\n configurable: true // Important to be able to use the ownKeys trap in a Proxy to strip ` + "`" + `length` + "`" + `\n }\n })\n\n // Generate our functional function mocks :-)\n const functionMocks = fns.generateFunctionMocks(utils)(\n proto,\n itemMainProp,\n magicArray\n )\n\n // We need to overlay our custom object with a JS Proxy\n const magicArrayObjProxy = new Proxy(magicArrayObj, {\n get(target, key = '') {\n // Redirect function calls to our custom proxied versions mocking the vanilla behavior\n if (key === 'item') {\n return functionMocks.item\n }\n if (key === 'namedItem') {\n return functionMocks.namedItem\n }\n if (proto === PluginArray.prototype && key === 'refresh') {\n return functionMocks.refresh\n }\n // Everything else can pass through as normal\n return utils.cache.Reflect.get(...arguments)\n },\n ownKeys(target) {\n // There are a couple of quirks where the original property demonstrates \"magical\" behavior that makes no sense\n // This can be witnessed when calling ` + "`" + `Object.getOwnPropertyNames(navigator.mimeTypes)` + "`" + ` and the absence of ` + "`" + `length` + "`" + `\n // My guess is that it has to do with the recent change of not allowing data enumeration and this being implemented weirdly\n // For that reason we just completely fake the available property names based on our data to match what regular Chrome is doing\n // Specific issues when not patching this: ` + "`" + `length` + "`" + ` property is available, direct ` + "`" + `types` + "`" + ` props (e.g. ` + "`" + `obj['application/pdf']` + "`" + `) are missing\n const keys = []\n const typeProps = magicArray.map(mt => mt[itemMainProp])\n typeProps.forEach((_, i) => keys.push(` + "`" + `${i}` + "`" + `))\n typeProps.forEach(propName => keys.push(propName))\n return keys\n },\n getOwnPropertyDescriptor(target, prop) {\n if (prop === 'length') {\n return undefined\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n }\n })\n\n return magicArrayObjProxy\n }",generateFunctionMocks:"utils => (\n proto,\n itemMainProp,\n dataArray\n) => ({\n /** Returns the MimeType object with the specified index. */\n item: utils.createProxy(proto.item, {\n apply(target, ctx, args) {\n if (!args.length) {\n throw new TypeError(\n ` + "`" + `Failed to execute 'item' on '${\n proto[Symbol.toStringTag]\n }': 1 argument required, but only 0 present.` + "`" + `\n )\n }\n // Special behavior alert:\n // - Vanilla tries to cast strings to Numbers (only integers!) and use them as property index lookup\n // - If anything else than an integer (including as string) is provided it will return the first entry\n const isInteger = args[0] && Number.isInteger(Number(args[0])) // Cast potential string to number first, then check for integer\n // Note: Vanilla never returns ` + "`" + `undefined` + "`" + `\n return (isInteger ? dataArray[Number(args[0])] : dataArray[0]) || null\n }\n }),\n /** Returns the MimeType object with the specified name. */\n namedItem: utils.createProxy(proto.namedItem, {\n apply(target, ctx, args) {\n if (!args.length) {\n throw new TypeError(\n ` + "`" + `Failed to execute 'namedItem' on '${\n proto[Symbol.toStringTag]\n }': 1 argument required, but only 0 present.` + "`" + `\n )\n }\n return dataArray.find(mt => mt[itemMainProp] === args[0]) || null // Not ` + "`" + `undefined` + "`" + `!\n }\n }),\n /** Does nothing and shall return nothing */\n refresh: proto.refresh\n ? utils.createProxy(proto.refresh, {\n apply(target, ctx, args) {\n return undefined\n }\n })\n : undefined\n})"},data:{mimeTypes:[{type:"application/pdf",suffixes:"pdf",description:"",__pluginName:"Chrome PDF Viewer"},{type:"application/x-google-chrome-pdf",suffixes:"pdf",description:"Portable Document Format",__pluginName:"Chrome PDF Plugin"},{type:"application/x-nacl",suffixes:"",description:"Native Client Executable",__pluginName:"Native Client"},{type:"application/x-pnacl",suffixes:"",description:"Portable Native Client Executable",__pluginName:"Native Client"}],plugins:[{name:"Chrome PDF Plugin",filename:"internal-pdf-viewer",description:"Portable Document Format",__mimeTypes:["application/x-google-chrome-pdf"]},{name:"Chrome PDF Viewer",filename:"mhjfbmdgcfjbbpaeojofohoefgiehjai",description:"",__mimeTypes:["application/pdf"]},{name:"Native Client",filename:"internal-nacl-plugin",description:"",__mimeTypes:["application/x-nacl","application/x-pnacl"]}]}}]}),!1===navigator.webdriver||void 0===navigator.webdriver||delete Object.getPrototypeOf(navigator).webdriver,(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, opts) => {\n const getParameterProxyHandler = {\n apply: function(target, ctx, args) {\n const param = (args || [])[0]\n const result = utils.cache.Reflect.apply(target, ctx, args)\n // UNMASKED_VENDOR_WEBGL\n if (param === 37445) {\n return opts.vendor || 'Intel Inc.' // default in headless: Google Inc.\n }\n // UNMASKED_RENDERER_WEBGL\n if (param === 37446) {\n return opts.renderer || 'Intel Iris OpenGL Engine' // default in headless: Google SwiftShader\n }\n return result\n }\n }\n\n // There's more than one WebGL rendering context\n // https://developer.mozilla.org/en-US/docs/Web/API/WebGL2RenderingContext#Browser_compatibility\n // To find out the original values here: Object.getOwnPropertyDescriptors(WebGLRenderingContext.prototype.getParameter)\n const addProxy = (obj, propName) => {\n utils.replaceWithProxy(obj, propName, getParameterProxyHandler)\n }\n // For whatever weird reason loops don't play nice with Object.defineProperty, here's the next best thing:\n addProxy(WebGLRenderingContext.prototype, 'getParameter')\n addProxy(WebGL2RenderingContext.prototype, 'getParameter')\n }",_args:[{}]}),(()=>{try{if(window.outerWidth&&window.outerHeight)return;const n=85;window.outerWidth=window.innerWidth,window.outerHeight=window.innerHeight+n}catch(n){}})(),(({_utilsFns:_utilsFns,_mainFunction:_mainFunction,_args:_args})=>{const utils=Object.fromEntries(Object.entries(_utilsFns).map((([key,value])=>[key,eval(value)])));utils.init(),eval(_mainFunction)(utils,..._args)})({_utilsFns:{init:"() => {\n utils.preloadCache()\n}",stripProxyFromErrors:"(handler = {}) => {\n const newHandler = {\n setPrototypeOf: function (target, proto) {\n if (proto === null)\n throw new TypeError('Cannot convert object to primitive value')\n if (Object.getPrototypeOf(target) === Object.getPrototypeOf(proto)) {\n throw new TypeError('Cyclic __proto__ value')\n }\n return Reflect.setPrototypeOf(target, proto)\n }\n }\n // We wrap each trap in the handler in a try/catch and modify the error stack if they throw\n const traps = Object.getOwnPropertyNames(handler)\n traps.forEach(trap => {\n newHandler[trap] = function () {\n try {\n // Forward the call to the defined proxy handler\n return handler[trap].apply(this, arguments || [])\n } catch (err) {\n // Stack traces differ per browser, we only support chromium based ones currently\n if (!err || !err.stack || !err.stack.includes(` + "`" + `at ` + "`" + `)) {\n throw err\n }\n\n // When something throws within one of our traps the Proxy will show up in error stacks\n // An earlier implementation of this code would simply strip lines with a blacklist,\n // but it makes sense to be more surgical here and only remove lines related to our Proxy.\n // We try to use a known \"anchor\" line for that and strip it with everything above it.\n // If the anchor line cannot be found for some reason we fall back to our blacklist approach.\n\n const stripWithBlacklist = (stack, stripFirstLine = true) => {\n const blacklist = [\n ` + "`" + `at Reflect.${trap} ` + "`" + `, // e.g. Reflect.get or Reflect.apply\n ` + "`" + `at Object.${trap} ` + "`" + `, // e.g. Object.get or Object.apply\n ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // caused by this very wrapper :-)\n ]\n return (\n err.stack\n .split('\\n')\n // Always remove the first (file) line in the stack (guaranteed to be our proxy)\n .filter((line, index) => !(index === 1 && stripFirstLine))\n // Check if the line starts with one of our blacklisted strings\n .filter(line => !blacklist.some(bl => line.trim().startsWith(bl)))\n .join('\\n')\n )\n }\n\n const stripWithAnchor = (stack, anchor) => {\n const stackArr = stack.split('\\n')\n anchor = anchor || ` + "`" + `at Object.newHandler.<computed> [as ${trap}] ` + "`" + ` // Known first Proxy line in chromium\n const anchorIndex = stackArr.findIndex(line =>\n line.trim().startsWith(anchor)\n )\n if (anchorIndex === -1) {\n return false // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n return stackArr.join('\\n')\n }\n\n // Special cases due to our nested toString proxies\n err.stack = err.stack.replace(\n 'at Object.toString (',\n 'at Function.toString ('\n )\n if ((err.stack || '').includes('at Function.toString (')) {\n err.stack = stripWithBlacklist(err.stack, false)\n throw err\n }\n\n // Try using the anchor method, fallback to blacklist if necessary\n err.stack = stripWithAnchor(err.stack) || stripWithBlacklist(err.stack)\n\n throw err // Re-throw our now sanitized error\n }\n }\n })\n return newHandler\n}",stripErrorWithAnchor:"(err, anchor) => {\n const stackArr = err.stack.split('\\n')\n const anchorIndex = stackArr.findIndex(line => line.trim().startsWith(anchor))\n if (anchorIndex === -1) {\n return err // 404, anchor not found\n }\n // Strip everything from the top until we reach the anchor line (remove anchor line as well)\n // Note: We're keeping the 1st line (zero index) as it's unrelated (e.g. ` + "`" + `TypeError` + "`" + `)\n stackArr.splice(1, anchorIndex)\n err.stack = stackArr.join('\\n')\n return err\n}",replaceProperty:"(obj, propName, descriptorOverrides = {}) => {\n return Object.defineProperty(obj, propName, {\n // Copy over the existing descriptors (writable, enumerable, configurable, etc)\n ...(Object.getOwnPropertyDescriptor(obj, propName) || {}),\n // Add our overrides (e.g. value, get())\n ...descriptorOverrides\n })\n}",preloadCache:"() => {\n if (utils.cache) {\n return\n }\n utils.cache = {\n // Used in our proxies\n Reflect: {\n get: Reflect.get.bind(Reflect),\n apply: Reflect.apply.bind(Reflect)\n },\n // Used in ` + "`" + `makeNativeString` + "`" + `\n nativeToStringStr: Function.toString + '' // => ` + "`" + `function toString() { [native code] }` + "`" + `\n }\n}",makeNativeString:"(name = '') => {\n return utils.cache.nativeToStringStr.replace('toString', name || '')\n}",patchToString:"(obj, str = '') => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === obj) {\n // We either return the optional string verbatim or derive the most desired result automatically\n return str || utils.makeNativeString(obj.name)\n }\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",patchToStringNested:"(obj = {}) => {\n return utils.execRecursively(obj, ['function'], utils.patchToString)\n}",redirectToString:"(proxyObj, originalObj) => {\n const handler = {\n apply: function (target, ctx) {\n // This fixes e.g. ` + "`" + `HTMLMediaElement.prototype.canPlayType.toString + \"\"` + "`" + `\n if (ctx === Function.prototype.toString) {\n return utils.makeNativeString('toString')\n }\n\n // ` + "`" + `toString` + "`" + ` targeted at our proxied Object detected\n if (ctx === proxyObj) {\n const fallback = () =>\n originalObj && originalObj.name\n ? utils.makeNativeString(originalObj.name)\n : utils.makeNativeString(proxyObj.name)\n\n // Return the toString representation of our original object if possible\n return originalObj + '' || fallback()\n }\n\n if (typeof ctx === 'undefined' || ctx === null) {\n return target.call(ctx)\n }\n\n // Check if the toString protype of the context is the same as the global prototype,\n // if not indicates that we are doing a check across different windows., e.g. the iframeWithdirect` + "`" + ` test case\n const hasSameProto = Object.getPrototypeOf(\n Function.prototype.toString\n ).isPrototypeOf(ctx.toString) // eslint-disable-line no-prototype-builtins\n if (!hasSameProto) {\n // Pass the call on to the local Function.prototype.toString instead\n return ctx.toString()\n }\n\n return target.call(ctx)\n }\n }\n\n const toStringProxy = new Proxy(\n Function.prototype.toString,\n utils.stripProxyFromErrors(handler)\n )\n utils.replaceProperty(Function.prototype, 'toString', {\n value: toStringProxy\n })\n}",replaceWithProxy:"(obj, propName, handler) => {\n const originalObj = obj[propName]\n const proxyObj = new Proxy(obj[propName], utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.redirectToString(proxyObj, originalObj)\n\n return true\n}",replaceGetterWithProxy:"(obj, propName, handler) => {\n const fn = Object.getOwnPropertyDescriptor(obj, propName).get\n const fnStr = fn.toString() // special getter function string\n const proxyObj = new Proxy(fn, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { get: proxyObj })\n utils.patchToString(proxyObj, fnStr)\n\n return true\n}",replaceGetterSetter:"(obj, propName, handlerGetterSetter) => {\n const ownPropertyDescriptor = Object.getOwnPropertyDescriptor(obj, propName)\n const handler = { ...ownPropertyDescriptor }\n\n if (handlerGetterSetter.get !== undefined) {\n const nativeFn = ownPropertyDescriptor.get\n handler.get = function() {\n return handlerGetterSetter.get.call(this, nativeFn.bind(this))\n }\n utils.redirectToString(handler.get, nativeFn)\n }\n\n if (handlerGetterSetter.set !== undefined) {\n const nativeFn = ownPropertyDescriptor.set\n handler.set = function(newValue) {\n handlerGetterSetter.set.call(this, newValue, nativeFn.bind(this))\n }\n utils.redirectToString(handler.set, nativeFn)\n }\n\n Object.defineProperty(obj, propName, handler)\n}",mockWithProxy:"(obj, propName, pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n\n utils.replaceProperty(obj, propName, { value: proxyObj })\n utils.patchToString(proxyObj)\n\n return true\n}",createProxy:"(pseudoTarget, handler) => {\n const proxyObj = new Proxy(pseudoTarget, utils.stripProxyFromErrors(handler))\n utils.patchToString(proxyObj)\n\n return proxyObj\n}",splitObjPath:"objPath => ({\n // Remove last dot entry (property) ==> ` + "`" + `HTMLMediaElement.prototype` + "`" + `\n objName: objPath.split('.').slice(0, -1).join('.'),\n // Extract last dot entry ==> ` + "`" + `canPlayType` + "`" + `\n propName: objPath.split('.').slice(-1)[0]\n})",replaceObjPathWithProxy:"(objPath, handler) => {\n const { objName, propName } = utils.splitObjPath(objPath)\n const obj = eval(objName) // eslint-disable-line no-eval\n return utils.replaceWithProxy(obj, propName, handler)\n}",execRecursively:"(obj = {}, typeFilter = [], fn) => {\n function recurse(obj) {\n for (const key in obj) {\n if (obj[key] === undefined) {\n continue\n }\n if (obj[key] && typeof obj[key] === 'object') {\n recurse(obj[key])\n } else {\n if (obj[key] && typeFilter.includes(typeof obj[key])) {\n fn.call(this, obj[key])\n }\n }\n }\n }\n recurse(obj)\n return obj\n}",stringifyFns:"(fnObj = { hello: () => 'world' }) => {\n // Object.fromEntries() ponyfill (in 6 lines) - supported only in Node v12+, modern browsers are fine\n // https://github.com/feross/fromentries\n function fromEntries(iterable) {\n return [...iterable].reduce((obj, [key, val]) => {\n obj[key] = val\n return obj\n }, {})\n }\n return (Object.fromEntries || fromEntries)(\n Object.entries(fnObj)\n .filter(([key, value]) => typeof value === 'function')\n .map(([key, value]) => [key, value.toString()]) // eslint-disable-line no-eval\n )\n}",materializeFns:"(fnStrObj = { hello: \"() => 'world'\" }) => {\n return Object.fromEntries(\n Object.entries(fnStrObj).map(([key, value]) => {\n if (value.startsWith('function')) {\n // some trickery is needed to make oldschool functions work :-)\n return [key, eval(` + "`" + `() => ${value}` + "`" + `)()] // eslint-disable-line no-eval\n } else {\n // arrow functions just work\n return [key, eval(value)] // eslint-disable-line no-eval\n }\n })\n )\n}",makeHandler:"() => ({\n // Used by simple ` + "`" + `navigator` + "`" + ` getter evasions\n getterValue: value => ({\n apply(target, ctx, args) {\n // Let's fetch the value first, to trigger and escalate potential errors\n // Illegal invocations like ` + "`" + `navigator.__proto__.vendor` + "`" + ` will throw here\n utils.cache.Reflect.apply(...arguments)\n return value\n }\n })\n})",arrayEquals:"(array1, array2) => {\n if (array1.length !== array2.length) {\n return false\n }\n for (let i = 0; i < array1.length; ++i) {\n if (array1[i] !== array2[i]) {\n return false\n }\n }\n return true\n}",memoize:"fn => {\n const cache = []\n return function(...args) {\n if (!cache.some(c => utils.arrayEquals(c.key, args))) {\n cache.push({ key: args, value: fn.apply(this, args) })\n }\n return cache.find(c => utils.arrayEquals(c.key, args)).value\n }\n}"},_mainFunction:"(utils, opts) => {\n try {\n // Adds a contentWindow proxy to the provided iframe element\n const addContentWindowProxy = iframe => {\n const contentWindowProxy = {\n get(target, key) {\n // Now to the interesting part:\n // We actually make this thing behave like a regular iframe window,\n // by intercepting calls to e.g. ` + "`" + `.self` + "`" + ` and redirect it to the correct thing. :)\n // That makes it possible for these assertions to be correct:\n // iframe.contentWindow.self === window.top // must be false\n if (key === 'self') {\n return this\n }\n // iframe.contentWindow.frameElement === iframe // must be true\n if (key === 'frameElement') {\n return iframe\n }\n // Intercept iframe.contentWindow[0] to hide the property 0 added by the proxy.\n if (key === '0') {\n return undefined\n }\n return Reflect.get(target, key)\n }\n }\n\n if (!iframe.contentWindow) {\n const proxy = new Proxy(window, contentWindowProxy)\n Object.defineProperty(iframe, 'contentWindow', {\n get() {\n return proxy\n },\n set(newValue) {\n return newValue // contentWindow is immutable\n },\n enumerable: true,\n configurable: false\n })\n }\n }\n\n // Handles iframe element creation, augments ` + "`" + `srcdoc` + "`" + ` property so we can intercept further\n const handleIframeCreation = (target, thisArg, args) => {\n const iframe = target.apply(thisArg, args)\n\n // We need to keep the originals around\n const _iframe = iframe\n const _srcdoc = _iframe.srcdoc\n\n // Add hook for the srcdoc property\n // We need to be very surgical here to not break other iframes by accident\n Object.defineProperty(iframe, 'srcdoc', {\n configurable: true, // Important, so we can reset this later\n get: function() {\n return _srcdoc\n },\n set: function(newValue) {\n addContentWindowProxy(this)\n // Reset property, the hook is only needed once\n Object.defineProperty(iframe, 'srcdoc', {\n configurable: false,\n writable: false,\n value: _srcdoc\n })\n _iframe.srcdoc = newValue\n }\n })\n return iframe\n }\n\n // Adds a hook to intercept iframe creation events\n const addIframeCreationSniffer = () => {\n /* global document */\n const createElementHandler = {\n // Make toString() native\n get(target, key) {\n return Reflect.get(target, key)\n },\n apply: function(target, thisArg, args) {\n const isIframe =\n args && args.length && ` + "`" + `${args[0]}` + "`" + `.toLowerCase() === 'iframe'\n if (!isIframe) {\n // Everything as usual\n return target.apply(thisArg, args)\n } else {\n return handleIframeCreation(target, thisArg, args)\n }\n }\n }\n // All this just due to iframes with srcdoc bug\n utils.replaceWithProxy(\n document,\n 'createElement',\n createElementHandler\n )\n }\n\n // Let's go\n addIframeCreationSniffer()\n } catch (err) {\n // console.warn(err)\n }\n }",_args:[]}); })();` ================================================ FILE: pkg/engine/headless/captcha/capsolver/capsolver.go ================================================ package capsolver import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" "github.com/projectdiscovery/katana/pkg/engine/headless/captcha" ) const ( defaultPollInterval = 3 * time.Second defaultTimeout = 120 * time.Second ) var baseURL = "https://api.capsolver.com" func SetBaseURL(url string) { baseURL = url } var taskTypes = map[captcha.Provider]string{ captcha.ProviderRecaptchaV2: "ReCaptchaV2TaskProxyLess", captcha.ProviderRecaptchaV3: "ReCaptchaV3TaskProxyLess", captcha.ProviderRecaptchaV2Enterprise: "ReCaptchaV2EnterpriseTaskProxyLess", captcha.ProviderRecaptchaV3Enterprise: "ReCaptchaV3EnterpriseTaskProxyLess", captcha.ProviderTurnstile: "AntiTurnstileTaskProxyLess", captcha.ProviderHCaptcha: "HCaptchaTaskProxyLess", } type Solver struct { apiKey string client *http.Client } func init() { captcha.RegisterSolver("capsolver", func(apiKey string) (captcha.Solver, error) { return New(apiKey), nil }) } func New(apiKey string) *Solver { return &Solver{ apiKey: apiKey, client: &http.Client{Timeout: 30 * time.Second}, } } func (s *Solver) Solve(ctx context.Context, info *captcha.Info) (*captcha.Solution, error) { taskType, ok := taskTypes[info.Provider] if !ok { return nil, fmt.Errorf("unsupported captcha provider for capsolver: %s", info.Provider) } task := map[string]any{ "type": taskType, "websiteURL": info.PageURL, "websiteKey": info.SiteKey, } if (info.Provider == captcha.ProviderRecaptchaV3 || info.Provider == captcha.ProviderRecaptchaV3Enterprise) && info.Action != "" { task["pageAction"] = info.Action } taskID, err := s.createTask(ctx, task) if err != nil { return nil, fmt.Errorf("create task: %w", err) } return s.pollResult(ctx, taskID, info.Provider) } type createTaskRequest struct { ClientKey string `json:"clientKey"` Task map[string]any `json:"task"` } type createTaskResponse struct { ErrorID int `json:"errorId"` ErrorCode string `json:"errorCode"` ErrorDescription string `json:"errorDescription"` TaskID string `json:"taskId"` } type getTaskResultRequest struct { ClientKey string `json:"clientKey"` TaskID string `json:"taskId"` } type getTaskResultResponse struct { ErrorID int `json:"errorId"` ErrorCode string `json:"errorCode"` ErrorDescription string `json:"errorDescription"` Status string `json:"status"` Solution map[string]any `json:"solution"` } func (s *Solver) createTask(ctx context.Context, task map[string]any) (string, error) { body, err := json.Marshal(createTaskRequest{ ClientKey: s.apiKey, Task: task, }) if err != nil { return "", err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+"/createTask", bytes.NewReader(body)) if err != nil { return "", err } req.Header.Set("Content-Type", "application/json") resp, err := s.client.Do(req) if err != nil { return "", err } defer func() { _ = resp.Body.Close() }() respBody, err := io.ReadAll(resp.Body) if err != nil { return "", err } var result createTaskResponse if err := json.Unmarshal(respBody, &result); err != nil { return "", err } if result.ErrorID != 0 { return "", fmt.Errorf("capsolver error %s: %s", result.ErrorCode, result.ErrorDescription) } return result.TaskID, nil } func (s *Solver) pollResult(ctx context.Context, taskID string, provider captcha.Provider) (*captcha.Solution, error) { ctx, cancel := context.WithTimeout(ctx, defaultTimeout) defer cancel() ticker := time.NewTicker(defaultPollInterval) defer ticker.Stop() for { select { case <-ctx.Done(): return nil, fmt.Errorf("captcha solve timed out after %s", defaultTimeout) case <-ticker.C: result, err := s.getTaskResult(ctx, taskID) if err != nil { return nil, err } if result.ErrorID != 0 { return nil, fmt.Errorf("capsolver error %s: %s", result.ErrorCode, result.ErrorDescription) } if result.Status != "ready" { continue } return extractToken(result.Solution, provider) } } } func (s *Solver) getTaskResult(ctx context.Context, taskID string) (*getTaskResultResponse, error) { body, err := json.Marshal(getTaskResultRequest{ ClientKey: s.apiKey, TaskID: taskID, }) if err != nil { return nil, err } req, err := http.NewRequestWithContext(ctx, http.MethodPost, baseURL+"/getTaskResult", bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") resp, err := s.client.Do(req) if err != nil { return nil, err } defer func() { _ = resp.Body.Close() }() respBody, err := io.ReadAll(resp.Body) if err != nil { return nil, err } var result getTaskResultResponse if err := json.Unmarshal(respBody, &result); err != nil { return nil, err } return &result, nil } func extractToken(solution map[string]any, provider captcha.Provider) (*captcha.Solution, error) { // capsolver returns turnstile tokens under "token", everything else under "gRecaptchaResponse" key := "gRecaptchaResponse" if provider == captcha.ProviderTurnstile { key = "token" } token, ok := solution[key].(string) if !ok || token == "" { return nil, fmt.Errorf("no token found in capsolver solution (key=%s)", key) } return &captcha.Solution{Token: token, Provider: provider}, nil } ================================================ FILE: pkg/engine/headless/captcha/capsolver/capsolver_test.go ================================================ package capsolver import ( "context" "encoding/json" "net/http" "net/http/httptest" "sync/atomic" "testing" "github.com/projectdiscovery/katana/pkg/engine/headless/captcha" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestSolve(t *testing.T) { var pollCount atomic.Int32 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var body map[string]any _ = json.NewDecoder(r.Body).Decode(&body) switch r.URL.Path { case "/createTask": task, ok := body["task"].(map[string]any) assert.True(t, ok) assert.Equal(t, "ReCaptchaV2TaskProxyLess", task["type"]) assert.Equal(t, "https://example.com", task["websiteURL"]) assert.Equal(t, "test-sitekey", task["websiteKey"]) _ = json.NewEncoder(w).Encode(map[string]any{ "errorId": 0, "taskId": "task-123", }) case "/getTaskResult": assert.Equal(t, "task-123", body["taskId"]) count := pollCount.Add(1) if count < 2 { _ = json.NewEncoder(w).Encode(map[string]any{ "errorId": 0, "status": "processing", }) return } _ = json.NewEncoder(w).Encode(map[string]any{ "errorId": 0, "status": "ready", "solution": map[string]any{ "gRecaptchaResponse": "solved-token-abc", }, }) } })) defer server.Close() origURL := baseURL SetBaseURL(server.URL) defer SetBaseURL(origURL) cs := New("test-api-key") solution, err := cs.Solve(context.Background(), &captcha.Info{ Provider: captcha.ProviderRecaptchaV2, SiteKey: "test-sitekey", PageURL: "https://example.com", }) require.NoError(t, err) require.NotNil(t, solution) assert.Equal(t, "solved-token-abc", solution.Token) assert.Equal(t, captcha.ProviderRecaptchaV2, solution.Provider) assert.GreaterOrEqual(t, int(pollCount.Load()), 2) } func TestCreateTaskError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { _ = json.NewEncoder(w).Encode(map[string]any{ "errorId": 1, "errorCode": "ERROR_KEY_DENIED", "errorDescription": "Account not found or blocked", }) })) defer server.Close() origURL := baseURL SetBaseURL(server.URL) defer SetBaseURL(origURL) cs := New("bad-key") _, err := cs.Solve(context.Background(), &captcha.Info{ Provider: captcha.ProviderRecaptchaV2, SiteKey: "key", PageURL: "https://example.com", }) require.Error(t, err) assert.Contains(t, err.Error(), "ERROR_KEY_DENIED") } func TestTurnstileToken(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/createTask": _ = json.NewEncoder(w).Encode(map[string]any{ "errorId": 0, "taskId": "task-turnstile", }) case "/getTaskResult": _ = json.NewEncoder(w).Encode(map[string]any{ "errorId": 0, "status": "ready", "solution": map[string]any{ "token": "turnstile-token-xyz", }, }) } })) defer server.Close() origURL := baseURL SetBaseURL(server.URL) defer SetBaseURL(origURL) cs := New("key") solution, err := cs.Solve(context.Background(), &captcha.Info{ Provider: captcha.ProviderTurnstile, SiteKey: "cf-key", PageURL: "https://example.com", }) require.NoError(t, err) assert.Equal(t, "turnstile-token-xyz", solution.Token) assert.Equal(t, captcha.ProviderTurnstile, solution.Provider) } func TestExtractToken(t *testing.T) { tests := []struct { name string solution map[string]any provider captcha.Provider want string wantErr bool }{ { name: "recaptcha v2", solution: map[string]any{"gRecaptchaResponse": "token-a"}, provider: captcha.ProviderRecaptchaV2, want: "token-a", }, { name: "turnstile", solution: map[string]any{"token": "token-b"}, provider: captcha.ProviderTurnstile, want: "token-b", }, { name: "missing token", solution: map[string]any{}, provider: captcha.ProviderRecaptchaV2, wantErr: true, }, { name: "empty token", solution: map[string]any{"gRecaptchaResponse": ""}, provider: captcha.ProviderHCaptcha, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { sol, err := extractToken(tt.solution, tt.provider) if tt.wantErr { require.Error(t, err) return } require.NoError(t, err) assert.Equal(t, tt.want, sol.Token) }) } } func TestUnsupportedProvider(t *testing.T) { cs := New("key") _, err := cs.Solve(context.Background(), &captcha.Info{ Provider: "unknown", SiteKey: "key", PageURL: "https://example.com", }) require.Error(t, err) assert.Contains(t, err.Error(), "unsupported") } ================================================ FILE: pkg/engine/headless/captcha/captcha.go ================================================ package captcha import ( "context" "fmt" "strings" "github.com/go-rod/rod" ditcaptcha "github.com/happyhackingspace/dit/captcha" "github.com/projectdiscovery/gologger" captchajs "github.com/projectdiscovery/katana/pkg/engine/headless/captcha/js" ) type Handler struct { solver Solver } func NewHandler(solverProvider, apiKey string) (*Handler, error) { solver, err := NewSolver(solverProvider, apiKey) if err != nil { return nil, fmt.Errorf("captcha solver init: %w", err) } return &Handler{ solver: solver, }, nil } func (h *Handler) HandleIfCaptcha(ctx context.Context, page *rod.Page, pageHTML string) (bool, error) { if ditcaptcha.DetectCaptchaInHTML(pageHTML) == ditcaptcha.CaptchaTypeNone && !strings.Contains(pageHTML, "data-sitekey") { return false, nil } info, err := Identify(page) if err != nil { gologger.Debug().Msgf("captcha identification failed: %s", err) } if info == nil { return false, nil } return h.solveCaptcha(ctx, page, info) } func (h *Handler) solveCaptcha(ctx context.Context, page *rod.Page, info *Info) (bool, error) { gologger.Debug().Msgf("captcha detected: provider=%s sitekey=%s url=%s", info.Provider, info.SiteKey, info.PageURL) solution, err := h.solver.Solve(ctx, info) if err != nil { return true, fmt.Errorf("captcha solve: %w", err) } gologger.Debug().Msgf("captcha solved, injecting token: provider=%s", solution.Provider) if err := injectToken(page, solution); err != nil { return true, fmt.Errorf("captcha inject: %w", err) } return true, nil } func injectToken(page *rod.Page, solution *Solution) error { js, err := injectionScript(solution.Provider) if err != nil { return err } _, err = page.Eval(js, solution.Token) return err } func injectionScript(provider Provider) (string, error) { switch provider { case ProviderRecaptchaV2, ProviderRecaptchaV3, ProviderRecaptchaV2Enterprise, ProviderRecaptchaV3Enterprise: return captchajs.InjectRecaptchaJS, nil case ProviderTurnstile: return captchajs.InjectTurnstileJS, nil case ProviderHCaptcha: return captchajs.InjectHCaptchaJS, nil default: return "", fmt.Errorf("unsupported captcha provider for injection: %s", provider) } } ================================================ FILE: pkg/engine/headless/captcha/helpers_test.go ================================================ package captcha import ( "fmt" "net/http" "net/http/httptest" "testing" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" ) func setupBrowser(t *testing.T) *rod.Browser { t.Helper() path, found := launcher.LookPath() if !found { t.Skip("chromium not found, skipping browser tests") } u := launcher.New().Bin(path).Headless(true).Leakless(true).MustLaunch() browser := rod.New().ControlURL(u).MustConnect() t.Cleanup(func() { browser.MustClose() }) return browser } func servePage(t *testing.T, html string) string { t.Helper() server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html") _, _ = fmt.Fprint(w, html) })) t.Cleanup(server.Close) return server.URL } ================================================ FILE: pkg/engine/headless/captcha/identify.go ================================================ package captcha import ( "github.com/go-rod/rod" captchajs "github.com/projectdiscovery/katana/pkg/engine/headless/captcha/js" ) type Provider string const ( ProviderRecaptchaV2 Provider = "recaptchav2" ProviderRecaptchaV3 Provider = "recaptchav3" ProviderRecaptchaV2Enterprise Provider = "recaptchav2enterprise" ProviderRecaptchaV3Enterprise Provider = "recaptchav3enterprise" ProviderTurnstile Provider = "turnstile" ProviderHCaptcha Provider = "hcaptcha" ) type Info struct { Provider Provider SiteKey string PageURL string Action string } func Identify(page *rod.Page) (*Info, error) { pageURL, err := page.Eval("() => window.location.href") if err != nil { return nil, err } result, err := page.Eval(captchajs.IdentifyJS) if err != nil { return nil, err } if result.Value.Nil() { return nil, nil } return &Info{ Provider: Provider(result.Value.Get("provider").Str()), SiteKey: result.Value.Get("sitekey").Str(), PageURL: pageURL.Value.Str(), Action: result.Value.Get("action").Str(), }, nil } ================================================ FILE: pkg/engine/headless/captcha/identify_test.go ================================================ package captcha import ( "testing" ditcaptcha "github.com/happyhackingspace/dit/captcha" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDetectCaptchaWithDit(t *testing.T) { tests := []struct { name string html string want ditcaptcha.CaptchaType }{ { name: "recaptcha", html: `<html><body><div class="g-recaptcha" data-sitekey="6Lc"></div> <script src="https://www.google.com/recaptcha/api.js"></script></body></html>`, want: ditcaptcha.CaptchaTypeRecaptcha, }, { name: "turnstile", html: `<html><body><div class="cf-turnstile" data-sitekey="0x4AAA"></div> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js"></script></body></html>`, want: ditcaptcha.CaptchaTypeTurnstile, }, { name: "hcaptcha", html: `<html><body><div class="h-captcha" data-sitekey="abc"></div> <script src="https://js.hcaptcha.com/1/api.js"></script></body></html>`, want: ditcaptcha.CaptchaTypeHCaptcha, }, { name: "no captcha", html: `<html><body><h1>Hello</h1></body></html>`, want: ditcaptcha.CaptchaTypeNone, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ditcaptcha.DetectCaptchaInHTML(tt.html) assert.Equal(t, tt.want, got) }) } } func TestIdentify(t *testing.T) { browser := setupBrowser(t) tests := []struct { name string html string wantProvider Provider wantSiteKey string wantNil bool }{ { name: "recaptcha v2", html: `<html><body> <div class="g-recaptcha" data-sitekey="6LcXrecapv2"></div> <script src="https://www.google.com/recaptcha/api.js" async defer></script> </body></html>`, wantProvider: ProviderRecaptchaV2, wantSiteKey: "6LcXrecapv2", }, { name: "recaptcha v3", html: `<html><body> <script src="https://www.google.com/recaptcha/api.js?render=6LcXrecapv3"></script> </body></html>`, wantProvider: ProviderRecaptchaV3, wantSiteKey: "6LcXrecapv3", }, { name: "cloudflare turnstile", html: `<html><body> <div class="cf-turnstile" data-sitekey="0x4AAATURNSTILE"></div> <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script> </body></html>`, wantProvider: ProviderTurnstile, wantSiteKey: "0x4AAATURNSTILE", }, { name: "hcaptcha", html: `<html><body> <div class="h-captcha" data-sitekey="hcap-sitekey-123"></div> <script src="https://js.hcaptcha.com/1/api.js" async defer></script> </body></html>`, wantProvider: ProviderHCaptcha, wantSiteKey: "hcap-sitekey-123", }, { name: "no captcha", html: `<html><body> <h1>Hello World</h1> <form><input type="text" name="q"><button>Search</button></form> </body></html>`, wantNil: true, }, { name: "recaptcha v2 enterprise", html: `<html><body> <div class="g-recaptcha" data-sitekey="6LcEntV2"></div> <script src="https://www.google.com/recaptcha/enterprise.js" async defer></script> </body></html>`, wantProvider: ProviderRecaptchaV2Enterprise, wantSiteKey: "6LcEntV2", }, { name: "recaptcha v3 enterprise", html: `<html><body> <script src="https://www.google.com/recaptcha/enterprise.js?render=6LcEntV3"></script> </body></html>`, wantProvider: ProviderRecaptchaV3Enterprise, wantSiteKey: "6LcEntV3", }, { name: "generic data-sitekey fallback", html: `<html><body> <div id="captcha-widget" data-sitekey="generic-key-456"></div> </body></html>`, wantProvider: ProviderRecaptchaV2, wantSiteKey: "generic-key-456", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { url := servePage(t, tt.html) page := browser.MustPage(url) defer page.MustClose() page.MustWaitLoad() info, err := Identify(page) require.NoError(t, err) if tt.wantNil { assert.Nil(t, info) return } require.NotNil(t, info) assert.Equal(t, tt.wantProvider, info.Provider) assert.Equal(t, tt.wantSiteKey, info.SiteKey) assert.Contains(t, info.PageURL, url) }) } } ================================================ FILE: pkg/engine/headless/captcha/inject_test.go ================================================ package captcha import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestInjectionScript(t *testing.T) { tests := []struct { name string provider Provider wantErr bool contains string }{ { name: "recaptcha v2", provider: ProviderRecaptchaV2, contains: "g-recaptcha-response", }, { name: "recaptcha v3", provider: ProviderRecaptchaV3, contains: "g-recaptcha-response", }, { name: "recaptcha v2 enterprise", provider: ProviderRecaptchaV2Enterprise, contains: "g-recaptcha-response", }, { name: "recaptcha v3 enterprise", provider: ProviderRecaptchaV3Enterprise, contains: "g-recaptcha-response", }, { name: "turnstile", provider: ProviderTurnstile, contains: "cf-turnstile-response", }, { name: "hcaptcha", provider: ProviderHCaptcha, contains: "h-captcha-response", }, { name: "unknown provider", provider: "unknown", wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { js, err := injectionScript(tt.provider) if tt.wantErr { require.Error(t, err) return } require.NoError(t, err) assert.Contains(t, js, tt.contains) }) } } ================================================ FILE: pkg/engine/headless/captcha/injection_test.go ================================================ package captcha import ( "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestE2E_RecaptchaV2_Injection(t *testing.T) { browser := setupBrowser(t) received := make(chan string, 1) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { _ = r.ParseForm() received <- r.FormValue("g-recaptcha-response") w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`<html><body><div id="done">OK</div></body></html>`)) return } w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`<html><body> <form method="POST"> <div class="g-recaptcha" data-sitekey="6LcTestKey" data-callback="onSolved"></div> <textarea id="g-recaptcha-response" name="g-recaptcha-response" style="display:none"></textarea> </form> <div id="result"></div> <script>function onSolved(token) { document.getElementById('result').textContent = 'SOLVED:' + token; }</script> </body></html>`)) })) t.Cleanup(server.Close) p := browser.MustPage(server.URL) defer p.MustClose() p.MustWaitLoad() info, err := Identify(p) require.NoError(t, err) require.NotNil(t, info) assert.Equal(t, ProviderRecaptchaV2, info.Provider) assert.Equal(t, "6LcTestKey", info.SiteKey) err = injectToken(p, &Solution{Token: "test-token-abc", Provider: ProviderRecaptchaV2}) require.NoError(t, err) p.MustWaitLoad() token := <-received assert.Equal(t, "test-token-abc", token) } func TestE2E_Turnstile_Injection(t *testing.T) { browser := setupBrowser(t) received := make(chan string, 1) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { _ = r.ParseForm() received <- r.FormValue("cf-turnstile-response") w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`<html><body><div id="done">OK</div></body></html>`)) return } w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`<html><body> <form method="POST"> <div class="cf-turnstile" data-sitekey="0x4AAATurnstileKey" data-callback="onTurnstile"></div> <input type="hidden" name="cf-turnstile-response" value=""> </form> <div id="result"></div> <script>function onTurnstile(token) { document.getElementById('result').textContent = 'TURNSTILE:' + token; }</script> </body></html>`)) })) t.Cleanup(server.Close) p := browser.MustPage(server.URL) defer p.MustClose() p.MustWaitLoad() info, err := Identify(p) require.NoError(t, err) require.NotNil(t, info) assert.Equal(t, ProviderTurnstile, info.Provider) assert.Equal(t, "0x4AAATurnstileKey", info.SiteKey) err = injectToken(p, &Solution{Token: "test-token-def", Provider: ProviderTurnstile}) require.NoError(t, err) p.MustWaitLoad() token := <-received assert.Equal(t, "test-token-def", token) } func TestE2E_HCaptcha_Injection(t *testing.T) { browser := setupBrowser(t) received := make(chan map[string]string, 1) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { _ = r.ParseForm() received <- map[string]string{ "h-captcha-response": r.FormValue("h-captcha-response"), "g-recaptcha-response": r.FormValue("g-recaptcha-response"), } w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`<html><body><div id="done">OK</div></body></html>`)) return } w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`<html><body> <form method="POST"> <div class="h-captcha" data-sitekey="hcap-test-key" data-callback="onHCaptcha"></div> <textarea name="h-captcha-response" style="display:none"></textarea> <textarea name="g-recaptcha-response" style="display:none"></textarea> </form> <div id="result"></div> <script>function onHCaptcha(token) { document.getElementById('result').textContent = 'HCAPTCHA:' + token; }</script> </body></html>`)) })) t.Cleanup(server.Close) p := browser.MustPage(server.URL) defer p.MustClose() p.MustWaitLoad() info, err := Identify(p) require.NoError(t, err) require.NotNil(t, info) assert.Equal(t, ProviderHCaptcha, info.Provider) assert.Equal(t, "hcap-test-key", info.SiteKey) err = injectToken(p, &Solution{Token: "test-token-ghi", Provider: ProviderHCaptcha}) require.NoError(t, err) p.MustWaitLoad() vals := <-received assert.Equal(t, "test-token-ghi", vals["h-captcha-response"]) assert.Equal(t, "test-token-ghi", vals["g-recaptcha-response"]) } func TestE2E_RecaptchaV2_FormSubmitFallback(t *testing.T) { browser := setupBrowser(t) received := make(chan string, 1) server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == "POST" { _ = r.ParseForm() received <- r.FormValue("g-recaptcha-response") _, _ = w.Write([]byte("OK")) return } w.Header().Set("Content-Type", "text/html") _, _ = w.Write([]byte(`<html><body> <form action="` + r.Host + `" method="POST"> <div class="g-recaptcha" data-sitekey="6LcNoCallback"></div> <textarea id="g-recaptcha-response" name="g-recaptcha-response" style="display:none"></textarea> </form> </body></html>`)) })) t.Cleanup(server.Close) p := browser.MustPage(server.URL) defer p.MustClose() p.MustWaitLoad() err := injectToken(p, &Solution{Token: "fallback-token", Provider: ProviderRecaptchaV2}) require.NoError(t, err) p.MustWaitLoad() token := <-received assert.Equal(t, "fallback-token", token) } ================================================ FILE: pkg/engine/headless/captcha/integration_test.go ================================================ package captcha import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestIntegration_DetectRealCaptchaPages(t *testing.T) { if testing.Short() { t.Skip("skipping integration test in short mode") } browser := setupBrowser(t) tests := []struct { name string url string wantProvider Provider }{ { name: "google recaptcha v2 demo", url: "https://www.google.com/recaptcha/api2/demo", wantProvider: ProviderRecaptchaV2, }, { name: "hcaptcha official demo", url: "https://accounts.hcaptcha.com/demo", wantProvider: ProviderHCaptcha, }, { name: "2captcha turnstile demo", url: "https://2captcha.com/demo/cloudflare-turnstile", wantProvider: ProviderTurnstile, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { page := browser.MustPage(tt.url) defer page.MustClose() page.MustWaitLoad() info, err := Identify(page) require.NoError(t, err) require.NotNil(t, info, "expected captcha to be detected on %s", tt.url) t.Logf("detected: provider=%s sitekey=%s url=%s", info.Provider, info.SiteKey, info.PageURL) assert.Equal(t, tt.wantProvider, info.Provider) assert.NotEmpty(t, info.SiteKey) }) } } ================================================ FILE: pkg/engine/headless/captcha/js/identify.js ================================================ () => { const hcap = document.querySelector('.h-captcha[data-sitekey]'); if (hcap) { return { provider: "hcaptcha", sitekey: hcap.getAttribute("data-sitekey"), action: "" }; } const cf = document.querySelector('.cf-turnstile[data-sitekey]'); if (cf) { return { provider: "turnstile", sitekey: cf.getAttribute("data-sitekey"), action: "" }; } const recapScripts = document.querySelectorAll( 'script[src*="recaptcha/api.js"], script[src*="recaptcha/enterprise.js"]' ); for (const s of recapScripts) { try { const u = new URL(s.src); const isEnterprise = u.pathname.includes("/enterprise.js"); const renderParam = u.searchParams.get("render"); if (renderParam && renderParam !== "explicit") { return { provider: isEnterprise ? "recaptchav3enterprise" : "recaptchav3", sitekey: renderParam, action: "" }; } } catch {} } const isEnterprise = recapScripts.length > 0 && Array.from(recapScripts).some(s => s.src.includes("/enterprise.js")); const recap = document.querySelector('.g-recaptcha[data-sitekey]'); if (recap) { return { provider: isEnterprise ? "recaptchav2enterprise" : "recaptchav2", sitekey: recap.getAttribute("data-sitekey"), action: "" }; } const generic = document.querySelector('[data-sitekey]'); if (generic) { return { provider: isEnterprise ? "recaptchav2enterprise" : "recaptchav2", sitekey: generic.getAttribute("data-sitekey"), action: "" }; } return null; } ================================================ FILE: pkg/engine/headless/captcha/js/inject-hcaptcha.js ================================================ (token) => { document.querySelectorAll('textarea[name="h-captcha-response"]').forEach(el => { el.value = token; }); document.querySelectorAll('textarea[name="g-recaptcha-response"]').forEach(el => { el.value = token; }); let called = false; if (!called) { const el = document.querySelector('.h-captcha[data-callback]'); if (el) { const name = el.getAttribute('data-callback'); if (name && typeof window[name] === 'function') { window[name](token); called = true; } } } // callback alone may not trigger navigation const form = document.querySelector('form:has(.h-captcha)') || document.querySelector('form:has([name="h-captcha-response"])'); if (form) form.submit(); } ================================================ FILE: pkg/engine/headless/captcha/js/inject-recaptcha.js ================================================ (token) => { document.querySelectorAll('[id="g-recaptcha-response"], [name="g-recaptcha-response"]').forEach(el => { el.value = token; el.style.display = 'block'; }); let called = false; if (!called) { const el = document.querySelector('.g-recaptcha[data-callback]'); if (el) { const name = el.getAttribute('data-callback'); if (name && typeof window[name] === 'function') { window[name](token); called = true; } } } // ___grecaptcha_cfg.clients holds internal reCAPTCHA state including registered callbacks if (!called && typeof ___grecaptcha_cfg !== 'undefined' && ___grecaptcha_cfg.clients) { try { for (const key in ___grecaptcha_cfg.clients) { const client = ___grecaptcha_cfg.clients[key]; const find = (obj, depth) => { if (depth > 4 || !obj || typeof obj !== 'object') return null; if (obj instanceof Node || obj instanceof Window) return null; try { for (const k in obj) { try { const v = obj[k]; if (k === 'callback' && typeof v === 'function') return v; if (v && typeof v === 'object' && !(v instanceof Node) && !(v instanceof Window)) { if (typeof v.callback === 'function') return v.callback; const found = find(v, depth + 1); if (found) return found; } } catch(e) { continue; } } } catch(e) { return null; } return null; }; const cb = find(client, 0); if (cb) { cb(token); called = true; break; } } } catch(e) {} } // callback alone may not trigger navigation const form = document.querySelector('form:has(#g-recaptcha-response)') || document.querySelector('form:has(.g-recaptcha)'); if (form) form.submit(); } ================================================ FILE: pkg/engine/headless/captcha/js/inject-turnstile.js ================================================ (token) => { document.querySelectorAll('input[name="cf-turnstile-response"]').forEach(el => { el.value = token; }); let called = false; if (!called) { const el = document.querySelector('.cf-turnstile[data-callback]'); if (el) { const name = el.getAttribute('data-callback'); if (name && typeof window[name] === 'function') { window[name](token); called = true; } } } // callback alone may not trigger navigation const form = document.querySelector('form:has(.cf-turnstile)') || document.querySelector('form:has([name="cf-turnstile-response"])'); if (form) form.submit(); } ================================================ FILE: pkg/engine/headless/captcha/js/js.go ================================================ package js import _ "embed" var ( //go:embed identify.js IdentifyJS string //go:embed inject-recaptcha.js InjectRecaptchaJS string //go:embed inject-turnstile.js InjectTurnstileJS string //go:embed inject-hcaptcha.js InjectHCaptchaJS string ) ================================================ FILE: pkg/engine/headless/captcha/solver.go ================================================ package captcha import ( "context" "fmt" ) type Solution struct { Token string Provider Provider } type Solver interface { Solve(ctx context.Context, info *Info) (*Solution, error) } func NewSolver(provider, apiKey string) (Solver, error) { constructor, ok := solverRegistry[provider] if !ok { return nil, fmt.Errorf("unsupported captcha solver provider: %s", provider) } return constructor(apiKey) } type SolverConstructor func(apiKey string) (Solver, error) var solverRegistry = map[string]SolverConstructor{} func RegisterSolver(name string, constructor SolverConstructor) { solverRegistry[name] = constructor } ================================================ FILE: pkg/engine/headless/captcha/solver_test.go ================================================ package captcha import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNewSolver_UnsupportedProvider(t *testing.T) { _, err := NewSolver("unknown-provider", "key") require.Error(t, err) assert.Contains(t, err.Error(), "unsupported") } func TestRegisterSolver(t *testing.T) { RegisterSolver("test-provider", func(apiKey string) (Solver, error) { return nil, nil }) defer delete(solverRegistry, "test-provider") _, ok := solverRegistry["test-provider"] assert.True(t, ok) } ================================================ FILE: pkg/engine/headless/crawler/crawler.go ================================================ package crawler import ( "context" "fmt" "log/slog" "os" "os/user" "path/filepath" "regexp" "sync" "time" "github.com/adrianbrad/queue" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" "github.com/go-rod/rod/lib/utils" "github.com/pkg/errors" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/engine/headless/browser" "github.com/projectdiscovery/katana/pkg/engine/headless/captcha" "github.com/projectdiscovery/katana/pkg/engine/headless/crawler/diagnostics" "github.com/projectdiscovery/katana/pkg/engine/headless/crawler/normalizer" "github.com/projectdiscovery/katana/pkg/engine/headless/crawler/normalizer/simhash" "github.com/projectdiscovery/katana/pkg/engine/headless/graph" "github.com/projectdiscovery/katana/pkg/engine/headless/types" "github.com/projectdiscovery/katana/pkg/output" ) type Crawler struct { logger *slog.Logger launcher *browser.Launcher options Options crawlQueue queue.Queue[*types.Action] crawlGraph *graph.CrawlGraph simhashOracle *simhash.Oracle uniqueActions map[string]struct{} diagnostics diagnostics.Writer } type Options struct { ChromiumPath string MaxBrowsers int MaxDepth int PageMaxTimeout time.Duration NoSandbox bool ShowBrowser bool SlowMotion bool MaxCrawlDuration time.Duration MaxFailureCount int Trace bool CookieConsentBypass bool AutomaticFormFill bool // EnableDiagnostics enables the diagnostics mode // which writes diagnostic information to a directory // specified by the DiagnosticsDir optionally. EnableDiagnostics bool DiagnosticsDir string Proxy string Logger *slog.Logger ScopeValidator browser.ScopeValidator RequestCallback func(*output.Result) ChromeUser *user.User CaptchaHandler *captcha.Handler } var domNormalizer *normalizer.Normalizer var initOnce sync.Once var initError error func init() { initOnce.Do(func() { var err error domNormalizer, err = normalizer.New() if err != nil { initError = errors.Wrap(err, "failed to create domnormalizer") } }) } func New(opts Options) (*Crawler, error) { if initError != nil { return nil, initError } if opts.Logger == nil { opts.Logger = slog.Default() } launcher, err := browser.NewLauncher(browser.LauncherOptions{ ChromiumPath: opts.ChromiumPath, MaxBrowsers: opts.MaxBrowsers, PageMaxTimeout: opts.PageMaxTimeout, ShowBrowser: opts.ShowBrowser, RequestCallback: opts.RequestCallback, SlowMotion: opts.SlowMotion, ScopeValidator: opts.ScopeValidator, ChromeUser: opts.ChromeUser, Trace: opts.Trace, CookieConsentBypass: opts.CookieConsentBypass, NoSandbox: opts.NoSandbox, Proxy: opts.Proxy, }) if err != nil { return nil, err } var diagnosticsWriter diagnostics.Writer if opts.EnableDiagnostics { directory := opts.DiagnosticsDir if directory == "" { cwd, _ := os.Getwd() directory = filepath.Join(cwd, fmt.Sprintf("katana-diagnostics-%s", time.Now().Format(time.RFC3339))) } writer, err := diagnostics.NewWriter(directory) if err != nil { return nil, err } diagnosticsWriter = writer opts.DiagnosticsDir = directory opts.Logger.Info("Diagnostics enabled", slog.String("directory", directory)) } crawler := &Crawler{ launcher: launcher, options: opts, logger: opts.Logger, uniqueActions: make(map[string]struct{}), diagnostics: diagnosticsWriter, simhashOracle: simhash.NewOracle(), } return crawler, nil } func (c *Crawler) Close() { c.launcher.Close() if c.diagnostics != nil { if err := c.diagnostics.Close(); err != nil { c.logger.Warn("Failed to close diagnostics", slog.String("error", err.Error())) } } } func (c *Crawler) GetCrawlGraph() *graph.CrawlGraph { return c.crawlGraph } func (c *Crawler) Crawl(URL string) error { defer func() { if c.diagnostics == nil { return } err := c.crawlGraph.DrawGraph(filepath.Join(c.options.DiagnosticsDir, "crawl-graph.dot")) if err != nil { c.logger.Error("Failed to draw crawl graph", slog.String("error", err.Error())) } }() actions := []*types.Action{{ Type: types.ActionTypeLoadURL, Input: URL, Depth: 0, OriginID: emptyPageHash, }} crawlQueue := queue.NewLinked(actions) c.crawlQueue = crawlQueue crawlGraph := graph.NewCrawlGraph() c.crawlGraph = crawlGraph // Add the initial blank state err := crawlGraph.AddPageState(types.PageState{ UniqueID: emptyPageHash, URL: "about:blank", Depth: 0, }) if err != nil { return err } // Create a master context that will automatically cancel all page operations // once the per-URL crawl deadline is reached. var ( ctx context.Context cancel context.CancelFunc ) if c.options.MaxCrawlDuration > 0 { ctx, cancel = context.WithTimeout(context.Background(), c.options.MaxCrawlDuration) } else { ctx, cancel = context.WithCancel(context.Background()) } defer cancel() // Retain the legacy time.After guard as a secondary fail-safe but the // context cancellation is what actually stops in-flight rod calls. var crawlTimeout <-chan time.Time if c.options.MaxCrawlDuration > 0 { crawlTimeout = time.After(c.options.MaxCrawlDuration) } consecutiveFailures := 0 for { select { case <-crawlTimeout: c.logger.Debug("Max crawl duration reached, stopping crawl") return nil default: // Check for too many failures if c.options.MaxFailureCount > 0 && consecutiveFailures >= c.options.MaxFailureCount { c.logger.Warn("Too many consecutive failures, stopping crawl", slog.Int("failures", consecutiveFailures), slog.Int("max_allowed", c.options.MaxFailureCount), slog.Int("remaining_actions", c.crawlQueue.Size()), ) return nil } action, err := crawlQueue.Get() if err == queue.ErrNoElementsAvailable { c.logger.Debug("No more actions to process") return nil } if err != nil { return err } if c.options.MaxDepth > 0 && action.Depth > c.options.MaxDepth { continue } page, err := c.launcher.GetPageFromPool() if err != nil { return err } page.Page = page.Context(ctx) c.logger.Debug("Processing action", slog.String("action", action.String()), ) if err := c.crawlFn(ctx, action, page); err != nil { if err == ErrNoCrawlingAction { return nil } if errors.Is(err, ErrElementNotVisible) { consecutiveFailures++ continue } var npe *rod.NoPointerEventsError var ish *rod.InvisibleShapeError if errors.As(err, &npe) || errors.As(err, &ish) { c.logger.Debug("Skipping action as it is not visible", slog.String("action", action.String()), slog.String("error", err.Error()), ) consecutiveFailures++ continue } var ne *rod.NavigationError if errors.As(err, &ne) { c.logger.Debug("Skipping action as navigation failed", slog.String("action", action.String()), slog.String("error", err.Error()), ) consecutiveFailures++ continue } if errors.Is(err, ErrNoNavigationPossible) { c.logger.Debug("Skipping action as no navigation possible", slog.String("action", action.String())) consecutiveFailures++ continue } var msce *utils.MaxSleepCountError if errors.As(err, &msce) { c.logger.Debug("Skipping action as it is taking too long", slog.String("action", action.String())) consecutiveFailures++ continue } c.logger.Debug("Skipping action due to site-specific error", slog.String("error", err.Error()), slog.String("action", action.String()), ) consecutiveFailures++ continue } consecutiveFailures = 0 } } } var ErrNoCrawlingAction = errors.New("no more actions to crawl") func (c *Crawler) crawlFn(ctx context.Context, action *types.Action, page *browser.BrowserPage) error { defer func() { c.launcher.PutBrowserToPool(page) }() currentPageHash, _, err := getPageHash(page) if err != nil { return err } c.logger.Debug("Processing action - current state", slog.String("current_page_hash", currentPageHash), slog.String("action_origin_id", action.OriginID), slog.String("action", action.String()), ) if action.OriginID != "" && action.OriginID != currentPageHash { c.logger.Debug("Need to navigate back to origin", slog.String("from", currentPageHash), slog.String("to", action.OriginID), ) newPageHash, err := c.navigateBackToStateOrigin(action, page, currentPageHash) if err != nil { return err } // Refresh the page hash currentPageHash = newPageHash } // FIXME: TODO: Restrict the navigation using scope manager and only // proceed with actions if the scope is allowed // Check the action and do actions based on action type if c.diagnostics != nil { if err := c.diagnostics.LogAction(action); err != nil { return err } } if err := c.executeCrawlStateAction(action, page); err != nil { return err } // Check for captcha pages after navigation and attempt to solve them. // On success, wait for the page to settle and re-enter crawlFn so navigation // discovery runs on the post-solve page instead of the captcha page. if c.options.CaptchaHandler != nil { html, htmlErr := page.HTML() if htmlErr == nil { handled, solveErr := c.options.CaptchaHandler.HandleIfCaptcha(ctx, page.Page, html) if solveErr != nil { gologger.Warning().Msgf("captcha solving failed: %s", solveErr) } if handled && solveErr == nil { _ = page.WaitPageLoadHeurisitics() } if handled { // Skip navigation discovery on captcha pages — the discovered // links/forms belong to the captcha widget, not the real page. return nil } } } pageState, err := newPageState(page, action) if err != nil { return err } if c.diagnostics != nil { if err := c.diagnostics.LogPageState(pageState, diagnostics.PostActionPageState); err != nil { return err } } pageState.OriginID = currentPageHash if c.options.ScopeValidator != nil { if !c.options.ScopeValidator(pageState.URL) { c.logger.Debug("Skipping navigation collection - current page is out of scope", slog.String("url", pageState.URL), ) if c.crawlQueue.Size() == 0 { return ErrNoCrawlingAction } return nil } } navigations, err := page.FindNavigations() if err != nil { return err } // Log navigations for diagnostics if c.diagnostics != nil { screenshotState, err := page.Screenshot(false, &proto.PageCaptureScreenshot{ Format: proto.PageCaptureScreenshotFormatPng, }) if err != nil { c.logger.Error("Failed to take screenshot", slog.String("error", err.Error())) } if err := c.diagnostics.LogPageStateScreenshot(pageState.UniqueID, screenshotState); err != nil { c.logger.Error("Failed to log page state screenshot", slog.String("error", err.Error())) } if err := c.diagnostics.LogNavigations(pageState.UniqueID, navigations); err != nil { c.logger.Error("Failed to log navigations", slog.String("error", err.Error())) } } for _, nav := range navigations { actionHash := nav.Hash() if _, ok := c.uniqueActions[actionHash]; ok { continue } c.uniqueActions[actionHash] = struct{}{} // Check if the element we have is a logout page if nav.Element != nil && isLogoutPage(nav.Element) { c.logger.Debug("Skipping Found logout page", slog.String("url", nav.Element.Attributes["href"]), ) continue } nav.OriginID = pageState.UniqueID c.logger.Debug("Got new navigation", slog.Any("navigation", nav), ) if err := c.crawlQueue.Offer(nav); err != nil { return err } } err = c.crawlGraph.AddPageState(*pageState) if err != nil { return err } // TODO: Check if the page opened new sub pages and if so capture their // navigation as well as close them so the state change can work. if len(navigations) == 0 && c.crawlQueue.Size() == 0 { return ErrNoCrawlingAction } return nil } var ErrElementNotVisible = errors.New("element not visible") func (c *Crawler) executeCrawlStateAction(action *types.Action, page *browser.BrowserPage) error { var err error switch action.Type { case types.ActionTypeLoadURL: // Apply a timeout to every critical Rod call. pTimeout := page.Timeout(c.options.PageMaxTimeout) if err := pTimeout.Navigate(action.Input); err != nil { return err } if err = page.WaitPageLoadHeurisitics(); err != nil { return err } case types.ActionTypeFillForm: if err := c.processForm(page, action.Form); err != nil { return err } case types.ActionTypeLeftClick, types.ActionTypeLeftClickDown: pTimeout := page.Timeout(c.options.PageMaxTimeout) element, err := pTimeout.ElementX(action.Element.XPath) if err != nil { return err } elementTimeout := element.Timeout(c.options.PageMaxTimeout) if err := elementTimeout.ScrollIntoView(); err != nil { return err } visible, err := element.Visible() if err != nil { return err } if !visible { return ErrElementNotVisible } // Check if element is interactable (not blocked by overlays) interactable, err := element.Interactable() if err != nil { var ce *rod.CoveredError if errors.As(err, &ce) { return ErrElementNotVisible } return err } if interactable == nil { return ErrElementNotVisible } if err := element.Click(proto.InputMouseButtonLeft, 1); err != nil { return err } if err = page.WaitPageLoadHeurisitics(); err != nil { return err } default: return fmt.Errorf("unknown action type: %v", action.Type) } return nil } var logoutPattern = regexp.MustCompile(`(?i)(log[\s-]?out|sign[\s-]?out|signout|deconnexion|cerrar[\s-]?sesion|sair|abmelden|uitloggen|ausloggen|exit|disconnect|terminate|end[\s-]?session|salir|desconectar|afmelden|wyloguj|logout|sign[\s-]?off)`) func isLogoutPage(element *types.HTMLElement) bool { return logoutPattern.MatchString(element.TextContent) || logoutPattern.MatchString(element.Attributes["href"]) } ================================================ FILE: pkg/engine/headless/crawler/diagnostics/diagnostics.go ================================================ package diagnostics import ( "encoding/json" "os" "path/filepath" "sync" "time" "github.com/projectdiscovery/katana/pkg/engine/headless/types" mapsutil "github.com/projectdiscovery/utils/maps" ) // Writer is a writer that writes diagnostics to a directory // for the katana headless crawler module. type Writer interface { Close() error LogAction(action *types.Action) error LogPageState(state *types.PageState, stateType PageStateType) error LogNavigations(pageStateID string, navigations []*types.Action) error LogPageStateScreenshot(pageStateID string, screenshot []byte) error } type PageStateType string var ( PreActionPageState PageStateType = "pre-action" PostActionPageState PageStateType = "post-action" ) type diskWriter struct { index mapsutil.OrderedMap[string, *stateMetadata] actions []*types.Action mu sync.Mutex directory string } type stateMetadata struct { UniqueID string `json:"unique_id"` URL string `json:"url"` Title string `json:"title"` Occurence int `json:"occurence"` Type string `json:"type"` } type navigationEntry struct { PageStateID string `json:"page_state_id"` URL string `json:"url"` NavigationCount int `json:"navigation_count"` Navigations []*types.Action `json:"navigations"` Timestamp int64 `json:"timestamp"` } // NewWriter creates a new Writer. func NewWriter(directory string) (Writer, error) { if err := os.MkdirAll(directory, 0755); err != nil { return nil, err } return &diskWriter{ directory: directory, index: mapsutil.NewOrderedMap[string, *stateMetadata](), actions: make([]*types.Action, 0), mu: sync.Mutex{}, }, nil } func (w *diskWriter) Close() error { w.mu.Lock() defer w.mu.Unlock() actionsList := w.actions marshallIndented, err := json.MarshalIndent(actionsList, "", " ") if err != nil { return err } if err := os.WriteFile(filepath.Join(w.directory, "actions.json"), marshallIndented, 0644); err != nil { return err } // Write index to a separate file var data []*stateMetadata w.index.Iterate(func(key string, value *stateMetadata) bool { data = append(data, value) return true }) marshallIndented, err = json.MarshalIndent(data, "", " ") if err != nil { return err } return os.WriteFile(filepath.Join(w.directory, "index.json"), marshallIndented, 0644) } func (w *diskWriter) LogAction(action *types.Action) error { w.mu.Lock() defer w.mu.Unlock() w.actions = append(w.actions, action) return nil } func (w *diskWriter) LogPageState(state *types.PageState, stateType PageStateType) error { w.mu.Lock() val, ok := w.index.Get(state.UniqueID) if ok && val != nil { val.Occurence++ w.mu.Unlock() return nil } w.index.Set(state.UniqueID, &stateMetadata{ URL: state.URL, Title: state.Title, Occurence: 1, Type: string(stateType), UniqueID: state.UniqueID, }) w.mu.Unlock() // Write dom to a separate file and remove striped dom // Create new directory for each state dom, strippedDOM := state.DOM, state.StrippedDOM dir := filepath.Join(w.directory, state.UniqueID) if err := os.MkdirAll(dir, 0755); err != nil { return err } domFile := filepath.Join(dir, "dom.html") if err := os.WriteFile(domFile, []byte(dom), 0644); err != nil { return err } strippedDOMFile := filepath.Join(dir, "stripped-dom.html") if err := os.WriteFile(strippedDOMFile, []byte(strippedDOM), 0644); err != nil { return err } return nil } func (w *diskWriter) LogNavigations(pageStateID string, navigations []*types.Action) error { w.mu.Lock() defer w.mu.Unlock() metadata, exists := w.index.Get(pageStateID) url := "" if exists && metadata != nil { url = metadata.URL } dir := filepath.Join(w.directory, pageStateID) if err := os.MkdirAll(dir, 0755); err != nil { return err } navigationsFile := filepath.Join(dir, "navigations.json") var entry navigationEntry if existingData, err := os.ReadFile(navigationsFile); err == nil { if err := json.Unmarshal(existingData, &entry); err != nil { return err } entry.Navigations = append(entry.Navigations, navigations...) entry.NavigationCount = len(entry.Navigations) entry.Timestamp = time.Now().Unix() } else { entry = navigationEntry{ PageStateID: pageStateID, URL: url, NavigationCount: len(navigations), Navigations: navigations, Timestamp: time.Now().Unix(), } } marshalledData, err := json.MarshalIndent(entry, "", " ") if err != nil { return err } // Write to navigations.json file in the state directory return os.WriteFile(navigationsFile, marshalledData, 0644) } func (w *diskWriter) LogPageStateScreenshot(pageStateID string, screenshot []byte) error { w.mu.Lock() defer w.mu.Unlock() dir := filepath.Join(w.directory, pageStateID) if err := os.MkdirAll(dir, 0755); err != nil { return err } screenshotFile := filepath.Join(dir, "screenshot.png") return os.WriteFile(screenshotFile, screenshot, 0644) } ================================================ FILE: pkg/engine/headless/crawler/formfill.go ================================================ package crawler import ( "fmt" "log/slog" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" "github.com/projectdiscovery/katana/pkg/engine/headless/browser" "github.com/projectdiscovery/katana/pkg/engine/headless/types" utilsformfill "github.com/projectdiscovery/katana/pkg/utils" mapsutil "github.com/projectdiscovery/utils/maps" ) func deriveName(e *types.HTMLElement) string { if n, ok := e.Attributes["name"]; ok && n != "" { return n } return e.ID } func copyAttrs(src map[string]string, skipKeys ...string) mapsutil.OrderedMap[string, string] { skip := map[string]struct{}{} for _, k := range skipKeys { skip[k] = struct{}{} } dst := mapsutil.NewOrderedMap[string, string]() for k, v := range src { if _, s := skip[k]; !s { dst.Set(k, v) } } return dst } func convertHTMLElementToFormInput(element *types.HTMLElement) utilsformfill.FormInput { return utilsformfill.FormInput{ Name: deriveName(element), Type: element.Type, Value: element.Value, Attributes: copyAttrs(element.Attributes, "name", "value", "type"), } } func convertHTMLElementToFormTextArea(element *types.HTMLElement) utilsformfill.FormTextArea { return utilsformfill.FormTextArea{ Name: deriveName(element), Attributes: copyAttrs(element.Attributes, "name"), } } func convertHTMLElementToFormSelect(element *types.HTMLElement) utilsformfill.FormSelect { return utilsformfill.FormSelect{ Name: deriveName(element), Attributes: copyAttrs(element.Attributes, "name"), SelectOptions: []utilsformfill.SelectOption{}, } } func (c *Crawler) processForm(page *browser.BrowserPage, form *types.HTMLForm) error { if !c.options.AutomaticFormFill { return nil } var formFields []interface{} var submitButton *rod.Element elementMap := make(map[string]*rod.Element) for _, field := range form.Elements { if field.XPath == "" { continue } element, err := page.ElementX(field.XPath) if err != nil { c.logger.Debug("Could not find form element", slog.String("xpath", field.XPath), slog.String("error", err.Error()), ) continue } fieldName := c.getFieldName(field) switch field.TagName { case "INPUT": if field.Type == "submit" || field.Type == "button" { if submitButton == nil && field.Type == "submit" { submitButton = element } continue } formInput := convertHTMLElementToFormInput(field) formFields = append(formFields, formInput) if fieldName != "" { elementMap[fieldName] = element } case "TEXTAREA": formTextArea := convertHTMLElementToFormTextArea(field) formFields = append(formFields, formTextArea) if fieldName != "" { elementMap[fieldName] = element } case "SELECT": formSelect := c.buildFormSelectWithOptions(page, field, element) formFields = append(formFields, formSelect) if fieldName != "" { elementMap[fieldName] = element } case "BUTTON": if field.Type == "submit" && submitButton == nil { submitButton = element } } } fillSuggestions := utilsformfill.FormFillSuggestions(formFields) if err := c.applyFormSuggestions(fillSuggestions, elementMap); err != nil { c.logger.Debug("Error applying form suggestions", slog.String("error", err.Error())) } if submitButton != nil { if err := submitButton.Click(proto.InputMouseButtonLeft, 1); err != nil { return err } } return nil } func (c *Crawler) getFieldName(field *types.HTMLElement) string { return deriveName(field) } func (c *Crawler) buildFormSelectWithOptions(page *browser.BrowserPage, field *types.HTMLElement, element *rod.Element) utilsformfill.FormSelect { formSelect := convertHTMLElementToFormSelect(field) options, err := element.Elements("option") if err == nil && len(options) > 0 { formSelect.SelectOptions = []utilsformfill.SelectOption{} for _, opt := range options { optionValue, _ := opt.Attribute("value") if optionValue == nil { text, _ := opt.Text() optionValue = &text } selected, _ := opt.Attribute("selected") selectOption := utilsformfill.SelectOption{ Value: *optionValue, Selected: "", } if selected != nil { selectOption.Selected = "selected" } formSelect.SelectOptions = append(formSelect.SelectOptions, selectOption) } } else { formSelect.SelectOptions = []utilsformfill.SelectOption{ {Value: utilsformfill.FormData.Placeholder, Selected: "selected"}, } } return formSelect } func (c *Crawler) applyFormSuggestions(suggestions mapsutil.OrderedMap[string, string], elementMap map[string]*rod.Element) error { suggestions.Iterate(func(fieldName, value string) bool { element, exists := elementMap[fieldName] if !exists || value == "" { return true } tagName, err := element.Eval(`() => this.tagName`) if err != nil { c.logger.Debug("Failed to get element tag", slog.String("field", fieldName), slog.String("error", err.Error()), ) return true } switch tagName.Value.String() { case "INPUT": inputType, _ := element.Attribute("type") if inputType != nil { switch *inputType { case "checkbox", "radio": if value == "on" || value == fieldName { if err := element.Click(proto.InputMouseButtonLeft, 1); err != nil { c.logger.Debug("Failed to check input", slog.String("field", fieldName), slog.String("type", *inputType), slog.String("error", err.Error()), ) } } default: if err := element.Input(value); err != nil { c.logger.Debug("Failed to fill input field", slog.String("field", fieldName), slog.String("value", value), slog.String("error", err.Error()), ) } } } case "TEXTAREA": if err := element.Input(value); err != nil { c.logger.Debug("Failed to fill textarea", slog.String("field", fieldName), slog.String("value", value), slog.String("error", err.Error()), ) } case "SELECT": if err := element.Select([]string{value}, true, rod.SelectorTypeText); err != nil { valueSelector := fmt.Sprintf(`[value="%s"]`, value) if err := element.Select([]string{valueSelector}, true, rod.SelectorTypeCSSSector); err != nil { c.logger.Debug("Failed to select option", slog.String("field", fieldName), slog.String("value", value), slog.String("error", err.Error()), ) } } } return true }) return nil } ================================================ FILE: pkg/engine/headless/crawler/normalizer/dom_utils.go ================================================ package normalizer import ( "strings" "github.com/PuerkitoBio/goquery" "golang.org/x/net/html" ) // DefaultDOMTransformations is default list of CSS selectors to remove from the DOM. var DefaultDOMTransformations = []string{ "style, script, path", // remove script and style tags "input[type='hidden']", // remove hidden inputs "meta[content]", // remove meta tags with content "link[rel='stylesheet']", // remove stylesheet links "svg", // remove svg "grammarly-desktop-integration", // remove grammarly "div[class*='ad'], div[id*='ad'], div[class*='banner'], div[id*='banner'], div[class*='pixel'], div[id*='pixel']", // remove ad, banner and pixel divs "input[name*='csrf'], input[name*='token']", // remove csrf and token inputs } // NoChildrenDomTransformations removes all elements with no children var NoChildrenDomTransformations = []string{ "div", // remove divs with no children "span", // remove spans with no children "form", // remove forms with no children "iframe", // remove iframes } // DOMNormalizer is a normalizer for DOM content type DOMNormalizer struct { customTransformations []domTransformationFunc } // NewDOMNormalizer returns a new DOMNormalizer // // transformations is a list of CSS selectors to remove from the DOM. func NewDOMNormalizer() *DOMNormalizer { var customTransformations []domTransformationFunc for _, t := range DefaultDOMTransformations { t := t customTransformations = append(customTransformations, func(doc *goquery.Document) { doc.Find(t).Each(func(_ int, s *goquery.Selection) { s.Remove() }) }) } for _, t := range NoChildrenDomTransformations { t := t customTransformations = append(customTransformations, func(doc *goquery.Document) { doc.Find(t).Each(func(_ int, s *goquery.Selection) { if s.Children().Length() == 0 && strings.TrimSpace(s.Text()) == "" { if node := s.Get(0); node != nil && len(node.Attr) == 0 { s.Remove() } } }) }) } return &DOMNormalizer{customTransformations: customTransformations} } // Apply applies the normalizers to the given content func (d *DOMNormalizer) Apply(content string) (string, error) { doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) if err != nil { return "", err } // Apply custom transformations first (selector-based removals, etc.) for _, f := range d.customTransformations { f(doc) } // Apply selection based transformations once at the root (recursive helpers will traverse) for _, f := range selectionBasedTransformationFuncs { f(doc.Selection) } result, err := doc.Html() if err != nil { return "", err } return result, nil } // domTransformationFunc does required transformation on document. type domTransformationFunc func(doc *goquery.Document) type selectionTransformationFunc func(s *goquery.Selection) var selectionBasedTransformationFuncs = []selectionTransformationFunc{ removeCommentsDomTransformationFunc, // remove comments removeClassIDDataAttributesDomTransformationFunc, // remove class, id and data attributes } func removeComments(n *html.Node) { if n.Type == html.CommentNode { n.Parent.RemoveChild(n) } for c := n.FirstChild; c != nil; c = c.NextSibling { removeComments(c) } } func removeCommentsDomTransformationFunc(s *goquery.Selection) { if n := s.Get(0); n != nil { removeComments(n) } } var attributes = []string{ "class", "id", "name", "href", "style", "width", "height", "src", "nowrap", "target", "valign", "cellpadding", "cellspacing", "value", "placeholder", "title", "alt", } func removeClassIDDataAttributesDomTransformationFunc(s *goquery.Selection) { removeAttributes(s) // Handle children s.Children().Each(func(_ int, child *goquery.Selection) { removeClassIDDataAttributesDomTransformationFunc(child) }) } func removeAttributes(s *goquery.Selection) { for _, attr := range attributes { s.RemoveAttr(attr) } for _, node := range s.Nodes { for _, attr := range node.Attr { attr := attr if strings.HasPrefix(attr.Key, "data-") || strings.HasPrefix(attr.Key, "aria-") || strings.HasPrefix(attr.Key, "js") { s.RemoveAttr(attr.Key) } } } } ================================================ FILE: pkg/engine/headless/crawler/normalizer/dom_utils_test.go ================================================ package normalizer import ( "testing" ) func TestDOMNormalizer_Apply(t *testing.T) { type args struct { content string } normalizer := NewDOMNormalizer() tests := []struct { name string d *DOMNormalizer args args want string wantErr bool }{ { name: "comments-style-script", d: normalizer, args: args{ content: `<html><head><style>body {color: blue;}</style></head><body><h1>Hello World</h1><!-- comment --><script>alert("hi");</script></body></html>`, }, want: "<html><head></head><body><h1>Hello World</h1></body></html>", wantErr: false, }, { name: "hidden input", d: normalizer, args: args{ content: `<html><head></head><body><input type="hidden" name="test" value="test"></body></html>`, }, want: "<html><head></head><body></body></html>", wantErr: false, }, // write tests for other cases { name: "csrf", d: normalizer, args: args{ content: `<html><head></head><body><input name="csrf" value="test"></body></html>`, }, want: "<html><head></head><body></body></html>", wantErr: false, }, { name: "class-id-data-attributes", d: normalizer, args: args{ content: `<html><head></head><body><div class="test" id="test" data-test="test"></div></body></html>`, }, want: "<html><head></head><body><div></div></body></html>", }, { name: "inline-style", d: normalizer, args: args{ content: `<html><head></head><body><div style="color: blue;"></div></body></html>`, }, want: "<html><head></head><body><div></div></body></html>", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { d := normalizer got, err := d.Apply(tt.args.content) if (err != nil) != tt.wantErr { t.Errorf("DOMNormalizer.Apply() error = %v, wantErr %v", err, tt.wantErr) return } if got != tt.want { t.Errorf("DOMNormalizer.Apply() = %v, want %v", got, tt.want) } }) } } ================================================ FILE: pkg/engine/headless/crawler/normalizer/helpers.go ================================================ package normalizer // dateTimePatterns contains regex patterns for various date and time formats // The ordering is important for proper matching var dateTimePatterns = []string{ /* with days */ "[a-zA-Z]{3,} [0-9]{1,2} [a-zA-Z]{3,} [0-9]{4}", "[a-zA-Z]{3,} [0-9]{1,2} [a-zA-Z]{3,} '[0-9]{2}", "[a-zA-Z]{3,} [0-9]{1,2} [a-zA-Z]{3,}", /* only numeric */ "[0-9]{4}-[0-9]{1,2}-[0-9]{1,2}", "[0-9]{4}\\.[0-9]{1,2}\\.[0-9]{1,2}", "[0-9]{4}/[0-9]{1,2}/[0-9]{1,2}", "[0-9]{1,2}-[0-9]{1,2}-[0-9]{4}", "[0-9]{1,2}\\.[0-9]{1,2}\\.[0-9]{4}", "[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}", "[0-9]{1,2}-[0-9]{1,2}-'[0-9]{2}", "[0-9]{1,2}\\.[0-9]{1,2}\\.'[0-9]{2}", "[0-9]{1,2}/[0-9]{1,2}/'[0-9]{2}", "[0-9]{1,2}-[0-9]{1,2}-[0-9]{2}", "[0-9]{1,2}\\.[0-9]{1,2}\\.[0-9]{2}", "[0-9]{1,2}/[0-9]{1,2}/[0-9]{2}", /* long months */ "[0-9]{1,2} [a-zA-Z]{3,} [0-9]{4}", "[0-9]{1,2}th [a-zA-Z]{3,} [0-9]{4}", "[0-9]{1,2}th [a-zA-Z]{3,}", "[0-9]{4} [a-zA-Z]{3,} [0-9]{1,2}", "[0-9]{4}[a-zA-Z]{3,}[0-9]{1,2}", "[a-zA-Z]{3,} [0-9]{4}", "[a-zA-Z]{3,} '[0-9]{2}", "[a-zA-Z]{3,} [0-9]{1,2} [0-9]{4}", "[a-zA-Z]{3,} [0-9]{1,2}, [0-9]{4}", "[a-zA-Z]{3,} [0-9]{1,2} '[0-9]{2}", "[a-zA-Z]{3,} [0-9]{1,2}, '[0-9]{2}", /* Times */ "[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}( )?(pm|PM|am|AM)", "[0-9]{1,2}:[0-9]{1,2}( )?(pm|PM|am|AM)", "[0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2}", "[0-9]{1,2}:[0-9]{1,2}", } ================================================ FILE: pkg/engine/headless/crawler/normalizer/normalizer.go ================================================ package normalizer import ( "fmt" "html" "net/url" "regexp" "strconv" "strings" "github.com/PuerkitoBio/goquery" "github.com/pkg/errors" htmlpkg "golang.org/x/net/html" ) var whiteSpacesRegex = regexp.MustCompile(`[\r\n]+|\s+`) type Normalizer struct { dom *DOMNormalizer text *TextNormalizer } // New returns a new Normalizer func New() (*Normalizer, error) { textNormalizer, err := NewTextNormalizer() if err != nil { return nil, errors.Wrap(err, "failed to create text normalizer") } domNormalizer := NewDOMNormalizer() return &Normalizer{ dom: domNormalizer, text: textNormalizer, }, nil } // Apply applies the normalizers to the given content // // It normalizes the given content by: // - Applying the DOM normalizer // - Applying the text normalizer // - Denormalizing it func (n *Normalizer) Apply(text string) (string, error) { first := normalizeDocument(text) firstpass, err := n.dom.Apply(first) if err != nil { return "", errors.Wrap(err, "failed to apply DOM normalizer") } secondpass, err := stripTextContent(firstpass) if err != nil { return "", errors.Wrap(err, "failed to strip text content") } thirdpass := n.text.Apply(secondpass) fourthpass := normalizeDocument(thirdpass) return fourthpass, nil } // normalizeDocument normalizes the given document by: // - Lowercasing it // - URL decoding it // - HTML entity decoding it // - Replacing all whitespace variations with a space // - Trimming the document whitespaces func normalizeDocument(text string) string { // Lowercase the document lowercased := strings.ToLower(text) // Convert hexadecimal escape sequences to HTML entities converted := convertHexEscapeSequencesToEntities(lowercased) unescaped := html.UnescapeString(converted) // URL Decode and HTML entity decode the document to standardize it. urlDecoded, err := url.QueryUnescape(unescaped) if err != nil { urlDecoded = unescaped } // Replace all whitespaces with a space normalized := whiteSpacesRegex.ReplaceAllString(urlDecoded, " ") // Trim the document to remove leading and trailing whitespaces return strings.Trim(normalized, " \r\n\t") } func replaceHexEscapeSequence(match string) string { // Remove the '\x' prefix code := strings.TrimPrefix(match, "\\x") // Parse the hexadecimal code to an integer value, err := strconv.ParseInt(code, 16, 32) if err != nil { // If there's an error, return the original match return match } // Return the corresponding HTML entity return fmt.Sprintf("&#x%x;", value) } // Define the regex pattern to match hexadecimal escape sequences var pattern = regexp.MustCompile(`\\x[0-9a-fA-F]{2}`) func convertHexEscapeSequencesToEntities(input string) string { return pattern.ReplaceAllStringFunc(input, func(match string) string { return replaceHexEscapeSequence(match) }) } func stripTextContent(content string) (string, error) { doc, err := goquery.NewDocumentFromReader(strings.NewReader(content)) if err != nil { return "", err } doc.Find("h1, h2, h3, h4, h5, h6, p, span, div, td, th, li, a").Each(func(_ int, s *goquery.Selection) { removeTextNodesFromSelection(s) }) result, err := doc.Html() if err != nil { return "", err } return result, nil } func removeTextNodesFromSelection(s *goquery.Selection) { node := s.Get(0) if node == nil { return } for c := node.FirstChild; c != nil; { next := c.NextSibling if c.Type == htmlpkg.TextNode { node.RemoveChild(c) } c = next } } ================================================ FILE: pkg/engine/headless/crawler/normalizer/simhash/simhash.go ================================================ // Package simhash implements SimHash algorithm for near-duplicate detection. // // The original algorithm is taken from: https://github.com/yahoo/gryffin/blob/master/html-distance/feature.go // Optimized implementation with performance improvements. // // Original Copyright 2015, Yahoo Inc. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package simhash import ( "bytes" "fmt" "io" "sync" "github.com/mfonda/simhash" "golang.org/x/net/html" ) // Constants for optimization const ( maxTokens = 5000 featuresBufSize = 1000 ) // Pre-allocated buffers for feature generation var bufferPool = sync.Pool{ New: func() interface{} { return &bytes.Buffer{} }, } func fingerprintOptimized(r io.Reader, shingle int) uint64 { if shingle < 1 { shingle = 1 } v := simhash.Vector{} z := html.NewTokenizer(r) features := make([]string, 0, featuresBufSize) window := make([][]byte, shingle) windowIndex := 0 // Single-pass tokenization and feature extraction count := 0 for count < maxTokens { if tt := z.Next(); tt == html.ErrorToken { break } t := z.Token() count++ extractFeatures(&t, &features) } // Process features with shingling buf := bufferPool.Get().(*bytes.Buffer) defer func() { buf.Reset() bufferPool.Put(buf) }() for _, f := range features { window[windowIndex%shingle] = []byte(f) windowIndex++ buf.Reset() for i := 0; i < shingle; i++ { if i > 0 { buf.WriteByte(' ') } buf.Write(window[(windowIndex-shingle+i+shingle)%shingle]) } sum := simhash.NewFeature(buf.Bytes()).Sum() for i := uint8(0); i < 64; i++ { if (sum>>i)&1 == 1 { v[i]++ } else { v[i]-- } } } return simhash.Fingerprint(v) } // extractFeatures extracts features from HTML token and appends to slice func extractFeatures(t *html.Token, features *[]string) { // Pre-allocate string builder for efficiency var s string switch t.Type { case html.StartTagToken: s = "A:" + t.DataAtom.String() case html.EndTagToken: s = "B:" + t.DataAtom.String() case html.SelfClosingTagToken: s = "C:" + t.DataAtom.String() case html.DoctypeToken: s = "D:" + string(t.Data) case html.CommentToken: s = "E:" + string(t.Data) case html.TextToken: s = "F:" + string(t.Data) case html.ErrorToken: s = "Z:" + string(t.Data) default: return } *features = append(*features, s) // Process attributes for _, attr := range t.Attr { switch attr.Key { case "class", "name", "rel": s = fmt.Sprintf("G:%s:%s:%s", t.DataAtom.String(), attr.Key, attr.Val) default: s = fmt.Sprintf("G:%s:%s", t.DataAtom.String(), attr.Key) } *features = append(*features, s) } } // Fingerprint is the original function signature for compatibility func Fingerprint(r io.Reader, shingle int) uint64 { return fingerprintOptimized(r, shingle) } type Oracle struct { fingerprint uint64 // node value. nodes [65]*Oracle // leaf nodes } // NewOracle return an oracle that could tell if the fingerprint has been seen or not. func NewOracle() *Oracle { return newNode(0) } func newNode(f uint64) *Oracle { return &Oracle{fingerprint: f} } // Distance return the similarity distance between two fingerprint. func Distance(a, b uint64) uint8 { return simhash.Compare(a, b) } // See asks the oracle to see the fingerprint. func (n *Oracle) See(f uint64) *Oracle { d := Distance(n.fingerprint, f) if d == 0 { // current node with same fingerprint. return n } // the target node is already set, if c := n.nodes[d]; c != nil { return c.See(f) } n.nodes[d] = newNode(f) return n.nodes[d] } // Seen asks the oracle if anything closed to the fingerprint in a range (r) is seen before. func (n *Oracle) Seen(f uint64, r uint8) bool { d := Distance(n.fingerprint, f) if d <= r { return true } // Check the direct child at distance d first if c := n.nodes[d]; c != nil && c.Seen(f, r) { return true } // Optimized search: start from closest distance and expand outward for offset := uint8(1); offset <= r; offset++ { // Check both directions if d >= offset { if c := n.nodes[d-offset]; c != nil && c.Seen(f, r) { return true } } if d+offset <= 64 { if c := n.nodes[d+offset]; c != nil && c.Seen(f, r) { return true } } } return false } ================================================ FILE: pkg/engine/headless/crawler/normalizer/simhash/simhash_test.go ================================================ package simhash import ( "strings" "testing" ) var ( htmlA = `<html><body><p>Hello World</p></body></html>` // htmlB differs by an exclamation mark. This should keep the documents fairly similar // while still resulting in a different SimHash fingerprint. htmlB = `<html><body><p>Hello World!</p></body></html>` ) // TestFingerprintDeterministic ensures that hashing the same document twice produces // identical fingerprints and that a shingle value of 0 gracefully falls back to 1. func TestFingerprintDeterministic(t *testing.T) { in1 := strings.NewReader(htmlA) in2 := strings.NewReader(htmlA) fp1 := Fingerprint(in1, 0) fp2 := Fingerprint(in2, 1) if fp1 != fp2 { t.Fatalf("expected identical fingerprints, got %d and %d", fp1, fp2) } } // TestFingerprintSimilarity checks that two similar documents yield fingerprints with // a small (non-zero) Hamming distance. func TestFingerprintSimilarity(t *testing.T) { fpA := Fingerprint(strings.NewReader(htmlA), 3) fpB := Fingerprint(strings.NewReader(htmlB), 3) d := Distance(fpA, fpB) if d == 0 { t.Fatalf("expected different fingerprints, got distance 0") } const maxReasonableDistance = 20 // out of a maximum of 64 if d > maxReasonableDistance { t.Fatalf("expected similar documents to have distance <= %d, got %d", maxReasonableDistance, d) } } // TestOracle validates the basic behaviour of the Oracle structure. func TestOracle(t *testing.T) { fpA := Fingerprint(strings.NewReader(htmlA), 3) fpB := Fingerprint(strings.NewReader(htmlB), 3) o := NewOracle() if o.Seen(fpA, 0) { t.Fatalf("oracle should not have seen fingerprint yet") } // Teach the oracle about fpA. o.See(fpA) if !o.Seen(fpA, 0) { t.Fatalf("oracle should recognise an identical fingerprint once seen") } if o.Seen(fpB, 0) { t.Fatalf("oracle should not treat different fingerprint as identical when r=0") } r := Distance(fpA, fpB) if !o.Seen(fpB, r) { t.Fatalf("oracle should recognise fingerprint within distance %d", r) } } ================================================ FILE: pkg/engine/headless/crawler/normalizer/text_utils.go ================================================ package normalizer import ( "fmt" "regexp" "slices" ) // DefaultTextPatterns is a list of regex patterns for the text normalizer var DefaultTextPatterns = []string{ // emailAddress `\b(?i)[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b`, // ipAddress `\b(?:25[0-5]|2[0-4]\d|1?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|1?\d?\d)){3}\b`, // uuid `\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b`, // relativeDates `\b(?:[0-9]{1,2}\s(?:days?|weeks?|months?|years?)\s(?:ago|from\s+now))\b`, // priceAmounts (no leading \b due to currency symbols) `[\$€£¥]\s*\d+(?:\.\d{1,2})?\b`, // phoneNumbers `\b\+?\d{7,15}\b`, // ssnNumbers `\b\d{3}-\d{2}-\d{4}\b`, // timestampRegex `\b(?:(?:[0-9]{4}-[0-9]{2}-[0-9]{2})|(?:(?:[0-9]{2}\/){2}[0-9]{4}))\s(?:[0-9]{2}:[0-9]{2}:[0-9]{2})\b`, } // TextNormalizer is a normalizer for text type TextNormalizer struct { // patterns is a list of regex patterns for the text normalizer patterns []*regexp.Regexp } // NewTextNormalizer returns a new TextNormalizer // // patterns is a list of regex patterns for the text normalizer // DefaultTextPatterns is used if patterns is nil. See DefaultTextPatterns for more info. func NewTextNormalizer() (*TextNormalizer, error) { patterns := slices.Clone(DefaultTextPatterns) patterns = append(patterns, dateTimePatterns...) var compiledPatterns []*regexp.Regexp for _, pattern := range patterns { pattern := pattern compiledPattern, err := regexp.Compile(pattern) if err != nil { return nil, fmt.Errorf("error compiling pattern %s: %v", pattern, err) } compiledPatterns = append(compiledPatterns, compiledPattern) } return &TextNormalizer{patterns: compiledPatterns}, nil } // Apply applies the patterns to the text and returns the normalized text func (n *TextNormalizer) Apply(text string) string { for _, pattern := range n.patterns { pattern := pattern text = pattern.ReplaceAllString(text, "") } return text } ================================================ FILE: pkg/engine/headless/crawler/normalizer/text_utils_test.go ================================================ package normalizer import ( "strings" "testing" ) func TestTextNormalizer_AllPatterns(t *testing.T) { normalizer, err := NewTextNormalizer() if err != nil { t.Fatalf("Failed to create normalizer: %v", err) } testText := ` Contact us at test@example.com or admin@SITE.ORG for support. Server IP: 192.168.1.1 and public IP: 8.8.8.8 Invalid IPs should not match: 999.999.999.999 or 300.400.500.600 UUID: 550e8400-e29b-41d4-a716-446655440000 Relative dates: 5 days ago, 2 weeks from now, 10 months ago Prices: $19.99, €50.00, £25.50, ¥1000 Phone numbers: +1234567890, +447911123456, +33123456789 SSN: 123-45-6789, 987-65-4321 Timestamps: 2023-12-25 14:30:00, 12/25/2023 09:15:30 ` result := normalizer.Apply(testText) // Check that sensitive data was removed testCases := []struct { pattern string shouldBeRemoved bool description string }{ {"test@example.com", true, "lowercase email"}, {"admin@SITE.ORG", true, "uppercase email"}, {"192.168.1.1", true, "private IP"}, {"8.8.8.8", true, "public IP"}, {"999.999.999.999", false, "invalid IP (too high octets)"}, {"300.400.500.600", false, "invalid IP (too high octets)"}, {"550e8400-e29b-41d4-a716-446655440000", true, "UUID"}, {"5 days ago", true, "relative date - days ago"}, {"2 weeks from now", true, "relative date - weeks from now"}, {"10 months ago", true, "relative date - months ago"}, {"$19.99", true, "USD price"}, {"€50.00", true, "EUR price"}, {"£25.50", true, "GBP price"}, {"¥1000", true, "JPY price"}, {"+1234567890", true, "international phone"}, {"+447911123456", true, "UK phone"}, {"+33123456789", true, "French phone"}, {"123-45-6789", true, "SSN format 1"}, {"987-65-4321", true, "SSN format 2"}, {"2023-12-25 14:30:00", true, "ISO timestamp"}, {"12/25/2023 09:15:30", true, "US timestamp"}, } for _, tc := range testCases { if tc.shouldBeRemoved { if strings.Contains(result, tc.pattern) { t.Errorf("%s: Pattern '%s' should have been removed but was found in result", tc.description, tc.pattern) } } else { if !strings.Contains(result, tc.pattern) { t.Errorf("%s: Invalid pattern '%s' should not have been removed but was not found in result", tc.description, tc.pattern) } } } t.Logf("Original text length: %d", len(testText)) t.Logf("Normalized text length: %d", len(result)) t.Logf("Normalized result: %s", result) } ================================================ FILE: pkg/engine/headless/crawler/state.go ================================================ package crawler import ( "crypto/sha256" "encoding/hex" "fmt" "log/slog" "strings" graphlib "github.com/dominikbraun/graph" "github.com/pkg/errors" "github.com/projectdiscovery/katana/pkg/engine/headless/browser" "github.com/projectdiscovery/katana/pkg/engine/headless/crawler/diagnostics" "github.com/projectdiscovery/katana/pkg/engine/headless/crawler/normalizer/simhash" "github.com/projectdiscovery/katana/pkg/engine/headless/types" ) var emptyPageHash = sha256Hash("") const simhashThreshold = 2 // Allow up to 2 bits difference func (c *Crawler) isCorrectNavigation(page *browser.BrowserPage, action *types.Action) (string, *types.PageState, error) { currentPageHash, pageState, err := getPageHash(page) if err != nil { return "", nil, err } if currentPageHash == action.OriginID { return currentPageHash, pageState, nil } // Get the origin page state to compare SimHash originPageState, err := c.crawlGraph.GetPageState(action.OriginID) if err != nil { return "", pageState, fmt.Errorf("failed to get origin page state: %w", err) } if pageState != nil && originPageState != nil { distance := simhash.Distance(pageState.SimHash, originPageState.SimHash) if distance <= simhashThreshold { c.logger.Debug("Page is similar enough to origin, proceeding", slog.String("current_hash", currentPageHash), slog.String("origin_hash", action.OriginID), slog.Uint64("simhash_distance", uint64(distance)), ) // Treat this page as the origin state to avoid creating a new vertex return originPageState.UniqueID, pageState, nil } } return "", pageState, fmt.Errorf("failed to navigate back to origin page: %s != %s", currentPageHash, action.OriginID) } func getPageHash(page *browser.BrowserPage) (string, *types.PageState, error) { pageState, err := newPageState(page, nil) if err == ErrEmptyPage { return emptyPageHash, nil, nil } if err != nil { return "", nil, errors.Wrap(err, "could not get page state") } return pageState.UniqueID, pageState, nil } var ErrEmptyPage = errors.New("page is empty") func newPageState(page *browser.BrowserPage, action *types.Action) (*types.PageState, error) { pageInfo, err := page.Info() if err != nil { return nil, errors.Wrap(err, "could not get page info") } if pageInfo.URL == "" || pageInfo.URL == "about:blank" { return nil, ErrEmptyPage } outerHTML, err := page.HTML() if err != nil { return nil, errors.Wrap(err, "could not get html content") } state := &types.PageState{ URL: pageInfo.URL, DOM: outerHTML, NavigationAction: action, Title: pageInfo.Title, } if action != nil { state.Depth = action.Depth + 1 } strippedDOM, err := getStrippedDOM(outerHTML) if err != nil { return nil, errors.Wrap(err, "could not get stripped dom") } state.StrippedDOM = strippedDOM // Get sha256 hash of the stripped dom state.UniqueID = sha256Hash(strippedDOM) state.SimHash = simhash.Fingerprint(strings.NewReader(strippedDOM), 3) return state, nil } func sha256Hash(item string) string { hasher := sha256.New() hasher.Write([]byte(item)) hashItem := hex.EncodeToString(hasher.Sum(nil)) return hashItem } func getStrippedDOM(contents string) (string, error) { normalized, err := domNormalizer.Apply(contents) if err != nil { return "", errors.Wrap(err, "could not normalize dom") } return normalized, nil } var ErrNoNavigationPossible = errors.New("no navigation possible") // navigateBackToStateOrigin implements the logic to navigate back to the state origin // // It implements different logics as an optimization to decide // how to navigate back. // // 1. If the action has an element, check if the element is visible on the current page // If the element is visible, directly use that to navigate. // // 2. If we have browser history, and the page is in the history which was the origin // of the action, then we can directly use the browser history to navigate back. // // 3. If all else fails, we have the shortest path navigation. func (c *Crawler) navigateBackToStateOrigin(action *types.Action, page *browser.BrowserPage, currentPageHash string) (string, error) { c.logger.Debug("Found action with different origin id", slog.String("action_origin_id", action.OriginID), slog.String("current_page_hash", currentPageHash), ) // Get vertex from the graph originPageState, err := c.crawlGraph.GetPageState(action.OriginID) if err != nil { c.logger.Debug("Failed to get origin page state", slog.String("error", err.Error())) return "", err } // First, check if the element we want to interact with exists on current page if action.Element != nil && currentPageHash != emptyPageHash { newPageHash, err := c.tryElementNavigation(page, action, currentPageHash) if err != nil { c.logger.Debug("Failed to navigate back to origin page using element", slog.String("error", err.Error())) } if newPageHash != "" { return newPageHash, nil } } // Try to see if we can move back using the browser history newPageHash, err := c.tryBrowserHistoryNavigation(page, originPageState, action) if err != nil { c.logger.Debug("Failed to navigate back using browser history", slog.String("error", err.Error())) } if newPageHash != "" { return newPageHash, nil } // Finally try Shortest path walking from root. newPageHash, err = c.tryShortestPathNavigation(action, page, currentPageHash) if err != nil { return "", err } if newPageHash == "" { return "", ErrNoNavigationPossible } return newPageHash, nil } func (c *Crawler) tryElementNavigation(page *browser.BrowserPage, action *types.Action, currentPageHash string) (string, error) { element, err := page.ElementX(action.Element.XPath) if err != nil { return "", err } visible, err := element.Visible() if err != nil { return "", err } if !visible { return "", nil } // Also ensure its interactable interactable, err := element.Interactable() if err != nil || interactable == nil { return "", nil } // Ensure its the same element htmlElement, err := page.GetElementFromXpath(action.Element.XPath) if err != nil { return "", err } // Ensure its the same element with stronger identity matching if isElementMatch(htmlElement, action.Element) { c.logger.Debug("Found target element on current page, proceeding without navigation") // FIXME: Return the origin element ID so that the graph shows // correctly the fastest way to reach the state. return action.OriginID, nil } return "", nil } // isElementMatch implements stronger identity matching logic to reduce false positives. // It treats identical ID as definitive match, otherwise requires both Classes and TextContent // to match, or enforces at least two matching non-empty attributes. func isElementMatch(current, target *types.HTMLElement) bool { if current == nil || target == nil { return false } // Definitive match: identical non-empty IDs if current.ID != "" && target.ID != "" && current.ID == target.ID { return true } matchCount := 0 if current.Classes != "" && target.Classes != "" && current.Classes == target.Classes { matchCount++ } if current.TextContent != "" && target.TextContent != "" && current.TextContent == target.TextContent { matchCount++ } if current.TagName != "" && target.TagName != "" && current.TagName == target.TagName { matchCount++ } // Require at least two matching non-empty attributes for a positive match // This ensures stronger identity verification while still allowing reasonable fallbacks return matchCount >= 2 } func (c *Crawler) tryBrowserHistoryNavigation(page *browser.BrowserPage, originPageState *types.PageState, action *types.Action) (string, error) { canNavigateBack, stepsBack, err := c.isBackNavigationPossible(page, originPageState) if err != nil { return "", err } if !canNavigateBack { return "", nil } c.logger.Debug("Navigating back using browser history", slog.Int("steps_back", stepsBack)) var navigatedSuccessfully bool for i := 0; i < stepsBack; i++ { if err := page.NavigateBack(); err != nil { return "", err } navigatedSuccessfully = true } if !navigatedSuccessfully { return "", nil } if err := page.WaitPageLoadHeurisitics(); err != nil { c.logger.Debug("Failed to wait for page load after navigating back using browser history", slog.String("error", err.Error())) } newPageHash, pageState, err := c.isCorrectNavigation(page, action) if c.diagnostics != nil && pageState != nil { if err := c.diagnostics.LogPageState(pageState, diagnostics.PreActionPageState); err != nil { return "", err } } if err != nil { return "", err } return newPageHash, nil } func (c *Crawler) isBackNavigationPossible(page *browser.BrowserPage, originPage *types.PageState) (bool, int, error) { history, err := page.GetNavigationHistory() if err != nil { return false, 0, err } if len(history.Entries) == 0 { return false, 0, nil } currentIndex := history.CurrentIndex for i, entry := range history.Entries { if entry.URL == originPage.URL && originPage.Title == entry.Title { stepsBack := currentIndex - i return true, stepsBack, nil } } return false, 0, nil } func (c *Crawler) tryShortestPathNavigation(action *types.Action, page *browser.BrowserPage, currentPageHash string) (string, error) { c.logger.Debug("Trying Shortest path to navigate back to origin page", slog.String("action_origin_id", action.OriginID), slog.String("current_page_hash", currentPageHash)) actions, err := c.crawlGraph.ShortestPath(currentPageHash, action.OriginID) if err != nil { if errors.Is(err, graphlib.ErrTargetNotReachable) { c.logger.Debug("Target not reachable, reaching from blank state", slog.String("action_origin_id", action.OriginID), ) actions, err = c.crawlGraph.ShortestPath(emptyPageHash, action.OriginID) if err != nil { return "", errors.Wrap(err, "could not find path to origin page") } } else { return "", errors.Wrap(err, "failed to find shortest path") } } c.logger.Debug("Found actions to traverse", slog.Any("actions", actions), ) for _, action := range actions { if err := c.executeCrawlStateAction(action, page); err != nil { return "", err } } newPageHash, pageState, err := c.isCorrectNavigation(page, action) if c.diagnostics != nil && pageState != nil { if err := c.diagnostics.LogPageState(pageState, diagnostics.PreActionPageState); err != nil { return "", err } } if err != nil { return "", err } return newPageHash, nil } ================================================ FILE: pkg/engine/headless/crawler/state_test.go ================================================ package crawler import ( "strings" "testing" "github.com/pkg/errors" "github.com/projectdiscovery/katana/pkg/engine/headless/crawler/normalizer/simhash" "github.com/stretchr/testify/assert" ) func TestPageFingerprint_Stability(t *testing.T) { } func TestPageFingerprint(t *testing.T) { tests := []struct { name string html1 string html2 string shouldMatch bool }{ { name: "same page different dynamic content", html1: ` <html> <head><title>Home

Welcome John!

`, html2: ` Home

Welcome Jane!

`, shouldMatch: true, }, { name: "same form different values", html1: `
`, html2: `
`, shouldMatch: true, }, { name: "different error messages", html1: `
Invalid password
`, html2: `
Account locked
`, shouldMatch: true, }, { name: "different page structure", html1: `

Page 1

Content

`, html2: `

Page 1

Content
`, shouldMatch: false, }, } getHash := func(html string) (string, error) { strippedDOM, err := getStrippedDOM(html) if err != nil { return "", errors.Wrap(err, "could not get stripped dom") } // Get sha256 hash of the stripped dom return sha256Hash(strippedDOM), nil } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { hash1, err := getHash(tt.html1) assert.NoError(t, err) hash2, err := getHash(tt.html2) assert.NoError(t, err) if tt.shouldMatch { assert.Equal(t, hash1, hash2) } else { assert.NotEqual(t, hash1, hash2) } }) } } func TestSimHashSimilarity(t *testing.T) { tests := []struct { name string html1 string html2 string threshold uint8 similar bool }{ { name: "identical pages", html1: `

Hello World

Content here

`, html2: `

Hello World

Content here

`, threshold: 4, similar: true, }, { name: "pages with minor changes", html1: `

Hello World

Content here

Time: 12:00`, html2: `

Hello World

Content here

Time: 12:01`, threshold: 4, similar: true, }, { name: "pages with different content", html1: `

Hello World

Content here

`, html2: `

Goodbye World

Different content

Extra stuff
`, threshold: 4, similar: false, }, { name: "pages with dynamic IDs", html1: `

Hello

`, html2: `

Hello

`, threshold: 4, similar: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Normalize and compute SimHash norm1, err := domNormalizer.Apply(tt.html1) if err != nil { t.Fatalf("Failed to normalize html1: %v", err) } norm2, err := domNormalizer.Apply(tt.html2) if err != nil { t.Fatalf("Failed to normalize html2: %v", err) } hash1 := simhash.Fingerprint(strings.NewReader(norm1), 3) hash2 := simhash.Fingerprint(strings.NewReader(norm2), 3) distance := simhash.Distance(hash1, hash2) isSimilar := distance <= tt.threshold if isSimilar != tt.similar { t.Errorf("Expected similar=%v, got similar=%v (distance=%d)", tt.similar, isSimilar, distance) t.Logf("Hash1: %064b", hash1) t.Logf("Hash2: %064b", hash2) t.Logf("Normalized HTML1:\n%s", norm1) t.Logf("Normalized HTML2:\n%s", norm2) } }) } } ================================================ FILE: pkg/engine/headless/debugger.go ================================================ package headless import ( "encoding/json" "fmt" "net/http" "sync" "time" ) // ActiveURL represents a URL currently being processed type ActiveURL struct { URL string `json:"url"` StartTime time.Time `json:"start_time"` Duration string `json:"duration"` Depth int `json:"depth"` } // CrawlDebugger tracks active URLs for debugging type CrawlDebugger struct { mu sync.RWMutex activeURLs map[string]*ActiveURL httpServer *http.Server } // NewCrawlDebugger creates a new debugger instance func NewCrawlDebugger(httpPort int) *CrawlDebugger { cd := &CrawlDebugger{ activeURLs: make(map[string]*ActiveURL), } mux := http.NewServeMux() mux.HandleFunc("/debug/active-urls", cd.handleActiveURLs) mux.HandleFunc("/debug/health", cd.handleHealth) cd.httpServer = &http.Server{ Addr: fmt.Sprintf("127.0.0.1:%d", httpPort), Handler: mux, ReadTimeout: 5 * time.Second, ReadHeaderTimeout: 2 * time.Second, WriteTimeout: 5 * time.Second, IdleTimeout: 30 * time.Second, } go func() { if err := cd.httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { fmt.Printf("Debug HTTP server error: %v\n", err) } }() return cd } // StartURL marks a URL as being processed func (cd *CrawlDebugger) StartURL(url string, depth int) { if cd == nil { return } cd.mu.Lock() cd.activeURLs[url] = &ActiveURL{ URL: url, StartTime: time.Now(), Depth: depth, } cd.mu.Unlock() } // EndURL marks a URL as finished processing func (cd *CrawlDebugger) EndURL(url string) { if cd == nil { return } cd.mu.Lock() delete(cd.activeURLs, url) cd.mu.Unlock() } // GetActiveURLs returns currently active URLs with durations func (cd *CrawlDebugger) GetActiveURLs() []ActiveURL { if cd == nil { return nil } cd.mu.RLock() defer cd.mu.RUnlock() urls := make([]ActiveURL, 0, len(cd.activeURLs)) now := time.Now() for _, au := range cd.activeURLs { copy := *au copy.Duration = now.Sub(au.StartTime).String() urls = append(urls, copy) } return urls } // HTTP handlers func (cd *CrawlDebugger) handleActiveURLs(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") active := cd.GetActiveURLs() if err := json.NewEncoder(w).Encode(map[string]interface{}{ "timestamp": time.Now().Format(time.RFC3339), "active_urls": active, "count": len(active), }); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func (cd *CrawlDebugger) handleHealth(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(map[string]interface{}{ "status": "ok", "timestamp": time.Now().Format(time.RFC3339), }); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } func (cd *CrawlDebugger) Close() { if cd == nil { return } if cd.httpServer != nil { _ = cd.httpServer.Close() } } ================================================ FILE: pkg/engine/headless/graph/graph.go ================================================ // Package graph implements a Directed Graph for storing // state information during crawling of a Web Application. package graph import ( "os" "github.com/dominikbraun/graph" "github.com/dominikbraun/graph/draw" "github.com/pkg/errors" "github.com/projectdiscovery/katana/pkg/engine/headless/types" ) // CrawlGraph is a graph for storing state information during crawling type CrawlGraph struct { graph graph.Graph[string, types.PageState] } func navigationHasherFunc(n types.PageState) string { return n.UniqueID } // NewCrawlGraph creates a new CrawlGraph instance func NewCrawlGraph() *CrawlGraph { return &CrawlGraph{ graph: graph.New(navigationHasherFunc, func(t *graph.Traits) { t.IsDirected = true t.IsRooted = true t.IsWeighted = true }), } } func (g *CrawlGraph) GetVertices() []string { vertices := []string{} adjacencyMap, err := g.graph.AdjacencyMap() if err != nil { return nil } for vertex := range adjacencyMap { vertices = append(vertices, vertex) } return vertices } // AddNavigation adds a navigation to the graph func (g *CrawlGraph) AddPageState(n types.PageState) error { vertexAttrs := map[string]string{ "label": n.URL, } if n.IsRoot { vertexAttrs["is_root"] = "true" } err := g.graph.AddVertex(n, func(vp *graph.VertexProperties) { vp.Weight = n.Depth vp.Attributes = vertexAttrs }) if err != nil { if errors.Is(err, graph.ErrVertexAlreadyExists) { return nil } return errors.Wrap(err, "could not add vertex to graph") } if n.NavigationAction != nil { edgeAttrs := map[string]string{ "label": n.NavigationAction.String(), } err = g.graph.AddEdge(n.OriginID, n.UniqueID, func(ep *graph.EdgeProperties) { ep.Weight = n.Depth ep.Attributes = edgeAttrs }) if err != nil { if errors.Is(err, graph.ErrEdgeAlreadyExists) { return nil } return errors.Wrapf(err, "could not add edge to graph: source vertex %s", n.OriginID) } } return nil } func (g *CrawlGraph) AddEdge(sourceState, targetState string, action *types.Action) error { if action == nil { return errors.New("add edge: action cannot be nil") } edgeAttrs := map[string]string{ "label": action.String(), } err := g.graph.AddEdge(sourceState, targetState, func(ep *graph.EdgeProperties) { ep.Weight = action.Depth ep.Attributes = edgeAttrs }) if err != nil { if errors.Is(err, graph.ErrEdgeAlreadyExists) { return nil } return errors.Wrap(err, "could not add edge to graph") } return nil } func (g *CrawlGraph) GetPageState(id string) (*types.PageState, error) { pageVertex, err := g.graph.Vertex(id) if err != nil { return nil, errors.Wrap(err, "could not get vertex") } return &pageVertex, nil } func (g *CrawlGraph) ShortestPath(sourceState, targetState string) ([]*types.Action, error) { shortestPath, err := graph.ShortestPath(g.graph, sourceState, targetState) if err != nil { return nil, errors.Wrap(err, "could not find shortest path") } actionsSlice := make([]*types.Action, 0, len(shortestPath)) for _, path := range shortestPath { pageVertex, err := g.graph.Vertex(path) if err != nil { return nil, errors.Wrap(err, "could not get vertex") } if pageVertex.URL == "about:blank" || pageVertex.NavigationAction == nil { continue } actionsSlice = append(actionsSlice, pageVertex.NavigationAction) } return actionsSlice, nil } func (g *CrawlGraph) DrawGraph(file string) error { f, err := os.Create(file) if err != nil { return errors.Wrap(err, "could not create graph file") } defer func() { _ = f.Close() }() return draw.DOT(g.graph, f) } ================================================ FILE: pkg/engine/headless/headless.go ================================================ package headless import ( "log/slog" "net/url" "os" "time" "github.com/lmittmann/tint" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/engine/headless/browser" "github.com/projectdiscovery/katana/pkg/engine/headless/captcha" _ "github.com/projectdiscovery/katana/pkg/engine/headless/captcha/capsolver" "github.com/projectdiscovery/katana/pkg/engine/headless/crawler" "github.com/projectdiscovery/katana/pkg/engine/parser" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/types" "github.com/projectdiscovery/katana/pkg/utils" mapsutil "github.com/projectdiscovery/utils/maps" ) type Headless struct { logger *slog.Logger options *types.CrawlerOptions deduplicator *mapsutil.SyncLockMap[string, struct{}] pathTrie *utils.PathTrie debugger *CrawlDebugger } // New returns a new headless crawler instance func New(options *types.CrawlerOptions) (*Headless, error) { logger := newLogger(options) headless := &Headless{ logger: logger, options: options, deduplicator: mapsutil.NewSyncLockMap[string, struct{}](), } if options.Options.FilterSimilar { headless.pathTrie = utils.NewPathTrie(options.Options.FilterSimilarThreshold) } // Show crawl debugger if verbose is enabled if options.Options.Verbose { headless.debugger = NewCrawlDebugger(8089) } return headless, nil } func newLogger(options *types.CrawlerOptions) *slog.Logger { if options.Logger != nil { return options.Logger } writer := os.Stderr // set global logger with custom options level := slog.LevelInfo if options.Options.Debug { level = slog.LevelDebug } logger := slog.New( tint.NewHandler(writer, &tint.Options{ Level: level, TimeFormat: time.Kitchen, }), ) return logger } func validateScopeFunc(h *Headless, URL string) browser.ScopeValidator { parsedURL, err := url.Parse(URL) if err != nil { return func(string) bool { return true } } rootHostname := parsedURL.Hostname() return func(s string) bool { if h.options.ScopeManager == nil { return true } parsed, err := url.Parse(s) if err != nil { return false } validated, err := h.options.ScopeManager.Validate(parsed, rootHostname) if err != nil { return false } return validated } } // Crawl executes the headless crawling on a given URL func (h *Headless) Crawl(URL string) error { if h.debugger != nil { h.debugger.StartURL(URL, 0) } defer func() { if h.debugger != nil { h.debugger.EndURL(URL) } }() scopeValidator := validateScopeFunc(h, URL) crawlOpts := crawler.Options{ ChromiumPath: h.options.Options.SystemChromePath, MaxDepth: h.options.Options.MaxDepth, ShowBrowser: h.options.Options.ShowBrowser, MaxCrawlDuration: h.options.Options.CrawlDuration, MaxFailureCount: h.options.Options.MaxFailureCount, NoSandbox: h.options.Options.HeadlessNoSandbox, Proxy: h.options.Options.Proxy, MaxBrowsers: 1, PageMaxTimeout: 30 * time.Second, ScopeValidator: scopeValidator, AutomaticFormFill: h.options.Options.AutomaticFormFill, RequestCallback: func(rr *output.Result) { if rr == nil || rr.Request == nil { return } if scopeValidator != nil && !scopeValidator(rr.Request.URL) { return } navigationRequests := h.performAdditionalAnalysis(rr) for _, req := range navigationRequests { if err := h.options.OutputWriter.Write(req); err != nil { h.logger.Debug("failed to write navigation result", slog.String("url", func() string { if req != nil && req.Request != nil { return req.Request.URL } return "" }()), slog.String("error", err.Error()), ) } } if rr.Response != nil { rr.Response.KnowledgeBase = h.options.ClassifyPage(rr.Response.Body) rr.Response.Raw = "" rr.Response.Body = "" } if err := h.options.OutputWriter.Write(rr); err != nil { h.logger.Debug("failed to write result", slog.String("error", err.Error()), ) } }, Logger: h.logger, ChromeUser: h.options.ChromeUser, EnableDiagnostics: h.options.Options.EnableDiagnostics, Trace: h.options.Options.EnableDiagnostics, CookieConsentBypass: true, } if provider := h.options.Options.CaptchaSolverProvider; provider != "" { gologger.Debug().Msgf("captcha solver enabled: provider=%s", provider) handler, err := captcha.NewHandler(provider, h.options.Options.CaptchaSolverAPIKey) if err != nil { gologger.Warning().Msgf("captcha handler init failed: %s", err) } else { crawlOpts.CaptchaHandler = handler } } // TODO: Make the crawling multi-threaded. Right now concurrency is hardcoded to 1. headlessCrawler, err := crawler.New(crawlOpts) if err != nil { return err } defer headlessCrawler.Close() if err = headlessCrawler.Crawl(URL); err != nil { return err } return nil } func (h *Headless) Close() error { if h.debugger != nil { h.debugger.Close() } return nil } func (h *Headless) performAdditionalAnalysis(rr *output.Result) []*output.Result { responseParser := parser.NewResponseParser() newNavigations := responseParser.ParseResponse(rr.Response) navigationRequests := make([]*output.Result, 0) for _, resp := range newNavigations { dedupKey := resp.URL if h.options.Options.FilterSimilar { dedupKey = utils.FingerprintURL(dedupKey, h.pathTrie) } if _, ok := h.deduplicator.Get(dedupKey); ok { continue } if err := h.deduplicator.Set(dedupKey, struct{}{}); err != nil { h.logger.Debug("deduplicator set failed", slog.String("url", resp.URL), slog.String("error", err.Error()), ) continue } navigationRequests = append(navigationRequests, &output.Result{ Request: resp, }) } return navigationRequests } ================================================ FILE: pkg/engine/headless/js/js.go ================================================ package js import ( _ "embed" "github.com/go-rod/rod" "github.com/pkg/errors" ) var ( //go:embed utils.js utilsJavascriptBundle string //go:embed page-init.js pageInitJavascriptBundle string ) // InitJavascriptEnv injects the necessary javascript code into the browser func InitJavascriptEnv(page *rod.Page) error { if _, err := page.EvalOnNewDocument(utilsJavascriptBundle); err != nil { return errors.Wrap(err, "failed to inject utils.js") } if _, err := page.EvalOnNewDocument(pageInitJavascriptBundle); err != nil { return errors.Wrap(err, "failed to inject page-init.js") } return nil } ================================================ FILE: pkg/engine/headless/js/page-init.js ================================================ // This script initializes the page and hooks up event listeners // and other interesting stuff needed to make the crawling work. // // Actions performed: // // 1. Hook addTargetListener to capture all the event listeners added on the page. // These are accessible via window.__eventListeners // 2. Hook window.open to capture all the opened pages. // These are accessible via window.__navigatedLinks // 3. Hook setTimeout and setInterval to speed up delayed actions // 4. Hook form reset to prevent the form from being reset // 5. Hook window.close to prevent the page from being closed // 6. Hook history pushState and replaceState for new links // 7. Add event listener for hashchange to identify new navigations // 8. TODO: Hook inline event listeners so that layer0 event listeners can be tracked as well (function pageInitAndHook() { const markElementReadonlyProperties = { writable: false, configurable: false, }; // hookNavigatedLinkSinks hooks the navigated link sinks // on the page to capture all the navigated links. function hookNavigatedLinkSinks() { window.__navigatedLinks = []; // Hook history.pushState and history.replaceState to capture all the navigated links const __origPushState = window.history.pushState.bind(window.history); const __origReplaceState = window.history.replaceState.bind(window.history); function __wrappedPushState(a, b, c) { try { window.__navigatedLinks.push({ url: c, source: "history.pushState" }); } catch (_) {} return __origPushState(a, b, c); } function __wrappedReplaceState(a, b, c) { try { window.__navigatedLinks.push({ url: c, source: "history.replaceState" }); } catch (_) {} return __origReplaceState(a, b, c); } Object.defineProperty(window.history, "pushState", { value: __wrappedPushState, writable: false, configurable: false }); Object.defineProperty(window.history, "replaceState", { value: __wrappedReplaceState, writable: false, configurable: false }); // Hook window.open to capture all the opened pages const __origOpen = window.open.bind(window); function __wrappedOpen(url, ...rest) { try { window.__navigatedLinks.push({ url, source: "window.open" }); } catch (_) {} return __origOpen(url, ...rest); } Object.defineProperty(window, "open", { value: __wrappedOpen, writable: false, configurable: false }); // Add event listener for hashchange window.addEventListener("hashchange", function () { window.__navigatedLinks.push({ url: document.location.href, source: "hashchange", }); }); const __OrigWebSocket = window.WebSocket; function __WrappedWebSocket(url, protocols) { try { window.__navigatedLinks.push({ url, source: "websocket" }); } catch (_) {} return Reflect.construct(__OrigWebSocket, [url, protocols], new.target || __WrappedWebSocket); } __WrappedWebSocket.prototype = __OrigWebSocket.prototype; Object.setPrototypeOf(__WrappedWebSocket, __OrigWebSocket); Object.defineProperty(window, "WebSocket", { value: __WrappedWebSocket, writable: false, configurable: false }); const __OrigEventSource = window.EventSource; function __WrappedEventSource(url, eventSourceInitDict) { try { window.__navigatedLinks.push({ url, source: "eventsource" }); } catch (_) {} return Reflect.construct(__OrigEventSource, [url, eventSourceInitDict], new.target || __WrappedEventSource); } __WrappedEventSource.prototype = __OrigEventSource.prototype; Object.setPrototypeOf(__WrappedEventSource, __OrigEventSource); Object.defineProperty(window, "EventSource", { value: __WrappedEventSource, writable: false, configurable: false }); const __origFetch = window.fetch.bind(window); function __wrappedFetch(...args) { const url = args[0] instanceof Request ? args[0].url : args[0]; try { window.__navigatedLinks.push({ url: url, source: "fetch" }); } catch (_) {} return __origFetch(...args); } Object.defineProperty(window, "fetch", { value: __wrappedFetch, writable: false, configurable: false }); } // hookMiscellaneousUtilities performs miscellaneous hooks // on the page to prevent certain actions from happening // and to speed up certain actions. function hookMiscellaneousUtilities() { // Hook form reset to conditionally prevent the form from being reset const __origFormReset = HTMLFormElement.prototype.reset; const __wrappedFormReset = function (...args) { if (window.__katanaHooksOptions?.preventFormReset === true) { try { console.log("[hook] cancel reset form"); } catch (_) {} return; } return __origFormReset.apply(this, args); }; Object.defineProperty( HTMLFormElement.prototype, "reset", { value: __wrappedFormReset, writable: false, configurable: false } ); // Hook window.close to prevent the page from being closed const __wrappedClose = function () { console.log("[hook] trying to close page."); }; Object.defineProperty(window, "close", { value: __wrappedClose, writable: false, configurable: false }); // Hook setTimeout and setInterval to speed up delayed actions // on the page. This is useful where there is some request happening // on the page after a delay or some animation happening after a delay. const __origSetTimeout = window.setTimeout; const __origSetInterval = window.setInterval; const speedUpFactor = 0.1; function __wrappedSetTimeout(callback, delay, ...args) { return __origSetTimeout(callback, delay * speedUpFactor, ...args); } function __wrappedSetInterval(callback, delay, ...args) { return __origSetInterval(callback, delay * speedUpFactor, ...args); } Object.defineProperty(window, "setTimeout", { value: __wrappedSetTimeout, writable: false, configurable: false }); Object.defineProperty(window, "setInterval", { value: __wrappedSetInterval, writable: false, configurable: false }); } // hookAddEventListener hooks the addTargetListener to capture // all the event listeners added on the page function hookAddEventListener() { const originalAddEventListener = Element.prototype.addEventListener; window.__eventListeners = []; Element.prototype.addEventListener = function (type, listener, options) { // Ensure `this` is a valid element and has the necessary properties if (!this || !this.tagName) { return originalAddEventListener.call(this, type, listener, options); } if (this.tagName == "BODY") { return originalAddEventListener.call(this, type, listener, options); } let item = { element: { tagName: this.tagName, id: this.id, classes: this.className, outerHTML: this.outerHTML.slice(0, 100), // Capture a snippet of the element's outerHTML xpath: window.getXPath(this), cssSelector: window.getCssPath(this), attributes: window.getElementAttributes(this), textContent: this.textContent.trim(), hidden: this.hidden, name: this.name, type: this.type, value: this.value, }, type: type, listener: listener.toString(), options: options || {}, }; console.log("[hook] got event listener", item); window.__eventListeners.push(item); return originalAddEventListener.call(this, type, listener, options); }; } // Main hook initialization part const __opts = window.__katanaHooksOptions || { hooked: false }; try { if (__opts.hooked === true) hookAddEventListener(); } catch (_) {} try { if (__opts.hooked === true) hookNavigatedLinkSinks(); } catch (_) {} try { if (__opts.hooked === true) hookMiscellaneousUtilities(); } catch (_) {} })(); ================================================ FILE: pkg/engine/headless/js/utils.js ================================================ // This file contains utility JS functions that are utilised by // the main crawling JS code to perform actions. (function initUtilityFunctions() { // getElementAttributes returns the attributes of an element window.getElementAttributes = function (element) { const attrs = {}; for (let attr of element.attributes) { attrs[attr.name] = attr.value; } return attrs; }; // _elementDataFromElement returns the data for an element window._elementDataFromElement = function (el) { return { tagName: el.tagName, id: el.id, classes: typeof el.className === 'string' ? el.className : Array.from(el.classList).join(' '), attributes: window.getElementAttributes(el), hidden: el.hidden, outerHTML: el.outerHTML, name: el.name, type: el.type, value: el.value != null ? String(el.value) : '', textContent: el.textContent.trim(), xpath: window.getXPath(el), cssSelector: window.getCssPath(el), }; }; // getAllElements returns all the elements for a query // selector on the page window.getAllElements = function (selector) { try { const nodes = document.querySelectorAll(selector); return Array.from(nodes).map((el) => _elementDataFromElement(el)); } catch (_) { return []; } }; window.getElementFromXPath = function (xpath) { try { const element = document .evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null) .singleNodeValue; return element ? _elementDataFromElement(element) : null; } catch (_) { return null; } } // getAllElementsWithEventListeners returns all the elements // on the page along with their event listeners // TODO: Is it optimized? or do we need to do something else? window.getAllElementsWithEventListeners = function () { const elements = document.querySelectorAll("*"); const elementsWithListeners = []; for (let el of elements) { const listeners = getEventListeners(el); if (listeners && listeners.length) { elementsWithListeners.push({ element: _elementDataFromElement(el), listeners: listeners, }); } } return elementsWithListeners; }; // getEventListeners returns all the event listeners // attached to an element function getEventListeners(element) { const listeners = []; for (let event in element) { if (event.startsWith("on")) { const listener = element[event]; if (typeof listener === "function") { listeners.push({ type: event, listener: listener.toString(), }); } } } return listeners; } // getAllForms returns all the forms on the page // along with their elements window.getAllForms = function () { const forms = document.querySelectorAll("form"); const pseudoForms = document.querySelectorAll("div.form"); const allForms = [...forms, ...pseudoForms]; return Array.from(allForms).map((form) => ({ tagName: form.tagName, id: form.id, classes: typeof form.className === 'string' ? form.className : Array.from(form.classList).join(' '), attributes: window.getElementAttributes(form), outerHTML: form.outerHTML, action: form.action ? String(form.action) : '', method: form.method, xpath: window.getXPath(form), cssSelector: window.getCssPath(form), elements: form.elements ? Array.from(form.elements).map((el) => _elementDataFromElement(el)) : Array.from(form.querySelectorAll('input, select, textarea, button')).map((el) => _elementDataFromElement(el)) })); }; // Copyright (C) Chrome Authors // The below code is part of the Chrome DevTools project // and is adapted from there. // Utility to get the CSS selector path for an element. window.getCssPath = function (node, optimized = false) { if (node.nodeType !== Node.ELEMENT_NODE) return ""; const steps = []; let contextNode = node; while (contextNode) { const step = window._cssPathStep( contextNode, optimized, contextNode === node ); if (!step) break; // Error - bail out early. steps.push(step.value); if (step.optimized) break; contextNode = contextNode.parentNode; } steps.reverse(); return steps.join(" > "); }; // Utility to get the XPath for an element. window.getXPath = function (node, optimized = false) { if (node.nodeType === Node.DOCUMENT_NODE) return "/"; const steps = []; let contextNode = node; while (contextNode) { const step = window._xPathValue(contextNode, optimized); if (!step) break; // Error - bail out early. steps.push(step.value); if (step.optimized) break; contextNode = contextNode.parentNode; } steps.reverse(); return (steps.length && steps[0].optimized ? "" : "/") + steps.join("/"); }; // Helper to create a step in the CSS path. window._cssPathStep = function (node, optimized, isTargetNode) { if (node.nodeType !== Node.ELEMENT_NODE) return null; const id = node.getAttribute("id"); if (optimized) { if (id) return { value: `#${id}`, optimized: true }; const nodeNameLower = node.nodeName.toLowerCase(); if ( nodeNameLower === "body" || nodeNameLower === "head" || nodeNameLower === "html" ) return { value: node.nodeName, optimized: true }; } const nodeName = node.nodeName; if (id) return { value: `${nodeName}#${id}`, optimized: true }; const parent = node.parentNode; if (!parent || parent.nodeType === Node.DOCUMENT_NODE) return { value: nodeName, optimized: true }; const prefixedOwnClassNamesArray = window.prefixedElementClassNames(node); let needsClassNames = false; let needsNthChild = false; let ownIndex = -1; let elementIndex = -1; const siblings = parent.children; for ( let i = 0; (ownIndex === -1 || !needsNthChild) && i < siblings.length; ++i ) { const sibling = siblings[i]; if (sibling.nodeType !== Node.ELEMENT_NODE) continue; elementIndex += 1; if (sibling === node) { ownIndex = elementIndex; continue; } if (needsNthChild) continue; if (sibling.nodeName.toLowerCase() !== nodeName.toLowerCase()) continue; needsClassNames = true; const ownClassNames = new Set(prefixedOwnClassNamesArray); if (!ownClassNames.size) { needsNthChild = true; continue; } const siblingClassNamesArray = window.prefixedElementClassNames(sibling); for (let j = 0; j < siblingClassNamesArray.length; ++j) { const siblingClass = siblingClassNamesArray[j]; if (!ownClassNames.has(siblingClass)) continue; ownClassNames.delete(siblingClass); if (!ownClassNames.size) { needsNthChild = true; break; } } } let result = nodeName; if ( isTargetNode && nodeName.toLowerCase() === "input" && node.getAttribute("type") && !node.getAttribute("id") && !node.getAttribute("class") ) result += '[type="' + node.getAttribute("type") + '"]'; if (needsNthChild) { result += `:nth-child(${ownIndex + 1})`; } else if (needsClassNames) { for (const prefixedName of prefixedOwnClassNamesArray) result += "." + window.escapeIdentifierIfNeeded(prefixedName.substr(1)); } return { value: result, optimized: false }; }; // Helper to get class names prefixed with '$' for mapping purposes. window.prefixedElementClassNames = function (node) { const classAttribute = node.getAttribute("class"); if (!classAttribute) return []; return classAttribute .split(/\s+/g) .filter(Boolean) .map(function (name) { return "$" + name; }); }; // Helper to escape identifiers for use in CSS selectors. window.escapeIdentifierIfNeeded = function (ident) { if (window.isCSSIdentifier(ident)) return ident; const shouldEscapeFirst = /^(?:[0-9]|-[0-9-]?)/.test(ident); const lastIndex = ident.length - 1; return ident.replace(/./g, function (c, i) { return (shouldEscapeFirst && i === 0) || !window.isCSSIdentChar(c) ? window.escapeAsciiChar(c, i === lastIndex) : c; }); }; // Helper to determine if a character is valid in a CSS identifier. window.isCSSIdentChar = function (c) { if (/[a-zA-Z0-9_-]/.test(c)) return true; return c.charCodeAt(0) >= 0xa0; }; // Helper to determine if a string is a valid CSS identifier. window.isCSSIdentifier = function (value) { return /^-{0,2}[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value); }; // Helper to escape ASCII characters for use in CSS selectors. window.escapeAsciiChar = function (c, isLast) { return ( "\\" + c.charCodeAt(0).toString(16).padStart(2, "0") + (isLast ? "" : " ") ); }; // Helper to get the XPath step for a node. window._xPathValue = function (node, optimized) { let ownValue; const ownIndex = window._xPathIndex(node); if (ownIndex === -1) return null; switch (node.nodeType) { case Node.ELEMENT_NODE: if (optimized && node.getAttribute("id")) return { value: `//*[@id="${node.getAttribute("id")}"]`, optimized: true, }; ownValue = node.localName; break; case Node.ATTRIBUTE_NODE: ownValue = "@" + node.nodeName; break; case Node.TEXT_NODE: case Node.CDATA_SECTION_NODE: ownValue = "text()"; break; case Node.PROCESSING_INSTRUCTION_NODE: ownValue = "processing-instruction()"; break; case Node.COMMENT_NODE: ownValue = "comment()"; break; case Node.DOCUMENT_NODE: ownValue = ""; break; default: ownValue = ""; break; } if (ownIndex > 0) ownValue += `[${ownIndex}]`; return { value: ownValue, optimized: node.nodeType === Node.DOCUMENT_NODE }; }; // Helper to get the XPath index for a node. window._xPathIndex = function (node) { function areNodesSimilar(left, right) { if (left === right) return true; if ( left.nodeType === Node.ELEMENT_NODE && right.nodeType === Node.ELEMENT_NODE ) return left.localName === right.localName; if (left.nodeType === right.nodeType) return true; const leftType = left.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : left.nodeType; const rightType = right.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : right.nodeType; return leftType === rightType; } const siblings = node.parentNode ? node.parentNode.children : null; if (!siblings) return 0; let hasSameNamedElements = false; for (let i = 0; i < siblings.length; ++i) { if (areNodesSimilar(node, siblings[i]) && siblings[i] !== node) { hasSameNamedElements = true; break; } } if (!hasSameNamedElements) return 0; let ownIndex = 1; for (let i = 0; i < siblings.length; ++i) { if (areNodesSimilar(node, siblings[i])) { if (siblings[i] === node) return ownIndex; ++ownIndex; } } return -1; }; })(); ================================================ FILE: pkg/engine/headless/types/types.go ================================================ package types import ( "crypto/md5" "encoding/hex" "fmt" "os" "sort" "strings" ) var ( IsDiagnosticEnabled = false ) // PageState represents a page in the state of the // web application as determined by the crawler. // It represents the vertex of the crawl graph type PageState struct { UniqueID string `json:"unique_id,omitempty"` SimHash uint64 `json:"sim_hash,omitempty"` OriginID string `json:"origin_id,omitempty"` URL string `json:"url,omitempty"` Title string `json:"title,omitempty"` DOM string `json:"dom,omitempty"` StrippedDOM string `json:"stripped_dom,omitempty"` Depth int `json:"depth,omitempty"` IsRoot bool `json:"is_root,omitempty"` // NavigationAction is actions taken to reach this state NavigationAction *Action `json:"navigation_actions,omitempty"` } // Action is a action taken in the browser type Action struct { OriginID string `json:"origin_id,omitempty"` Type ActionType `json:"type,omitempty"` Input string `json:"input,omitempty"` Element *HTMLElement `json:"element,omitempty"` Form *HTMLForm `json:"form,omitempty"` Depth int `json:"depth,omitempty"` ResultID string `json:"result_id,omitempty"` } func (a *Action) Hash() string { if a.Element != nil { return a.Element.Hash() } if a.Form != nil { return a.Form.Hash() } return string(a.Type) + "|" + a.Input + "|" + a.OriginID } func (a *Action) String() string { var builder strings.Builder builder.WriteString(string(a.Type)) if a.Type == ActionTypeLoadURL { fmt.Fprintf(&builder, " %s", a.Input) } if a.Element != nil { fmt.Fprintf(&builder, " on %s", a.Element) } value := builder.String() return value } type ActionType string const ( ActionTypeUnknown ActionType = "unknown" ActionTypeLoadURL ActionType = "load_url" ActionTypeExecuteJS ActionType = "execute_js" ActionTypeLeftClick ActionType = "left_click" ActionTypeLeftClickDown ActionType = "left_click_down" ActionTypeLeftClickUp ActionType = "left_click_up" ActionTypeRightClick ActionType = "right_click" ActionTypeDoubleClick ActionType = "double_click" ActionTypeScroll ActionType = "scroll" ActionTypeSendKeys ActionType = "send_keys" ActionTypeKeyUp ActionType = "key_up" ActionTypeKeyDown ActionType = "key_down" ActionTypeHover ActionType = "hover" ActionTypeFocus ActionType = "focus" ActionTypeBlur ActionType = "blur" ActionTypeMouseOverAndOut ActionType = "mouse_over_and_out" ActionTypeMouseWheel ActionType = "mouse_wheel" ActionTypeFillForm ActionType = "fill_form" ActionTypeWait ActionType = "wait" ActionTypeRedirect ActionType = "redirect" ActionTypeSubRequest ActionType = "sub_request" ) func ActionFromEventListener(listener *EventListener) *Action { var actionType ActionType switch listener.Type { case "click": actionType = ActionTypeLeftClick case "mousedown": actionType = ActionTypeLeftClickDown case "mouseup": actionType = ActionTypeLeftClickUp case "keydown": actionType = ActionTypeKeyDown case "keyup": actionType = ActionTypeKeyUp case "focus": actionType = ActionTypeFocus case "blur": actionType = ActionTypeBlur case "scroll": actionType = ActionTypeScroll case "dblclick": actionType = ActionTypeDoubleClick case "contextmenu": actionType = ActionTypeRightClick case "mouseover", "mouseout": actionType = ActionTypeMouseOverAndOut case "wheel": actionType = ActionTypeMouseWheel default: actionType = ActionTypeUnknown } return &Action{ Type: actionType, Element: listener.Element, } } // HTMLElement represents a DOM element type HTMLElement struct { TagName string `json:"tagName,omitempty"` ID string `json:"id,omitempty"` Classes string `json:"classes,omitempty"` Attributes map[string]string `json:"attributes,omitempty"` Hidden bool `json:"hidden,omitempty"` OuterHTML string `json:"outerHTML,omitempty"` Type string `json:"type,omitempty"` Value string `json:"value,omitempty"` CSSSelector string `json:"cssSelector,omitempty"` XPath string `json:"xpath,omitempty"` TextContent string `json:"textContent,omitempty"` MD5Hash string `json:"md5Hash,omitempty"` } func (e *HTMLElement) String() string { var builder strings.Builder builder.WriteString(e.TagName) if e.ID != "" { fmt.Fprintf(&builder, "#%s", e.ID) } if e.Classes != "" { fmt.Fprintf(&builder, ".%s", e.Classes) } if e.TextContent != "" { trimmedContent := strings.Trim(e.TextContent, " \n\r\t") fmt.Fprintf(&builder, " (%s)", trimmedContent) } value := builder.String() return value } func (e *HTMLElement) Hash() string { hasher := md5.New() var parts []string // Use stable identifiers parts = append(parts, e.TagName) if e.ID != "" { parts = append(parts, "id:"+e.ID) } if e.Classes != "" { parts = append(parts, "class:"+e.Classes) } // Add stable attributes stableAttrs := getStableAttributes(e.Attributes) for _, k := range stableAttrs { parts = append(parts, fmt.Sprintf("%s:%s", k, e.Attributes[k])) } hashInput := strings.Join(parts, "|") if IsDiagnosticEnabled { fmt.Fprintf(os.Stderr, "[diagnostic] Element hash input: %s\n", hashInput) } hasher.Write([]byte(hashInput)) return hex.EncodeToString(hasher.Sum(nil)) } // HTMLForm represents a form element type HTMLForm struct { TagName string `json:"tagName,omitempty"` ID string `json:"id,omitempty"` Classes string `json:"classes,omitempty"` Attributes map[string]string `json:"attributes,omitempty"` Hidden bool `json:"hidden,omitempty"` OuterHTML string `json:"outerHTML,omitempty"` Action string `json:"action,omitempty"` Method string `json:"method,omitempty"` Elements []*HTMLElement `json:"elements,omitempty"` CSSSelector string `json:"cssSelector,omitempty"` XPath string `json:"xpath,omitempty"` } func (f *HTMLForm) Hash() string { hasher := md5.New() var parts []string parts = append(parts, f.TagName) if f.ID != "" { parts = append(parts, "id:"+f.ID) } if f.Classes != "" { parts = append(parts, "class:"+f.Classes) } // Add stable attributes stableAttrs := getStableAttributes(f.Attributes) for _, k := range stableAttrs { parts = append(parts, fmt.Sprintf("%s:%s", k, f.Attributes[k])) } parts = append(parts, fmt.Sprintf("action:%s", f.Action), fmt.Sprintf("method:%s", f.Method)) // Include hashes of form elements for _, element := range f.Elements { parts = append(parts, element.Hash()) } hashInput := strings.Join(parts, "|") if IsDiagnosticEnabled { fmt.Fprintf(os.Stderr, "[diagnostic] Form hash input: %s\n", hashInput) } hasher.Write([]byte(hashInput)) return hex.EncodeToString(hasher.Sum(nil)) } // getStableAttributes returns a sorted slice of attribute keys that are considered stable. func getStableAttributes(attrs map[string]string) []string { stableKeys := map[string]struct{}{ "id": {}, "name": {}, "type": {}, "href": {}, "src": {}, "action": {}, "method": {}, "placeholder": {}, } var stableAttrs []string for key := range attrs { if _, exists := stableKeys[key]; exists { stableAttrs = append(stableAttrs, key) } } // Sort the attributes to ensure consistent order for hashing. sort.Strings(stableAttrs) return stableAttrs } type EventListener struct { Element *HTMLElement `json:"element,omitempty"` Type string `json:"type,omitempty"` Listener string `json:"listener,omitempty"` } // NavigationType represents the type of navigation type NavigationType string const ( // NavigationTypeForm represents a form navigation NavigationTypeForm NavigationType = "form" // NavigationTypeButton represents a button navigation NavigationTypeButton NavigationType = "button" // NavigationTypeLink represents a link navigation NavigationTypeLink NavigationType = "link" // NavigationTypeEventListener represents an event listener navigation NavigationTypeEventListener NavigationType = "eventlistener" ) ================================================ FILE: pkg/engine/hybrid/crawl.go ================================================ package hybrid import ( "bytes" "errors" "io" "net/http" "net/http/httputil" "net/url" "slices" "strings" "time" "github.com/PuerkitoBio/goquery" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/engine/common" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/utils" "github.com/projectdiscovery/retryablehttp-go" "github.com/projectdiscovery/utils/errkit" mapsutil "github.com/projectdiscovery/utils/maps" sliceutil "github.com/projectdiscovery/utils/slice" stringsutil "github.com/projectdiscovery/utils/strings" urlutil "github.com/projectdiscovery/utils/url" ) func (c *Crawler) navigateRequest(s *common.CrawlSession, request *navigation.Request) (*navigation.Response, error) { depth := request.Depth + 1 response := &navigation.Response{ Depth: depth, RootHostname: s.Hostname, } page, err := s.Browser.Page(proto.TargetCreateTarget{}) if err != nil { return nil, errkit.Wrap(err, "hybrid: could not create target") } defer func() { if err := page.Close(); err != nil { gologger.Error().Msgf("Error closing page: %v\n", err) } }() c.addHeadersToPage(page) pageRouter := NewHijack(page) pageRouter.SetPattern(&proto.FetchRequestPattern{ URLPattern: "*", RequestStage: proto.FetchRequestStageResponse, }) xhrRequests := []navigation.Request{} go pageRouter.Start(func(e *proto.FetchRequestPaused) error { URL, err := urlutil.Parse(e.Request.URL) if err != nil { return errkit.Wrap(err, "hybrid: could not parse URL") } body, _ := FetchGetResponseBody(page, e) headers := make(map[string][]string) for _, h := range e.ResponseHeaders { headers[h.Name] = []string{h.Value} } var ( statusCode int statucCodeText string ) if e.ResponseStatusCode != nil { statusCode = *e.ResponseStatusCode } if e.ResponseStatusText != "" { statucCodeText = e.ResponseStatusText } else { statucCodeText = http.StatusText(statusCode) } httpreq, err := http.NewRequest(e.Request.Method, URL.String(), strings.NewReader(e.Request.PostData)) if err != nil { return errkit.Wrap(err, "hybrid: could not new request") } // Note: headers are originally sent using `c.addHeadersToPage` below changes are done so that // headers are reflected in request dump // Headers, CustomHeaders, and Cookies are present in e.Request.Headers. We need to consider all of them and not only CustomHeaders // Otherwise, we will miss headers and output will be inconsistent if httpreq != nil { for k, v := range e.Request.Headers { httpreq.Header.Set(k, v.String()) } } httpresp := &http.Response{ Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1, StatusCode: statusCode, Status: statucCodeText, Header: headers, Body: io.NopCloser(bytes.NewReader(body)), Request: httpreq, ContentLength: int64(len(body)), } var rawBytesRequest, rawBytesResponse []byte if r, err := retryablehttp.FromRequest(httpreq); err == nil { rawBytesRequest, _ = r.Dump() } else { rawBytesRequest, _ = httputil.DumpRequestOut(httpreq, true) } rawBytesResponse, _ = httputil.DumpResponse(httpresp, true) bodyReader, _ := goquery.NewDocumentFromReader(bytes.NewReader(body)) var technologies map[string]interface{} if c.Options.Wappalyzer != nil { fingerprints := c.Options.Wappalyzer.Fingerprint(headers, body) technologies = make(map[string]interface{}, len(fingerprints)) for k := range fingerprints { technologies[k] = struct{}{} } } resp := &navigation.Response{ Resp: httpresp, Body: string(body), Reader: bodyReader, Depth: depth, RootHostname: s.Hostname, Technologies: mapsutil.GetKeys(technologies), StatusCode: statusCode, Headers: utils.FlattenHeaders(headers), Raw: string(rawBytesResponse), ContentLength: httpresp.ContentLength, KnowledgeBase: c.Options.ClassifyPage(string(body)), } response.ContentLength = resp.ContentLength requestHeaders := make(map[string][]string) for name, value := range e.Request.Headers { requestHeaders[name] = []string{value.Str()} } shouldCapture := func(xhrExtraction bool) bool { resourceTypes := []proto.NetworkResourceType{ proto.NetworkResourceTypeXHR, proto.NetworkResourceTypeFetch, proto.NetworkResourceTypeScript, } return xhrExtraction && slices.Contains(resourceTypes, e.ResourceType) } if shouldCapture(c.Options.Options.XhrExtraction) { networkReq := navigation.Request{ URL: httpreq.URL.String(), Method: httpreq.Method, Body: e.Request.PostData, } if len(httpreq.Header) > 0 { networkReq.Headers = utils.FlattenHeaders(httpreq.Header) } else { networkReq.Headers = utils.FlattenHeaders(requestHeaders) } xhrRequests = append(xhrRequests, networkReq) } // trim trailing / normalizedheadlessURL := strings.TrimSuffix(e.Request.URL, "/") matchOriginalURL := stringsutil.EqualFoldAny(request.URL, e.Request.URL, normalizedheadlessURL) if matchOriginalURL { request.Raw = string(rawBytesRequest) response = resp } // process the raw response navigationRequests := c.Options.Parser.ParseResponse(resp) c.Enqueue(s.Queue, navigationRequests...) // do not continue following the request if it's a redirect and redirects are disabled if c.Options.Options.DisableRedirects && resp.IsRedirect() { return nil } return FetchContinueRequest(page, e) })() //nolint defer func() { if err := pageRouter.Stop(); err != nil { gologger.Warning().Msgf("%s\n", err) } }() timeout := time.Duration(c.Options.Options.Timeout) * time.Second page = page.Timeout(timeout) navigatedURLs := sliceutil.NewSyncSlice[string]() navigatedURLs.Append(request.URL) pageCtx, cancelPageEvents := page.WithCancel() defer cancelPageEvents() waitFrameEvents := pageCtx.EachEvent(func(e *proto.PageFrameNavigated) { if e.Frame.ParentID == "" { frameURL := e.Frame.URL if frameURL != "" && frameURL != request.URL { navigatedURLs.Append(frameURL) } } }) go waitFrameEvents() // wait the page to be fully loaded and becoming idle waitNavigation := page.WaitNavigation(proto.PageLifecycleEventNameFirstMeaningfulPaint) err = page.Navigate(request.URL) if err != nil { if c.Options.Options.DisableRedirects && response.IsRedirect() { return response, nil } return nil, errkit.Wrap(err, "hybrid: could not navigate target") } waitNavigation() // Wait the page to be stable a duration timeStable := time.Duration(c.Options.Options.TimeStable) * time.Second if timeout < timeStable { gologger.Warning().Msgf("timeout is less than time stable, setting time stable to half of timeout to avoid timeout\n") timeStable = timeout / 2 gologger.Warning().Msgf("setting time stable to %s\n", timeStable) } if err := page.WaitStable(timeStable); err != nil { gologger.Warning().Msgf("could not wait for page to be stable: %s\n", err) } // simulate clicks on links with onclick handlers to discover JS redirects time.Sleep(200 * time.Millisecond) clickableLinks, err := page.Elements("a[onclick]") if err == nil && len(clickableLinks) > 0 { maxLinks := c.Options.Options.MaxOnclickLinks linksToProcess := len(clickableLinks) if linksToProcess > maxLinks { linksToProcess = maxLinks } gologger.Debug().Msgf("Found %d clickable links with onclick handlers, processing %d", len(clickableLinks), linksToProcess) for idx := 0; idx < linksToProcess; idx++ { link := clickableLinks[idx] beforeURL, err := page.Info() if err != nil { gologger.Error().Msgf("Could not get page info: %v", err) continue } beforeURLStr := "" if beforeURL != nil { beforeURLStr = beforeURL.URL } // try to click the link using rod's Click method clickErr := link.Click(proto.InputMouseButtonLeft, 1) if clickErr != nil { gologger.Debug().Msgf("Could not click link %d: %v", idx, clickErr) continue } gologger.Debug().Msgf("Clicked onclick link [%d] at URL: %s", idx, beforeURLStr) time.Sleep(1 * time.Second) // check if URL changed (indicates redirect occurred) currentURL, _ := page.Info() if currentURL != nil && currentURL.URL != beforeURLStr { gologger.Debug().Msgf("detected navigation to: %s", currentURL.URL) navigatedURLs.Append(currentURL.URL) if navErr := page.Navigate(request.URL); navErr != nil { gologger.Warning().Msgf("Failed to navigate back to %s after onclick redirect: %v", request.URL, navErr) if reloadErr := page.Reload(); reloadErr != nil { gologger.Error().Msgf("Failed to reload page after navigation error: %v", reloadErr) break } } time.Sleep(500 * time.Millisecond) } } } var getDocumentDepth = int(-1) getDocument := &proto.DOMGetDocument{Depth: &getDocumentDepth, Pierce: true} result, err := getDocument.Call(page) if err != nil { return nil, errkit.Wrap(err, "hybrid: could not get dom") } var builder strings.Builder traverseDOMNode(result.Root, &builder) body, err := page.HTML() if err != nil { return nil, errkit.Wrap(err, "hybrid: could not get html") } parsed, err := urlutil.Parse(request.URL) if err != nil { return nil, errkit.Wrap(err, "hybrid: url could not be parsed") } if response == nil || response.Resp == nil { // err is guaranteed to be nil, due to previous checks. return nil, errors.New("hybrid: response is nil") } response.Resp.Request.URL = parsed.URL // Create a copy of intrapolated shadow DOM elements and parse them separately responseCopy := *response responseCopy.Body = builder.String() responseCopy.Reader, _ = goquery.NewDocumentFromReader(strings.NewReader(responseCopy.Body)) if responseCopy.Reader != nil { navigationRequests := c.Options.Parser.ParseResponse(&responseCopy) c.Enqueue(s.Queue, navigationRequests...) } response.Body = body if response.Reader != nil { response.Reader.Url, _ = url.Parse(request.URL) if c.Options.Options.FormExtraction { response.Forms = append(response.Forms, utils.ParseFormFields(response.Reader)...) } } response.Reader, err = goquery.NewDocumentFromReader(strings.NewReader(response.Body)) if err != nil { return nil, errkit.Wrap(err, "hybrid: could not parse html") } response.XhrRequests = xhrRequests // enqueue JS-triggered navigation URLs that were detected navigatedURLs.Each(func(i int, navURL string) error { if navURL != request.URL { parsed, err := urlutil.Parse(navURL) if err == nil { navReq := &navigation.Request{ URL: parsed.String(), Depth: depth, RootHostname: s.Hostname, } c.Enqueue(s.Queue, navReq) gologger.Debug().Msgf("enqueued JS navigation: %s", navURL) } } return nil }) return response, nil } func (c *Crawler) addHeadersToPage(page *rod.Page) { if len(c.Headers) == 0 { return } var arr []string for k, v := range c.Headers { switch { case stringsutil.EqualFoldAny(k, "User-Agent"): userAgentParams := &proto.NetworkSetUserAgentOverride{ UserAgent: v, } if err := page.SetUserAgent(userAgentParams); err != nil { gologger.Error().Msgf("headless: could not set user agent: %v", err) } default: arr = append(arr, k, v) } } if len(arr) > 0 { _, err := page.SetExtraHeaders(arr) if err != nil { gologger.Error().Msgf("headless: could not set extra headers: %v", err) } } } // traverseDOMNode performs traversal of node completely building a pseudo-HTML // from it including the Shadow DOM, Pseudo elements and other children. // // TODO: Remove this method when we implement human-like browser navigation // which will anyway use browser APIs to find elements instead of goquery // where they will have shadow DOM information. func traverseDOMNode(node *proto.DOMNode, builder *strings.Builder) { buildDOMFromNode(node, builder) if node.TemplateContent != nil { traverseDOMNode(node.TemplateContent, builder) } if node.ContentDocument != nil { traverseDOMNode(node.ContentDocument, builder) } for _, children := range node.Children { traverseDOMNode(children, builder) } for _, shadow := range node.ShadowRoots { traverseDOMNode(shadow, builder) } for _, pseudo := range node.PseudoElements { traverseDOMNode(pseudo, builder) } } const ( elementNode = 1 ) var knownElements = map[string]struct{}{ "a": {}, "applet": {}, "area": {}, "audio": {}, "base": {}, "blockquote": {}, "body": {}, "button": {}, "embed": {}, "form": {}, "frame": {}, "html": {}, "iframe": {}, "img": {}, "import": {}, "input": {}, "isindex": {}, "link": {}, "meta": {}, "object": {}, "script": {}, "svg": {}, "table": {}, "video": {}, } func buildDOMFromNode(node *proto.DOMNode, builder *strings.Builder) { if node.NodeType != elementNode { return } if _, ok := knownElements[node.LocalName]; !ok { return } builder.WriteRune('<') builder.WriteString(node.LocalName) builder.WriteRune(' ') if len(node.Attributes) > 0 { for i := 0; i < len(node.Attributes); i = i + 2 { builder.WriteString(node.Attributes[i]) builder.WriteRune('=') builder.WriteString("\"") builder.WriteString(node.Attributes[i+1]) builder.WriteString("\"") builder.WriteRune(' ') } } builder.WriteRune('>') builder.WriteString("') } ================================================ FILE: pkg/engine/hybrid/doc.go ================================================ // Package hybrid implements the functionality for a hybrid-headless crawler. // It uses both headless browser and net/http for making requests, and goquery for processing rawand dom-rendered web page HTML. package hybrid ================================================ FILE: pkg/engine/hybrid/hijack.go ================================================ package hybrid import ( "encoding/base64" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/proto" ) // NewHijack create hijack from page. func NewHijack(page *rod.Page) *Hijack { return &Hijack{ page: page, disable: &proto.FetchDisable{}, } } // HijackHandler type type HijackHandler = func(e *proto.FetchRequestPaused) error // Hijack is a hijack handler type Hijack struct { page *rod.Page enable *proto.FetchEnable disable *proto.FetchDisable cancel func() } // SetPattern set pattern directly func (h *Hijack) SetPattern(pattern *proto.FetchRequestPattern) { h.enable = &proto.FetchEnable{ Patterns: []*proto.FetchRequestPattern{pattern}, } } // Start hijack. func (h *Hijack) Start(handler HijackHandler) func() error { if h.enable == nil { panic("hijack pattern not set") } p, cancel := h.page.WithCancel() h.cancel = cancel err := h.enable.Call(p) if err != nil { return func() error { return err } } wait := p.EachEvent(func(e *proto.FetchRequestPaused) { if handler != nil { err = handler(e) } }) return func() error { wait() return err } } // Stop func (h *Hijack) Stop() error { if h.cancel != nil { h.cancel() } return h.disable.Call(h.page) } // FetchGetResponseBody get request body. func FetchGetResponseBody(page *rod.Page, e *proto.FetchRequestPaused) ([]byte, error) { m := proto.FetchGetResponseBody{ RequestID: e.RequestID, } r, err := m.Call(page) if err != nil { return nil, err } if !r.Base64Encoded { return []byte(r.Body), nil } bs, err := base64.StdEncoding.DecodeString(r.Body) if err != nil { return nil, err } return bs, nil } // FetchContinueRequest continue request func FetchContinueRequest(page *rod.Page, e *proto.FetchRequestPaused) error { m := proto.FetchContinueRequest{ RequestID: e.RequestID, } return m.Call(page) } ================================================ FILE: pkg/engine/hybrid/hybrid.go ================================================ package hybrid import ( "fmt" "os" "time" "github.com/go-rod/rod" "github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/launcher/flags" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/engine/common" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/types" "github.com/projectdiscovery/katana/pkg/utils" "github.com/projectdiscovery/utils/errkit" urlutil "github.com/projectdiscovery/utils/url" ) // Crawler is a standard crawler instance type Crawler struct { *common.Shared browser *rod.Browser // TODO: Remove the Chrome PID kill code in favor of using Leakless(true). // This change will be made if there are no complaints about zombie Chrome processes. // References: // https://github.com/projectdiscovery/katana/issues/632 // https://github.com/projectdiscovery/httpx/issues/1425 // previousPIDs map[int32]struct{} // track already running PIDs tempDir string } // New returns a new standard crawler instance func New(options *types.CrawlerOptions) (*Crawler, error) { var dataStore string var err error if options.Options.ChromeDataDir != "" { dataStore = options.Options.ChromeDataDir } else { dataStore, err = os.MkdirTemp("", "katana-*") if err != nil { return nil, errkit.Wrap(err, "hybrid: could not create temporary directory") } } // previousPIDs := processutil.FindProcesses(processutil.IsChromeProcess) var launcherURL string var chromeLauncher *launcher.Launcher if options.Options.ChromeWSUrl != "" { launcherURL = options.Options.ChromeWSUrl } else { // create new chrome launcher instance chromeLauncher, err = buildChromeLauncher(options, dataStore) if err != nil { return nil, err } // launch chrome headless process launcherURL, err = chromeLauncher.Launch() if err != nil { return nil, err } } browser := rod.New().ControlURL(launcherURL) if browserErr := browser.Connect(); browserErr != nil { return nil, errkit.Wrap(browserErr, fmt.Sprintf("hybrid: failed to connect to chrome instance at %s", launcherURL)) } // create a new browser instance (default to incognito mode) if !options.Options.HeadlessNoIncognito { incognito, err := browser.Incognito() if err != nil { if chromeLauncher != nil { chromeLauncher.Kill() } return nil, errkit.Wrap(err, "hybrid: failed to create incognito browser") } browser = incognito } shared, err := common.NewShared(options) if err != nil { return nil, errkit.Wrap(err, "hybrid") } crawler := &Crawler{ Shared: shared, browser: browser, // previousPIDs: previousPIDs, tempDir: dataStore, } return crawler, nil } // Close closes the crawler process func (c *Crawler) Close() error { if c.Options.Options.ChromeDataDir == "" { if err := os.RemoveAll(c.tempDir); err != nil { return err } } // processutil.CloseProcesses(processutil.IsChromeProcess, c.previousPIDs) return nil } // Crawl crawls a URL with the specified options func (c *Crawler) Crawl(rootURL string) error { crawlSession, err := c.NewCrawlSessionWithURL(rootURL) if err != nil { return errkit.Wrap(err, "hybrid") } crawlSession.Browser = c.browser defer crawlSession.CancelFunc() gologger.Info().Msgf("Started headless crawling for => %v", rootURL) if err := c.Do(crawlSession, c.navigateRequest); err != nil { return errkit.Wrap(err, "hybrid") } return nil } // Do executes the crawling loop with browser-safe concurrency. // Unlike the base implementation, this uses sequential processing (concurrency=1) // because Chrome DevTools Protocol operations cannot safely run concurrently // on the same browser instance. Multiple concurrent page operations cause // race conditions, navigation conflicts, and network interception issues. func (c *Crawler) Do(crawlSession *common.CrawlSession, doRequest common.DoRequestFunc) error { for item := range crawlSession.Queue.Pop() { if ctxErr := crawlSession.Ctx.Err(); ctxErr != nil { return ctxErr } req, ok := item.(*navigation.Request) if !ok { continue } if !utils.IsURL(req.URL) { if c.Options.Options.OnSkipURL != nil { c.Options.Options.OnSkipURL(req.URL) } gologger.Debug().Msgf("`%v` not a url. skipping", req.URL) continue } if !c.Options.ValidatePath(req.URL) { gologger.Debug().Msgf("`%v` filtered path. skipping", req.URL) continue } inScope, scopeErr := c.Options.ValidateScope(req.URL, crawlSession.Hostname) if scopeErr != nil { gologger.Debug().Msgf("Error validating scope for `%v`: %v. skipping", req.URL, scopeErr) continue } if !req.SkipValidation && !inScope { gologger.Debug().Msgf("`%v` not in scope. skipping", req.URL) continue } c.Options.RateLimit.Take() if c.Options.Options.Delay > 0 { time.Sleep(time.Duration(c.Options.Options.Delay) * time.Second) } resp, err := doRequest(crawlSession, req) if inScope { c.Output(req, resp, err) } if err != nil { gologger.Warning().Msgf("Could not request seed URL %s: %s\n", req.URL, err) outputError := &output.Error{ Timestamp: time.Now(), Endpoint: req.RequestURL(), Source: req.Source, Error: err.Error(), } _ = c.Options.OutputWriter.WriteErr(outputError) continue } if resp == nil || resp.Resp == nil || resp.Reader == nil { continue } if c.Options.Options.DisableRedirects && resp.IsRedirect() { continue } navigationRequests := c.Options.Parser.ParseResponse(resp) c.Enqueue(crawlSession.Queue, navigationRequests...) } return nil } // buildChromeLauncher builds a new chrome launcher instance func buildChromeLauncher(options *types.CrawlerOptions, dataStore string) (*launcher.Launcher, error) { chromeLauncher := launcher.New(). Leakless(true). Set("disable-gpu", "true"). Set("ignore-certificate-errors", "true"). Set("ignore-certificate-errors", "1"). Set("disable-crash-reporter", "true"). Set("disable-notifications", "true"). Set("hide-scrollbars", "true"). Set("window-size", fmt.Sprintf("%d,%d", 1080, 1920)). Set("mute-audio", "true"). Delete("use-mock-keychain"). UserDataDir(dataStore) if options.Options.UseInstalledChrome { if options.Options.SystemChromePath != "" { chromeLauncher.Bin(options.Options.SystemChromePath) } else { if chromePath, hasChrome := launcher.LookPath(); hasChrome { chromeLauncher.Bin(chromePath) } else { return nil, errkit.New("hybrid: the chrome browser is not installed") } } } if options.Options.SystemChromePath != "" { chromeLauncher.Bin(options.Options.SystemChromePath) } if options.Options.ShowBrowser { chromeLauncher = chromeLauncher.Headless(false) } else { chromeLauncher = chromeLauncher.Headless(true) } if options.Options.HeadlessNoSandbox { chromeLauncher.Set("no-sandbox", "true") } if options.Options.Proxy != "" && options.Options.Headless { proxyURL, err := urlutil.Parse(options.Options.Proxy) if err != nil { return nil, err } chromeLauncher.Set("proxy-server", proxyURL.String()) } for k, v := range options.Options.ParseHeadlessOptionalArguments() { chromeLauncher.Set(flags.Flag(k), v) } return chromeLauncher, nil } ================================================ FILE: pkg/engine/parser/files/request.go ================================================ package files import ( "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/retryablehttp-go" ) type visitFunc func(URL string) ([]*navigation.Request, error) type KnownFiles struct { parsers []visitFunc httpclient *retryablehttp.Client } // New returns a new known files parser instance func New(httpclient *retryablehttp.Client, files string) *KnownFiles { parser := &KnownFiles{ httpclient: httpclient, } switch files { case "robotstxt": crawler := &robotsTxtCrawler{httpclient: httpclient} parser.parsers = append(parser.parsers, crawler.Visit) case "sitemapxml": crawler := &sitemapXmlCrawler{httpclient: httpclient} parser.parsers = append(parser.parsers, crawler.Visit) default: crawler := &robotsTxtCrawler{httpclient: httpclient} parser.parsers = append(parser.parsers, crawler.Visit) another := &sitemapXmlCrawler{httpclient: httpclient} parser.parsers = append(parser.parsers, another.Visit) } return parser } // Request requests all known files with visitors func (k *KnownFiles) Request(URL string) (navigationRequests []*navigation.Request, err error) { for _, visitor := range k.parsers { navRequests, err := visitor(URL) if err != nil { return navigationRequests, err } navigationRequests = append(navigationRequests, navRequests...) } return } ================================================ FILE: pkg/engine/parser/files/robotstxt.go ================================================ package files import ( "bufio" "fmt" "io" "net/http" "strings" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/utils" "github.com/projectdiscovery/retryablehttp-go" "github.com/projectdiscovery/utils/errkit" ) type robotsTxtCrawler struct { httpclient *retryablehttp.Client } // Visit visits the provided URL with file crawlers func (r *robotsTxtCrawler) Visit(URL string) ([]*navigation.Request, error) { URL = strings.TrimSuffix(URL, "/") requestURL := fmt.Sprintf("%s/robots.txt", URL) req, err := retryablehttp.NewRequest(http.MethodGet, requestURL, nil) if err != nil { return nil, errkit.Wrap(err, "robotscrawler: could not create request") } req.Header.Set("User-Agent", utils.WebUserAgent()) resp, err := r.httpclient.Do(req) if err != nil { return nil, errkit.Wrap(err, "robotscrawler: could not do request") } defer func() { if err := resp.Body.Close(); err != nil { gologger.Error().Msgf("Error closing response body: %v\n", err) } }() return r.parseReader(resp.Body, resp) } func (r *robotsTxtCrawler) parseReader(reader io.Reader, resp *http.Response) (navigationRequests []*navigation.Request, err error) { scanner := bufio.NewScanner(reader) for scanner.Scan() { text := scanner.Text() splitted := strings.SplitN(text, ": ", 2) if len(splitted) < 2 { continue } directive := strings.ToLower(splitted[0]) if strings.HasPrefix(directive, "allow") || strings.EqualFold(directive, "disallow") { navResp := &navigation.Response{ Depth: 2, Resp: resp, StatusCode: resp.StatusCode, Headers: utils.FlattenHeaders(resp.Header), } navRequest := navigation.NewNavigationRequestURLFromResponse(strings.Trim(splitted[1], " "), resp.Request.URL.String(), "file", "robotstxt", navResp) navigationRequests = append(navigationRequests, navRequest) } } return } ================================================ FILE: pkg/engine/parser/files/robotstxt_test.go ================================================ package files import ( "net/http" "strings" "testing" urlutil "github.com/projectdiscovery/utils/url" "github.com/stretchr/testify/require" ) func TestRobotsTxtParseReader(t *testing.T) { requests := []string{} crawler := &robotsTxtCrawler{} content := `User-agent: * Disallow: /test/misc/known-files/robots.txt.found User-agent: * Disallow: /test/includes/ # User-agent: Googlebot # Allow: /random/ Sitemap: https://example.com/sitemap.xml` parsed, err := urlutil.Parse("http://localhost/robots.txt") require.Nil(t, err) navigationRequests, err := crawler.parseReader(strings.NewReader(content), &http.Response{Request: &http.Request{URL: parsed.URL}}) require.Nil(t, err) for _, navReq := range navigationRequests { requests = append(requests, navReq.URL) } require.ElementsMatch(t, requests, []string{ "http://localhost/test/includes/", "http://localhost/test/misc/known-files/robots.txt.found", }, "could not get correct elements") } ================================================ FILE: pkg/engine/parser/files/sitemapxml.go ================================================ package files import ( "encoding/xml" "fmt" "io" "net/http" "strings" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/utils" "github.com/projectdiscovery/retryablehttp-go" "github.com/projectdiscovery/utils/errkit" ) type sitemapXmlCrawler struct { httpclient *retryablehttp.Client } // Visit visits the provided URL with file crawlers func (r *sitemapXmlCrawler) Visit(URL string) (navigationRequests []*navigation.Request, err error) { URL = strings.TrimSuffix(URL, "/") requestURL := fmt.Sprintf("%s/sitemap.xml", URL) req, err := retryablehttp.NewRequest(http.MethodGet, requestURL, nil) if err != nil { return nil, errkit.Wrap(err, "sitemapcrawler: could not create request") } req.Header.Set("User-Agent", utils.WebUserAgent()) resp, err := r.httpclient.Do(req) if err != nil { return nil, errkit.Wrap(err, "sitemapcrawler: could not do request") } defer func() { if err := resp.Body.Close(); err != nil { gologger.Error().Msgf("Error closing response body: %v\n", err) } }() navigationRequests, err = r.parseReader(resp.Body, resp) if err != nil { return nil, errkit.Wrap(err, "sitemapcrawler: could not parse sitemap") } return } type sitemapStruct struct { URLs []parsedURL `xml:"url"` Sitemap []parsedURL `xml:"sitemap"` } type parsedURL struct { Loc string `xml:"loc"` } func (r *sitemapXmlCrawler) parseReader(reader io.Reader, resp *http.Response) (navigationRequests []*navigation.Request, err error) { sitemap := sitemapStruct{} if err := xml.NewDecoder(reader).Decode(&sitemap); err != nil { return nil, errkit.Wrap(err, "sitemapcrawler: could not decode xml") } for _, url := range sitemap.URLs { navResp := &navigation.Response{ Depth: 2, Resp: resp, StatusCode: resp.StatusCode, Headers: utils.FlattenHeaders(resp.Header), } navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(strings.Trim(url.Loc, " \t\n"), resp.Request.URL.String(), "file", "sitemapxml", navResp)) } for _, url := range sitemap.Sitemap { navResp := &navigation.Response{ Depth: 2, Resp: resp, StatusCode: resp.StatusCode, Headers: utils.FlattenHeaders(resp.Header), } navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(strings.Trim(url.Loc, " \t\n"), resp.Request.URL.String(), "file", "sitemapxml", navResp)) } return } ================================================ FILE: pkg/engine/parser/files/sitemapxml_test.go ================================================ package files import ( "net/http" "strings" "testing" urlutil "github.com/projectdiscovery/utils/url" "github.com/stretchr/testify/require" ) func TestSitemapXmlParseReader(t *testing.T) { requests := []string{} crawler := &sitemapXmlCrawler{} content := ` http://security-crawl-maze.app/test/misc/known-files/sitemap.xml.found 2019-06-19T12:00:00+00:00 ` parsed, err := urlutil.Parse("http://security-crawl-maze.app/sitemap.xml") require.Nil(t, err) navigationRequests, err := crawler.parseReader(strings.NewReader(content), &http.Response{Request: &http.Request{URL: parsed.URL}}) require.Nil(t, err) for _, navReq := range navigationRequests { requests = append(requests, navReq.URL) } require.ElementsMatch(t, requests, []string{ "http://security-crawl-maze.app/test/misc/known-files/sitemap.xml.found", }, "could not get correct elements") } ================================================ FILE: pkg/engine/parser/parser.go ================================================ package parser import ( "mime/multipart" "net/http" "strings" "github.com/PuerkitoBio/goquery" "github.com/projectdiscovery/gologger" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/output" "github.com/projectdiscovery/katana/pkg/utils" stringsutil "github.com/projectdiscovery/utils/strings" urlutil "github.com/projectdiscovery/utils/url" "golang.org/x/net/html" ) // responseParserFunc is a function that parses the document returning // new navigation items or requests for the crawler. type ResponseParserFunc func(resp *navigation.Response) []*navigation.Request type Parser []responseParser type responseParserType int const ( headerParser responseParserType = iota + 1 bodyParser contentParser ) type responseParser struct { parserType responseParserType parserFunc ResponseParserFunc } func NewResponseParser() *Parser { return &Parser{ // Header based parsers {headerParser, headerContentLocationParser}, {headerParser, headerLinkParser}, {headerParser, headerRefreshParser}, // Body based parsers {bodyParser, bodyATagParser}, {bodyParser, bodyLinkHrefTagParser}, {bodyParser, bodyBackgroundTagParser}, {bodyParser, bodyAudioTagParser}, {bodyParser, bodyAppletTagParser}, {bodyParser, bodyImgTagParser}, {bodyParser, bodyObjectTagParser}, {bodyParser, bodySvgTagParser}, {bodyParser, bodyTableTagParser}, {bodyParser, bodyVideoTagParser}, {bodyParser, bodyButtonFormactionTagParser}, {bodyParser, bodyBlockquoteCiteTagParser}, {bodyParser, bodyFrameSrcTagParser}, {bodyParser, bodyMapAreaPingTagParser}, {bodyParser, bodyBaseHrefTagParser}, {bodyParser, bodyImportImplementationTagParser}, {bodyParser, bodyEmbedTagParser}, {bodyParser, bodyFrameTagParser}, {bodyParser, bodyIframeTagParser}, {bodyParser, bodyInputSrcTagParser}, {bodyParser, bodyIsindexActionTagParser}, {bodyParser, bodyScriptSrcTagParser}, {bodyParser, bodyMetaContentTagParser}, {bodyParser, bodyHtmlManifestTagParser}, {bodyParser, bodyHtmlDoctypeTagParser}, {bodyParser, bodyHtmxAttrParser}, // custom field regex parser {bodyParser, customFieldRegexParser}, } } // parseResponse runs the response parsers on the navigation response func (p *Parser) ParseResponse(resp *navigation.Response) (navigationRequests []*navigation.Request) { for _, parser := range *p { switch { case parser.parserType == headerParser && resp.Resp != nil: navigationRequests = appendFiltered(navigationRequests, parser.parserFunc(resp)) case parser.parserType == bodyParser && resp.Reader != nil: navigationRequests = appendFiltered(navigationRequests, parser.parserFunc(resp)) case parser.parserType == contentParser && len(resp.Body) > 0: navigationRequests = appendFiltered(navigationRequests, parser.parserFunc(resp)) } } return } // appendFiltered filters navigation requests and appends valid ones to the slice func appendFiltered(existing []*navigation.Request, new []*navigation.Request) []*navigation.Request { for _, req := range new { if isValidNavigationRequest(req) { existing = append(existing, req) } } return existing } func isValidNavigationRequest(req *navigation.Request) bool { if req == nil { return false } url := strings.TrimSpace(req.URL) if url == "" { return false } lc := strings.ToLower(url) return !strings.HasPrefix(lc, "data:") && !strings.HasPrefix(lc, "mailto:") && !strings.HasPrefix(lc, "javascript:") && !strings.HasPrefix(lc, "vbscript:") } // ------------------------------------------------------------------------- // Begin Header based parsers // ------------------------------------------------------------------------- // headerContentLocationParser parsers Content-Location header from response func headerContentLocationParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { header := resp.Resp.Header.Get("Content-Location") if header == "" { return } navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(header, resp.Resp.Request.URL.String(), "header", "content-location", resp)) return } // headerLinkParser parsers Link header from response func headerLinkParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { header := resp.Resp.Header.Get("Link") if header == "" { return } values := utils.ParseLinkTag(header) for _, value := range values { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(value, resp.Resp.Request.URL.String(), "header", "link", resp)) } return } // headerLocationParser parsers Location header from response func headerLocationParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { header := resp.Resp.Header.Get("Location") if header == "" { return } navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(header, resp.Resp.Request.URL.String(), "header", "location", resp)) return } // headerRefreshParser parsers Refresh header from response func headerRefreshParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { header := resp.Resp.Header.Get("Refresh") if header == "" { return } values := utils.ParseRefreshTag(header) if values == "" { return } navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(values, resp.Resp.Request.URL.String(), "header", "refresh", resp)) return } // ------------------------------------------------------------------------- // Begin Body based parsers // ------------------------------------------------------------------------- // bodyATagParser parses A tag from response func bodyATagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("a").Each(func(i int, item *goquery.Selection) { href, ok := item.Attr("href") if ok && href != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(href, resp.Resp.Request.URL.String(), "a", "href", resp)) } ping, ok := item.Attr("ping") if ok && ping != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(ping, resp.Resp.Request.URL.String(), "a", "ping", resp)) } }) return } // bodyLinkHrefTagParser parses link tag from response func bodyLinkHrefTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("link[href]").Each(func(i int, item *goquery.Selection) { href, ok := item.Attr("href") if ok && href != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(href, resp.Resp.Request.URL.String(), "link", "href", resp)) } }) return } // bodyEmbedTagParser parses Embed tag from response func bodyEmbedTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("embed[src]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "embed", "src", resp)) } }) return } // bodyFrameTagParser parses frame tag from response func bodyFrameTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("frame[src]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "frame", "src", resp)) } }) return } // bodyIframeTagParser parses iframe tag from response func bodyIframeTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("iframe").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "iframe", "src", resp)) } srcDoc, ok := item.Attr("srcdoc") if ok && srcDoc != "" { endpoints := utils.ExtractRelativeEndpoints(srcDoc) for _, item := range endpoints { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(item, resp.Resp.Request.URL.String(), "iframe", "srcdoc", resp)) } } }) return } // bodyInputSrcTagParser parses input image src tag from response func bodyInputSrcTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("input[type='image' i]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "input-image", "src", resp)) } }) return } // bodyIsindexActionTagParser parses isindex action tag from response func bodyIsindexActionTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("isindex[action]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("action") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "isindex", "action", resp)) } }) return } // bodyScriptSrcTagParser parses script src tag from response func bodyScriptSrcTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("script[src]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "script", "src", resp)) } }) return } // bodyBackgroundTagParser parses body background tag from response func bodyBackgroundTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("body[background]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("background") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "body", "background", resp)) } }) return } // bodyAudioTagParser parses body audio tag from response func bodyAudioTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("audio").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "audio", "src", resp)) } item.Find("source").Each(func(i int, s *goquery.Selection) { src, ok := s.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "audio", "source", resp)) } srcSet, ok := s.Attr("srcset") if ok && srcSet != "" { for _, value := range utils.ParseSRCSetTag(srcSet) { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(value, resp.Resp.Request.URL.String(), "audio", "sourcesrcset", resp)) } } }) }) return } // bodyAppletTagParser parses body applet tag from response func bodyAppletTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("applet").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("archive") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "applet", "archive", resp)) } srcCodebase, ok := item.Attr("codebase") if ok && srcCodebase != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcCodebase, resp.Resp.Request.URL.String(), "applet", "codebase", resp)) } }) return } // bodyImgTagParser parses Img tag from response func bodyImgTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("img").Each(func(i int, item *goquery.Selection) { srcDynsrc, ok := item.Attr("dynsrc") if ok && srcDynsrc != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcDynsrc, resp.Resp.Request.URL.String(), "img", "dynsrc", resp)) } srcLongdesc, ok := item.Attr("longdesc") if ok && srcLongdesc != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcLongdesc, resp.Resp.Request.URL.String(), "img", "longdesc", resp)) } srcLowsrc, ok := item.Attr("lowsrc") if ok && srcLowsrc != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcLowsrc, resp.Resp.Request.URL.String(), "img", "lowsrc", resp)) } src, ok := item.Attr("src") if ok && src != "" && src != "#" { if strings.HasPrefix(src, "data:") { // TODO: Add data:uri/data:image parsing return } navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "img", "src", resp)) } srcSet, ok := item.Attr("srcset") if ok && srcSet != "" { for _, value := range utils.ParseSRCSetTag(srcSet) { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(value, resp.Resp.Request.URL.String(), "img", "srcset", resp)) } } }) return } // bodyObjectTagParser parses object tag from response func bodyObjectTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("object").Each(func(i int, item *goquery.Selection) { srcData, ok := item.Attr("data") if ok && srcData != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcData, resp.Resp.Request.URL.String(), "src", "data", resp)) } srcCodebase, ok := item.Attr("codebase") if ok && srcCodebase != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcCodebase, resp.Resp.Request.URL.String(), "src", "codebase", resp)) } item.Find("param").Each(func(i int, s *goquery.Selection) { srcValue, ok := s.Attr("value") if ok && srcValue != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcValue, resp.Resp.Request.URL.String(), "src", "value", resp)) } }) }) return } // bodySvgTagParser parses svg tag from response func bodySvgTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("svg").Each(func(i int, item *goquery.Selection) { item.Find("image").Each(func(i int, s *goquery.Selection) { hrefData, ok := s.Attr("href") if ok && hrefData != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(hrefData, resp.Resp.Request.URL.String(), "svg", "image-href", resp)) } }) item.Find("script").Each(func(i int, s *goquery.Selection) { hrefData, ok := s.Attr("href") if ok && hrefData != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(hrefData, resp.Resp.Request.URL.String(), "svg", "script-href", resp)) } }) }) return } // bodyTableTagParser parses table tag from response func bodyTableTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("table").Each(func(i int, item *goquery.Selection) { srcData, ok := item.Attr("background") if ok && srcData != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcData, resp.Resp.Request.URL.String(), "table", "background", resp)) } item.Find("td").Each(func(i int, s *goquery.Selection) { srcValue, ok := s.Attr("background") if ok && srcValue != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcValue, resp.Resp.Request.URL.String(), "table", "td-background", resp)) } }) }) return } // bodyVideoTagParser parses video tag from response func bodyVideoTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("video").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "video", "src", resp)) } srcData, ok := item.Attr("poster") if ok && srcData != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcData, resp.Resp.Request.URL.String(), "video", "poster", resp)) } item.Find("track").Each(func(i int, s *goquery.Selection) { srcValue, ok := s.Attr("src") if ok && srcValue != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(srcValue, resp.Resp.Request.URL.String(), "video", "track-src", resp)) } }) }) return } // bodyBlockquoteCiteTagParser parses blockquote cite tag from response func bodyBlockquoteCiteTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("blockquote[cite]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("cite") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "blockquote", "cite", resp)) } }) return } // bodyFrameSrcTagParser parses frame src tag from response func bodyFrameSrcTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("frame[src]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("src") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "frame", "src", resp)) } }) return } // bodyMapAreaPingTagParser parses map area ping tag from response func bodyMapAreaPingTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("area[ping]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("ping") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "area", "ping", resp)) } }) return } // bodyBaseHrefTagParser parses base href tag from response func bodyBaseHrefTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("base[href]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("href") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "base", "href", resp)) } }) return } // bodyImportImplementationTagParser parses import implementation tag from response func bodyImportImplementationTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("import[implementation]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("implementation") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "import", "implementation", resp)) } }) return } // bodyButtonFormactionTagParser parses button formaction tag from response func bodyButtonFormactionTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("button[formaction]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("formaction") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "button", "formaction", resp)) } }) return } // bodyHtmlManifestTagParser parses body manifest tag from response func bodyHtmlManifestTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("html[manifest]").Each(func(i int, item *goquery.Selection) { src, ok := item.Attr("manifest") if ok && src != "" { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(src, resp.Resp.Request.URL.String(), "html", "manifest", resp)) } }) return } // bodyHtmlDoctypeTagParser parses body doctype tag from response func bodyHtmlDoctypeTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { if len(resp.Reader.Nodes) < 1 || resp.Reader.Nodes[0].FirstChild == nil { return } docTypeNode := resp.Reader.Nodes[0].FirstChild if docTypeNode.Type != html.DoctypeNode { return } if len(docTypeNode.Attr) == 0 || strings.ToLower(docTypeNode.Attr[0].Key) != "system" { return } navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(docTypeNode.Attr[0].Val, resp.Resp.Request.URL.String(), "html", "doctype", resp)) return } // bodyFormTagParser parses forms from response func bodyFormTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("form").Each(func(i int, item *goquery.Selection) { href, _ := item.Attr("action") encType, ok := item.Attr("enctype") if !ok || encType == "" { encType = "application/x-www-form-urlencoded" } method, _ := item.Attr("method") if method == "" { method = "GET" } method = strings.ToUpper(method) actionURL := resp.AbsoluteURL(href) if actionURL == "" { return } parsed, err := urlutil.Parse(actionURL) if err != nil { gologger.Warning().Msgf("bodyFormTagParser :failed to parse url %v got %v", actionURL, err) return } isMultipartForm := strings.HasPrefix(encType, "multipart/") queryValuesWriter := urlutil.NewOrderedParams() queryValuesWriter.IncludeEquals = true var sb strings.Builder var multipartWriter *multipart.Writer if isMultipartForm { multipartWriter = multipart.NewWriter(&sb) } // Get the form field suggestions for all elements in the form formFields := []interface{}{} item.Find("input, select, textarea").Each(func(index int, item *goquery.Selection) { if len(item.Nodes) == 0 { return } formFields = append(formFields, utils.ConvertGoquerySelectionToFormField(item)) }) dataMap := utils.FormFillSuggestions(formFields) dataMap.Iterate(func(key, value string) bool { if key == "" { return true } if isMultipartForm { _ = multipartWriter.WriteField(key, value) } else { queryValuesWriter.Set(key, value) } return true }) // Guess content-type var contentType string if multipartWriter != nil { _ = multipartWriter.Close() contentType = multipartWriter.FormDataContentType() } else { contentType = encType } req := &navigation.Request{ Method: method, URL: actionURL, Depth: resp.Depth, RootHostname: resp.RootHostname, Tag: "form", Attribute: "action", Source: resp.Resp.Request.URL.String(), } switch method { case "GET": parsed.Params.Merge(queryValuesWriter.Encode()) req.URL = parsed.String() case "POST": if multipartWriter != nil { req.Body = sb.String() } else { req.Body = queryValuesWriter.Encode() } req.Headers = make(map[string]string) req.Headers["Content-Type"] = contentType } navigationRequests = append(navigationRequests, req) }) return } // bodyMetaContentTagParser parses meta content tag from response func bodyMetaContentTagParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("meta").Each(func(i int, item *goquery.Selection) { header, ok := item.Attr("content") if !ok { return } extracted := utils.ExtractRelativeEndpoints(header) for _, item := range extracted { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(item, resp.Resp.Request.URL.String(), "meta", "refresh", resp)) } }) return } func bodyHtmxAttrParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { // exclude hx-delete resp.Reader.Find("[hx-get],[hx-post],[hx-put],[hx-patch]").Each(func(i int, item *goquery.Selection) { req := &navigation.Request{ RootHostname: resp.RootHostname, Depth: resp.Depth, Source: resp.Resp.Request.URL.String(), Tag: "htmx", } if hxGet, ok := item.Attr("hx-get"); ok && hxGet != "" { req.Method = http.MethodGet req.URL = resp.AbsoluteURL(hxGet) req.Attribute = "hx-get" navigationRequests = append(navigationRequests, req) } if hxPost, ok := item.Attr(("hx-post")); ok && hxPost != "" { req.Method = http.MethodPost req.URL = resp.AbsoluteURL(hxPost) req.Attribute = "hx-post" navigationRequests = append(navigationRequests, req) } if hxPut, ok := item.Attr(("hx-put")); ok && hxPut != "" { req.Method = http.MethodPut req.URL = resp.AbsoluteURL(hxPut) req.Attribute = "hx-put" navigationRequests = append(navigationRequests, req) } if hxPatch, ok := item.Attr(("hx-patch")); ok && hxPatch != "" { req.Method = http.MethodPatch req.URL = resp.AbsoluteURL(hxPatch) req.Attribute = "hx-patch" navigationRequests = append(navigationRequests, req) } }) return } // ------------------------------------------------------------------------- // Begin JS Regex based parsers // ------------------------------------------------------------------------- // scriptContentRegexParser parses script content endpoints from response func scriptContentRegexParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("script").Each(func(i int, item *goquery.Selection) { text := item.Text() if text == "" { return } endpoints := utils.ExtractRelativeEndpoints(text) for _, item := range endpoints { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(item, resp.Resp.Request.URL.String(), "script", "text", resp)) } }) return } // scriptJSFileRegexParser parses relative endpoints from js file pages func scriptJSFileRegexParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { // Only process javascript file based on path or content type // CSS, JS are supported for relative endpoint extraction. contentType := resp.Resp.Header.Get("Content-Type") if !stringsutil.HasSuffixAny(resp.Resp.Request.URL.Path, ".js", ".css") && !strings.Contains(contentType, "/javascript") { return } endpointsItems := utils.ExtractRelativeEndpoints(string(resp.Body)) for _, item := range endpointsItems { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(item, resp.Resp.Request.URL.String(), "js", "regex", resp)) } return } // bodyScrapeEndpointsParser parses scraped URLs from HTML body func bodyScrapeEndpointsParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { endpoints := utils.ExtractBodyEndpoints(string(resp.Body)) for _, item := range endpoints { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(item, resp.Resp.Request.URL.String(), "html", "regex", resp)) } return } // customFieldRegexParser parses custom regex from HTML body and header func customFieldRegexParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { var customField = make(map[string][]string) for _, v := range output.CustomFieldsMap { results := []string{} for _, re := range v.CompileRegex { matches := [][]string{} // read body if v.Part == output.Body.ToString() || v.Part == output.Response.ToString() { matches = re.FindAllStringSubmatch(string(resp.Body), -1) } // read header if v.Part == output.Header.ToString() || v.Part == output.Response.ToString() { for key, v := range resp.Resp.Header { header := key + ": " + strings.Join(v, "\n") headerMatches := re.FindAllStringSubmatch(header, -1) matches = append(matches, headerMatches...) } } for _, match := range matches { if len(match) < (v.Group + 1) { continue } matchString := match[v.Group] results = append(results, matchString) } } if len(results) > 0 { customField[v.GetName()] = results } } if len(customField) != 0 { navigationRequests = append(navigationRequests, &navigation.Request{ Method: "GET", URL: resp.Resp.Request.URL.String(), Depth: resp.Depth, CustomFields: customField, }) } return } ================================================ FILE: pkg/engine/parser/parser_generic.go ================================================ //go:build !(386 || windows) package parser import ( "fmt" "strings" "github.com/PuerkitoBio/goquery" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/utils" stringsutil "github.com/projectdiscovery/utils/strings" ) type Options struct { AutomaticFormFill bool ScrapeJSLuiceResponses bool ScrapeJSResponses bool DisableRedirects bool } func (p *Parser) InitWithOptions(options *Options) { if options.AutomaticFormFill { *p = append(*p, responseParser{bodyParser, bodyFormTagParser}) } if options.ScrapeJSLuiceResponses { *p = append(*p, responseParser{bodyParser, scriptContentJsluiceParser}) *p = append(*p, responseParser{contentParser, scriptJSFileJsluiceParser}) } if options.ScrapeJSResponses { *p = append(*p, responseParser{bodyParser, scriptContentRegexParser}) *p = append(*p, responseParser{contentParser, scriptJSFileRegexParser}) *p = append(*p, responseParser{contentParser, bodyScrapeEndpointsParser}) } if !options.DisableRedirects { *p = append(*p, responseParser{headerParser, headerLocationParser}) } } // scriptContentJsluiceParser parses script content endpoints using jsluice from response func scriptContentJsluiceParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { resp.Reader.Find("script").Each(func(i int, item *goquery.Selection) { text := item.Text() if text == "" { return } endpointItems := utils.ExtractJsluiceEndpoints(text) for _, item := range endpointItems { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(item.Endpoint, resp.Resp.Request.URL.String(), "script", fmt.Sprintf("jsluice-%s", item.Type), resp)) } }) return } // scriptJSFileJsluiceParser parses endpoints using jsluice from js file pages func scriptJSFileJsluiceParser(resp *navigation.Response) (navigationRequests []*navigation.Request) { // Only process javascript file based on path or content type // CSS, JS are supported for relative endpoint extraction. contentType := resp.Resp.Header.Get("Content-Type") if !stringsutil.HasSuffixAny(resp.Resp.Request.URL.Path, ".js", ".css") && !strings.Contains(contentType, "/javascript") { return } // Skip common js libraries if utils.IsPathCommonJSLibraryFile(resp.Resp.Request.URL.Path) { return } endpointsItems := utils.ExtractJsluiceEndpoints(string(resp.Body)) for _, item := range endpointsItems { navigationRequests = append(navigationRequests, navigation.NewNavigationRequestURLFromResponse(item.Endpoint, resp.Resp.Request.URL.String(), "js", fmt.Sprintf("jsluice-%s", item.Type), resp)) } return } ================================================ FILE: pkg/engine/parser/parser_nojs.go ================================================ //go:build windows || 386 package parser type Options struct { AutomaticFormFill bool ScrapeJSLuiceResponses bool ScrapeJSResponses bool DisableRedirects bool } func (p *Parser) InitWithOptions(options *Options) { if options.AutomaticFormFill { *p = append(*p, responseParser{bodyParser, bodyFormTagParser}) } if options.ScrapeJSResponses { *p = append(*p, responseParser{bodyParser, scriptContentRegexParser}) *p = append(*p, responseParser{contentParser, scriptJSFileRegexParser}) *p = append(*p, responseParser{contentParser, bodyScrapeEndpointsParser}) } if !options.DisableRedirects { *p = append(*p, responseParser{headerParser, headerLocationParser}) } } ================================================ FILE: pkg/engine/parser/parser_test.go ================================================ package parser import ( "net/http" "regexp" "strings" "testing" "github.com/PuerkitoBio/goquery" "github.com/projectdiscovery/katana/pkg/navigation" "github.com/projectdiscovery/katana/pkg/output" urlutil "github.com/projectdiscovery/utils/url" "github.com/stretchr/testify/require" ) func TestHeaderParsers(t *testing.T) { parsed, _ := urlutil.Parse("https://security-crawl-maze.app/headers/xyz/") t.Run("content-location", func(t *testing.T) { resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}, Header: http.Header{"Content-Location": []string{"/test/headers/content-location.found"}}}} navigationRequests := headerContentLocationParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/headers/content-location.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("link", func(t *testing.T) { resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}, Header: http.Header{"Link": []string{"; rel=\"preload\""}}}} navigationRequests := headerLinkParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/headers/link.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("location", func(t *testing.T) { resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}, Header: http.Header{"Location": []string{"http://security-crawl-maze.app/test/headers/location.found"}}}} navigationRequests := headerLocationParser(resp) require.Equal(t, "http://security-crawl-maze.app/test/headers/location.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("refresh", func(t *testing.T) { resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}, Header: http.Header{"Refresh": []string{"999; url=/test/headers/refresh.found"}}}} navigationRequests := headerRefreshParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/headers/refresh.found", navigationRequests[0].URL, "could not get correct url") }) } func TestBodyParsers(t *testing.T) { parsed, _ := urlutil.Parse("https://security-crawl-maze.app/html/body/xyz/") t.Run("a", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyATagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/a/href.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader("")) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyATagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/a/ping.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("background", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyBackgroundTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/background.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("blockquote", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(`
`)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyBlockquoteCiteTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/blockquote/cite.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("area", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(` `)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyMapAreaPingTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/map/area/ping.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("audio", func(t *testing.T) { t.Run("src", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyAudioTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/audio/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("source", func(t *testing.T) { t.Run("src", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyAudioTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/audio/source/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("srcset", func(t *testing.T) { var gotURL []string documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} for _, navigationRequest := range bodyAudioTagParser(resp) { gotURL = append(gotURL, navigationRequest.URL) } require.ElementsMatch(t, []string{ "https://security-crawl-maze.app/test/html/body/audio/source/srcset1x.found", "https://security-crawl-maze.app/test/html/body/audio/source/srcset2x.found", }, gotURL, "could not get correct url") }) }) }) t.Run("img", func(t *testing.T) { t.Run("dynsrc", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyImgTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/img/dynsrc.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("longdesc", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyImgTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/img/longdesc.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("lowsrc", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyImgTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/img/lowsrc.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("src", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyImgTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/img/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("srcset", func(t *testing.T) { var gotURL []string documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} for _, navigationResponse := range bodyImgTagParser(resp) { gotURL = append(gotURL, navigationResponse.URL) } require.ElementsMatch(t, []string{ "https://security-crawl-maze.app/test/html/body/img/srcset1x.found", "https://security-crawl-maze.app/test/html/body/img/srcset2x.found", }, gotURL, "could not get correct url") }) }) t.Run("html-body", func(t *testing.T) { // TODO: Fix parsing // // parsed, _ = url.Parse("https://security-crawl-maze.app/html/body/frameset/frame/src.html") // var gotURL []string // resp := navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Body: []byte(`

// The test contains an inline string with known extension - /string-known-extension.pdf // The test contains an inline string - ./test/html/misc/string/dot-slash-prefix.found // The test contains an inline string - ../test/html/misc/string/dot-dot-slash-prefix.found // The test contains an inline string - http://security-crawl-maze.app/test/html/misc/string/url-string.found //

`), Options: &types.CrawlerOptions{Options: &types.Options{ScrapeJSResponses: true}}} // bodyScrapeEndpointsParser(resp, func(resp navigation.Request) { // gotURL = append(gotURL, resp.URL) // }) // require.ElementsMatch(t, []string{ // "https://security-crawl-maze.app/test/string-known-extension.pdf", // "https://security-crawl-maze.app/test/html/misc/string/dot-slash-prefix.found", // "https://security-crawl-maze.app/test/html/misc/string/dot-dot-slash-prefix.found", // "http://security-crawl-maze.app/test/html/misc/string/url-string.found", // }, gotURL, "could not get correct url") }) t.Run("object", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyObjectTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/object/data.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyObjectTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/object/codebase.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(` `)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyObjectTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/object/param/value.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("svg", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(` `)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodySvgTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/svg/image/xlink.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(` `)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodySvgTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/svg/script/xlink.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("table", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(`
`)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyTableTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/table/background.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(`
`)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyTableTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/table/td/background.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("video", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyVideoTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/video/poster.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyVideoTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/video/src.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyVideoTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/video/track/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("applet", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyAppletTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/applet/archive.found", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyAppletTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/applet/codebase.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("link", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyLinkHrefTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/css/font-face.css", navigationRequests[0].URL, "could not get correct url") documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests = bodyLinkHrefTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/head/link/href.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("base", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyBaseHrefTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/head/base/href.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("manifest", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyHtmlManifestTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/manifest.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("doctype", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(` `)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyHtmlDoctypeTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/doctype.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("import", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyImportImplementationTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/head/import/implementation.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("embed", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyEmbedTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/embed/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("frame", func(t *testing.T) { // var gotURL string // // TODO: Fix test // documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(` // // // CrawlMaze - Testbed for Web Crawlers - frame tag //

src attribute

// // // `)) // resp := navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} // bodyFrameTagParser(resp, func(resp navigation.Request) { // gotURL = resp.URL // }) // require.Equal(t, "https://security-crawl-maze.app/test/html/body/frameset/frame/src.found", gotURL, "could not get correct url") }) t.Run("iframe", func(t *testing.T) { t.Run("src", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyIframeTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/iframe/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("srcdoc", func(t *testing.T) { //var gotURL string //documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) //resp := navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} //bodyIframeTagParser(resp, func(resp navigation.Request) { // gotURL = resp.URL //}) //require.Equal(t, "https://security-crawl-maze.app/test/html/body/iframe/srcdoc.found", gotURL, "could not get correct url") }) }) t.Run("input", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyInputSrcTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/input/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("isindex", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyIsindexActionTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/isindex/action.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("script", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyScriptSrcTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/script/src.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("button", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("
")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyButtonFormactionTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/form/button/formaction.found", navigationRequests[0].URL, "could not get correct url") }) t.Run("form", func(t *testing.T) { t.Run("get", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("
")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyFormTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/form/action-get.found?test1=test&test2=test", navigationRequests[0].URL, "could not get correct url") }) t.Run("post", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("
")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyFormTagParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/body/form/action-post.found", navigationRequests[0].URL, "could not get correct url") require.Equal(t, "POST", navigationRequests[0].Method, "could not get correct method") }) }) t.Run("meta", func(t *testing.T) { // var gotURL string // documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) // resp := navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} // bodyMetaContentTagParser(resp, func(resp navigation.Request) { // gotURL = resp.URL // }) // require.Equal(t, "https://security-crawl-maze.app/test/html/head/meta/content-redirect.found", gotURL, "could not get correct url") // // documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) // resp = navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} // bodyMetaContentTagParser(resp, func(resp navigation.Request) { // gotURL = resp.URL // }) // require.Equal(t, "https://security-crawl-maze.app/test/html/head/meta/content-csp.found", gotURL, "could not get correct url") // // documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) // resp = navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} // bodyMetaContentTagParser(resp, func(resp navigation.Request) { // gotURL = resp.URL // }) // require.Equal(t, "https://security-crawl-maze.app/test/html/head/meta/content-pinned-websites.found", gotURL, "could not get correct url") // // documentReader, _ = goquery.NewDocumentFromReader(strings.NewReader(``)) // resp = navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} // bodyMetaContentTagParser(resp, func(resp navigation.Request) { // gotURL = resp.URL // }) // require.Equal(t, "https://security-crawl-maze.app/test/html/head/meta/content-reading-view.found", gotURL, "could not get correct url") }) } func TestScriptParsers(t *testing.T) { parsed, _ := urlutil.Parse("https://security-crawl-maze.app/html/script/xyz/") t.Run("content", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader("")) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := scriptContentRegexParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/script/content.do", navigationRequests[0].URL, "could not get correct url") }) t.Run("js", func(t *testing.T) { parsed, _ = urlutil.Parse("https://security-crawl-maze.app/html/script/xyz/data.js") resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Body: "var endpoint='/test/html/script/body.do';"} navigationRequests := scriptJSFileRegexParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/script/body.do", navigationRequests[0].URL, "could not get correct url") parsed, _ = urlutil.Parse("https://security-crawl-maze.app/html/script/xyz/") resp = &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}, Header: http.Header{"Content-Type": []string{"application/javascript"}}}, Body: "var endpoint='/test/html/script/body-content-type.do';"} navigationRequests = scriptJSFileRegexParser(resp) require.Equal(t, "https://security-crawl-maze.app/test/html/script/body-content-type.do", navigationRequests[0].URL, "could not get correct url") }) } func TestRegexBodyParsers(t *testing.T) { parsed, _ := urlutil.Parse("https://security-crawl-maze.app/contact") t.Run("regexbody", func(t *testing.T) { output.CustomFieldsMap = make(map[string]output.CustomFieldConfig) resp := &navigation.Response{ Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Depth: 0, Body: "some content contact@example.com", } // set required regex output.CustomFieldsMap["email"] = output.CustomFieldConfig{ Name: "email", Type: "regex", Part: "body", CompileRegex: []*regexp.Regexp{regexp.MustCompile(`([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)`)}, } navigationRequests := customFieldRegexParser(resp) var requireFields = map[string][]string{"email": {"contact@example.com"}} require.Equal(t, requireFields, navigationRequests[0].CustomFields, "could not get correct url") }) t.Run("regexheader", func(t *testing.T) { output.CustomFieldsMap = make(map[string]output.CustomFieldConfig) resp := &navigation.Response{ Resp: &http.Response{Request: &http.Request{URL: parsed.URL}, Header: http.Header{ "server": []string{"ECS (dcb/7F84)"}, }, }, } // set required regex output.CustomFieldsMap["server"] = output.CustomFieldConfig{ Name: "server", Type: "regex", Part: "header", CompileRegex: []*regexp.Regexp{regexp.MustCompile(`server: ECS`)}, } navigationRequests := customFieldRegexParser(resp) var requireFields = map[string][]string{"server": {"server: ECS"}} require.Equal(t, requireFields, navigationRequests[0].CustomFields, "could not get correct url") }) t.Run("regexresponse", func(t *testing.T) { output.CustomFieldsMap = make(map[string]output.CustomFieldConfig) resp := &navigation.Response{ Resp: &http.Response{Request: &http.Request{URL: parsed.URL}, Header: http.Header{ "server": []string{"ECS (dcb/7F84)"}, }, }, Body: "some content contact@example.com", } // set required regex output.CustomFieldsMap["server"] = output.CustomFieldConfig{ Name: "server", Type: "regex", Part: "response", CompileRegex: []*regexp.Regexp{regexp.MustCompile(`ECS`)}, } output.CustomFieldsMap["email"] = output.CustomFieldConfig{ Name: "email", Type: "regex", Part: "response", CompileRegex: []*regexp.Regexp{regexp.MustCompile(`([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)`)}, } navigationRequests := customFieldRegexParser(resp) var requireFields = map[string][]string{"server": {"ECS"}, "email": {"contact@example.com"}} require.Equal(t, requireFields, navigationRequests[0].CustomFields, "could not get correct url") }) } func TestHtmxBodyParser(t *testing.T) { parsed, _ := urlutil.Parse("https://htmx.org/examples/") t.Run("hx-get", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(``)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyHtmxAttrParser(resp) require.Equal(t, "https://htmx.org/contact/1/edit", navigationRequests[0].URL, "could not get correct url") require.Equal(t, "GET", navigationRequests[0].Method, "could not get correct method") }) t.Run("hx-post", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(`
`)) resp := &navigation.Response{Resp: &http.Response{Request: &http.Request{URL: parsed.URL}}, Reader: documentReader} navigationRequests := bodyHtmxAttrParser(resp) require.Equal(t, "https://htmx.org/users", navigationRequests[0].URL, "could not get correct url") require.Equal(t, "POST", navigationRequests[0].Method, "could not get correct method") }) t.Run("hx-put", func(t *testing.T) { documentReader, _ := goquery.NewDocumentFromReader(strings.NewReader(`