Compare commits

...

11 Commits

Author SHA1 Message Date
MCG-pok
f75994197c Update css selector to get shadowed iframe 2024-08-14 14:05:04 +03:00
MCG-pok
42714b248f add js click on iframe_body 2024-08-14 14:05:04 +03:00
MCG-pok
2900c58165 Access iframe in closed shadow root + click on checkbox from iframe body 2024-08-14 14:05:04 +03:00
ilike2burnthing
a798561338 Bump requests version
*.0 was yanked
2024-07-30 02:38:13 +01:00
Bogdan
eb680efc90 Don't build docker images for PRs from forks (#1281) 2024-07-20 22:08:40 +03:00
ilike2burnthing
0f8f0bec25 revert and bump action version 2024-07-20 19:41:49 +01:00
ilike2burnthing
3d9bc5627b Change to GITHUB_TOKEN for GHRC login 2024-07-20 14:21:34 +01:00
ilike2burnthing
dd7eaee2e3 Bump requirements
resolves Dependabot alerts
2024-07-12 17:11:40 +01:00
ilike2burnthing
031177bbdb Bump version 3.3.21 (#1240) 2024-06-26 02:14:25 +01:00
Bogdan
a8644532a1 Escape values for generated form used in request.post (#1236)
and build docker images for PRs
2024-06-26 02:04:59 +01:00
ilike2burnthing
e96161c873 Add challenge selector to catch reloading page on non-English systems. resolves #1237 2024-06-25 22:32:06 +01:00
6 changed files with 93 additions and 49 deletions

View File

@@ -4,50 +4,64 @@ on:
push: push:
tags: tags:
- 'v*.*.*' - 'v*.*.*'
pull_request:
branches:
- master
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs: jobs:
build-docker-images: build-docker-images:
if: ${{ !github.event.pull_request.head.repo.fork }}
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- - name: Checkout
name: Checkout uses: actions/checkout@v4
uses: actions/checkout@v3
- - name: Downcase repo
name: Downcase repo
run: echo REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV run: echo REPOSITORY=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
-
name: Docker meta - name: Docker meta
id: docker_meta id: docker_meta
uses: crazy-max/ghaction-docker-meta@v3 uses: docker/metadata-action@v5
with: with:
images: ${{ env.REPOSITORY }},ghcr.io/${{ env.REPOSITORY }} images: |
tag-sha: false ${{ env.REPOSITORY }},enable=${{ github.event_name != 'pull_request' }}
- ghcr.io/${{ env.REPOSITORY }}
name: Set up QEMU tags: |
uses: docker/setup-qemu-action@v2 type=semver,pattern={{version}},prefix=v
- type=ref,event=pr
name: Set up Docker Buildx flavor: |
uses: docker/setup-buildx-action@v2 latest=auto
-
name: Login to DockerHub - name: Set up QEMU
uses: docker/login-action@v2 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
if: github.event_name != 'pull_request'
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v2 uses: docker/login-action@v3
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GH_PAT }} password: ${{ secrets.GH_PAT }}
-
name: Build and push - name: Build and push
uses: docker/build-push-action@v3 uses: docker/build-push-action@v6
with: with:
context: . context: .
file: ./Dockerfile file: ./Dockerfile
platforms: linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 platforms: linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8
push: ${{ github.event_name != 'pull_request' }} push: true
tags: ${{ steps.docker_meta.outputs.tags }} tags: ${{ steps.docker_meta.outputs.tags }}
labels: ${{ steps.docker_meta.outputs.labels }} labels: ${{ steps.docker_meta.outputs.labels }}

View File

@@ -1,5 +1,9 @@
# Changelog # Changelog
## v3.3.21 (2024/06/26)
* Add challenge selector to catch reloading page on non-English systems
* Escape values for generated form used in request.post. Thanks @mynameisbogdan
## v3.3.20 (2024/06/21) ## v3.3.20 (2024/06/21)
* maxTimeout should always be int * maxTimeout should always be int
* Check not running in Docker before logging version_main error * Check not running in Docker before logging version_main error

View File

