mirror of
https://github.com/FlareSolverr/FlareSolverr.git
synced 2025-12-06 09:38:49 +01:00
Compare commits
11 Commits
v3.3.20
...
iframe-sha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f75994197c | ||
|
|
42714b248f | ||
|
|
2900c58165 | ||
|
|
a798561338 | ||
|
|
eb680efc90 | ||
|
|
0f8f0bec25 | ||
|
|
3d9bc5627b | ||
|
|
dd7eaee2e3 | ||
|
|
031177bbdb | ||
|
|
a8644532a1 | ||
|
|
e96161c873 |
66
.github/workflows/release-docker.yml
vendored
66
.github/workflows/release-docker.yml
vendored
@@ -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 }}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
10
Dockerfile
10
Dockerfile
@@ -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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user