@@ -62,17 +62,17 @@ ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["/usr/local/bin/python", "-u", "/app/flaresolverr.py"] CMD ["/usr/local/bin/python", "-u", "/app/flaresolverr.py"]
# Local build # Local build
# docker build -t ngosang/flaresolverr:3.3.20 . # docker build -t ngosang/flaresolverr:3.3.21 .
# docker run -p 8191:8191 ngosang/flaresolverr:3.3.20 # docker run -p 8191:8191 ngosang/flaresolverr:3.3.21
# Multi-arch build # Multi-arch build
# docker run --rm --privileged multiarch/qemu-user-static --reset -p yes # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# docker buildx create --use # docker buildx create --use
# docker buildx build -t ngosang/flaresolverr:3.3.20 --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 . # docker buildx build -t ngosang/flaresolverr:3.3.21 --platform linux/386,linux/amd64,linux/arm/v7,linux/arm64/v8 .
# add --push to publish in DockerHub # add --push to publish in DockerHub
# Test multi-arch build # Test multi-arch build
# docker run --rm --privileged multiarch/qemu-user-static --reset -p yes # docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
# docker buildx create --use # docker buildx create --use
# docker buildx build -t ngosang/flaresolverr:3.3.20 --platform linux/arm/v7 --load . # docker buildx build -t ngosang/flaresolverr:3.3.21 --platform linux/arm/v7 --load .
# docker run -p 8191:8191 --platform linux/arm/v7 ngosang/flaresolverr:3.3.20 # docker run -p 8191:8191 --platform linux/arm/v7 ngosang/flaresolverr:3.3.21

View File

@@ -1,6 +1,6 @@
{ {
"name": "flaresolverr", "name": "flaresolverr",
"version": "3.3.20", "version": "3.3.21",
"description": "Proxy server to bypass Cloudflare protection", "description": "Proxy server to bypass Cloudflare protection",
"author": "Diego Heras (ngosang / ngosang@hotmail.es)", "author": "Diego Heras (ngosang / ngosang@hotmail.es)",
"license": "MIT" "license": "MIT"

View File

@@ -4,8 +4,8 @@ selenium==4.15.2
func-timeout==4.3.5 func-timeout==4.3.5
prometheus-client==0.17.1 prometheus-client==0.17.1
# required by undetected_chromedriver # required by undetected_chromedriver
requests==2.31.0 requests==2.32.3
certifi==2023.7.22 certifi==2024.07.04
websockets==11.0.3 websockets==11.0.3
# only required for linux and macos # only required for linux and macos
xvfbwrapper==0.2.9; platform_system != "Windows" xvfbwrapper==0.2.9; platform_system != "Windows"

View File

@@ -3,7 +3,8 @@ import platform
import sys import sys
import time import time
from datetime import timedelta from datetime import timedelta
from urllib.parse import unquote from html import escape
from urllib.parse import unquote, quote
from func_timeout import FunctionTimedOut, func_timeout from func_timeout import FunctionTimedOut, func_timeout
from selenium.common import TimeoutException from selenium.common import TimeoutException
@@ -40,7 +41,7 @@ CHALLENGE_TITLES = [
] ]
CHALLENGE_SELECTORS = [ CHALLENGE_SELECTORS = [
# Cloudflare # Cloudflare
'#cf-challenge-running', '.ray_id', '.attack-box', '#cf-please-wait', '#challenge-spinner', '#trk_jschal_js', '#turnstile-wrapper', '#cf-challenge-running', '.ray_id', '.attack-box', '#cf-please-wait', '#challenge-spinner', '#trk_jschal_js', '#turnstile-wrapper', '.lds-ring',
# Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands # Custom CloudFlare for EbookParadijs, Film-Paleis, MuziekFabriek and Puur-Hollands
'td.info #js_info', 'td.info #js_info',
# Fairlane / pararius.com # Fairlane / pararius.com
@@ -218,6 +219,20 @@ def _cmd_sessions_destroy(req: V1RequestBase) -> V1ResponseBase:
"message": "The session has been removed." "message": "The session has been removed."
}) })
def _init_driver(driver):
try:
driver.execute_cdp_cmd('Page.enable', {})
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': """
Element.prototype._as = Element.prototype.attachShadow;
Element.prototype.attachShadow = function (params) {
return this._as({mode: "open"})
};
"""
})
except Exception as e:
logging.debug("Driver init exception: %s", repr(e))
def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT: def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT:
timeout = int(req.maxTimeout) / 1000 timeout = int(req.maxTimeout) / 1000
@@ -238,6 +253,7 @@ def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT:
else: else:
driver = utils.get_webdriver(req.proxy) driver = utils.get_webdriver(req.proxy)
logging.debug('New instance of webdriver has been created to perform the request') logging.debug('New instance of webdriver has been created to perform the request')
_init_driver(driver)
return func_timeout(timeout, _evil_logic, (req, driver, method)) return func_timeout(timeout, _evil_logic, (req, driver, method))
except FunctionTimedOut: except FunctionTimedOut:
raise Exception(f'Error solving the challenge. Timeout after {timeout} seconds.') raise Exception(f'Error solving the challenge. Timeout after {timeout} seconds.')
@@ -251,23 +267,33 @@ def _resolve_challenge(req: V1RequestBase, method: str) -> ChallengeResolutionT:
logging.debug('A used instance of webdriver has been destroyed') logging.debug('A used instance of webdriver has been destroyed')
def get_shadowed_iframe(driver: WebDriver, css_selector: str):
logging.debug("Getting ShadowRoot by selector: %s", css_selector)
shadow_element = driver.execute_script("""
return document.querySelector(arguments[0]).shadowRoot.firstChild;
""", css_selector)
if shadow_element:
logging.debug("iframe found")
else:
logging.debug("iframe not found")
return shadow_element
def click_verify(driver: WebDriver): def click_verify(driver: WebDriver):
try: try:
logging.debug("Try to find the Cloudflare verify checkbox...") logging.debug("Try to find the Cloudflare verify checkbox...")
iframe = driver.find_element(By.XPATH, "//iframe[starts-with(@id, 'cf-chl-widget-')]") iframe = get_shadowed_iframe(driver, "div:not(:has(div))")
driver.switch_to.frame(iframe) driver.switch_to.frame(iframe)
checkbox = driver.find_element( iframe_body = driver.find_element(By.CSS_SELECTOR, "body")
by=By.XPATH, if iframe_body:
value='//*[@id="content"]/div/div/label/input', iframe_body.click()
)
if checkbox:
actions = ActionChains(driver) actions = ActionChains(driver)
actions.move_to_element_with_offset(checkbox, 5, 7) actions.move_to_element_with_offset(iframe_body, 10, 10)
actions.click(checkbox) actions.click(iframe_body)
actions.perform() actions.perform()
logging.debug("Cloudflare verify checkbox found and clicked!") logging.debug("Attempted to click on iframe body")
except Exception: except Exception as e:
logging.debug("Cloudflare verify checkbox not found on the page.") logging.debug("Cloudflare verify checkbox not found on the page. %s", repr(e))
finally: finally:
driver.switch_to.default_content() driver.switch_to.default_content()
@@ -439,7 +465,7 @@ def _post_request(req: V1RequestBase, driver: WebDriver):
value = unquote(parts[1]) value = unquote(parts[1])
except Exception: except Exception:
value = parts[1] value = parts[1]
post_form += f'<input type="text" name="{name}" value="{value}"><br>' post_form += f'<input type="text" name="{escape(quote(name))}" value="{escape(quote(value))}"><br>'
post_form += '</form>' post_form += '</form>'
html_content = f""" html_content = f"""
<!DOCTYPE html> <!DOCTYPE html>
@@ -449,6 +475,6 @@ def _post_request(req: V1RequestBase, driver: WebDriver):
<script>document.getElementById('hackForm').submit();</script> <script>document.getElementById('hackForm').submit();</script>
</body> </body>
</html>""" </html>"""
driver.get("data:text/html;charset=utf-8," + html_content) driver.get("data:text/html;charset=utf-8,{html_content}".format(html_content=html_content))
driver.start_session() driver.start_session()
driver.start_session() # required to bypass Cloudflare driver.start_session() # required to bypass Cloudflare