Compare commits

...

38 Commits

Author SHA1 Message Date
Joe Chen
282f3c0c70 ci: fix up docker workflow issue 2026-01-23 12:35:06 -05:00
Joe Chen
89ad16f9b3 Suppress golangci-lint errors 2026-01-23 12:28:00 -05:00
Joe Chen
9ea429faaf Fix CI setup and errors 2026-01-23 12:18:28 -05:00
Joe Chen
c7d2d8b525 release: update version to 0.13.4 2026-01-23 10:21:06 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
4dc0a99919 repo: validate Git server hook name for editing (#8103) 2026-01-23 09:40:55 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
9e70cdf437 api: verify write access to update repo content (#8102) 2026-01-23 09:19:26 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
961a79e8f9 api: verify owner access to delete repos (#8101) 2026-01-22 22:53:17 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
d568e04831 two_factor: verify recovery code ownership upon using (#8100) 2026-01-22 22:35:52 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
af825ff56f wiki: sanitize old wiki page name when editing (#8099) 2026-01-22 11:06:41 -05:00
Jakub Domeracki
71a72a72ad security: patch mermaid package version
https://github.com/gogs/gogs/security/advisories/GHSA-26gq-grmh-6xm6

Co-authored-by: Jakub Domeracki <jdomeracki.itsec@gmail.com>
Co-authored-by: ᴊᴏᴇ ᴄʜᴇɴ <jc@unknwon.io>
2026-01-22 09:48:30 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
4167a4d568 wiki: auto-detect default branch (#8094) 2026-01-20 23:39:05 -05:00
Mukaiu
5b5793bb4a api: fix nil pointer dereference when listing user repos (#8069)
Co-authored-by: Joe Chen <jc@unknwon.io>
2026-01-20 23:33:43 -05:00
ᴊᴏᴇ ᴄʜᴇɴ
c3eca1fca3 repository: reject any updates that has symlink in path hierarchy (#8082)
Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-01-20 23:31:41 -05:00
Neptunium93
33990972fa repo: fix potential null pointer dereference in mirror sync (#8065) 2026-01-20 23:25:23 -05:00
Joe Chen
5084b4a9b7 release: update version to 0.13.3 2025-06-08 18:55:56 -04:00
Joe Chen
8aaabfcc99 ci: fix up MySQL test ubuntu version 2025-06-08 18:55:16 -04:00
Joe Chen
1cba9bc81b web_editor: prohibit CRUD to symbolic files (#7981)
Fixes
[GHSA-wj44-9vcg-wjq7](https://github.com/gogs/gogs/security/advisories/GHSA-wj44-9vcg-wjq7)

---------

Co-authored-by: deepsource-autofix[bot] <62050782+deepsource-autofix[bot]@users.noreply.github.com>
2025-06-08 18:47:04 -04:00
宋子桓🌈
e453425d1b email: fix unable to override templates in custom directory (#7905)
Co-authored-by: Joe Chen <jc@unknwon.io>
2025-06-08 18:45:12 -04:00
Edoardo Ottavianelli
110117b2e5 security: patch for Stored XSS in PDF renderer (#7966) 2025-06-08 18:44:32 -04:00
MarcUs7i
36be6a2871 Set timeout to explicit 0 in gogs.js (#7890)
## Describe the pull request

A simple fix in public/js/gogs.js making bug upload not result in a
timeout (added just one line)

Link to the issue: closes https://github.com/gogs/gogs/issues/6149

## Test plan

- Set the max_size of `attachment` to a high number

```toml
[release.attachment]
ENABLED          = true
ALLOWED_TYPES    = */*
MAX_SIZE         = 512
MAX_FILES        = 20
```

- Upload a file to releases


![image](https://github.com/user-attachments/assets/8cf29c73-c8ec-42a3-9660-681a583b577a)

It doesn't randomly timeout!
2025-06-08 18:44:10 -04:00
Joe Chen
593c7b6db6 release: update version to 0.13.2 2024-12-23 10:57:35 -05:00
Joe Chen
01157b2f79 Dockerfile: fix up outdated s6-svscan path (#7880)
## Describe the pull request

Link to the issue: https://github.com/gogs/gogs/issues/7864
2024-12-23 10:54:01 -05:00
Joe Chen
0c40e600a2 ci: fix up unsupported host system version 2024-12-22 17:54:57 -05:00
Joe Chen
080b9a9d03 release: update version to 0.13.1 2024-12-22 17:52:03 -05:00
Joe Chen
300519d1ca ci: fix up lint issues 2024-12-22 17:33:50 -05:00
Joe Chen
88a13fa378 ci: modernize setup 2024-12-22 17:21:26 -05:00
Joe Chen
2b0f129a91 dep: update github.com/gogs/git-module to v1.8.4 (#7872)
Fixes
https://github.com/gogs/gogs/security/advisories/GHSA-m27m-h5gj-wwmg by
including https://github.com/gogs/git-module/pull/110
2024-12-22 17:16:11 -05:00
Joe Chen
ce51a8e538 repo: ignore unintended Git options for diff preview (#7871)
## Describe the pull request

Fixes
https://github.com/gogs/gogs/security/advisories/GHSA-9pp6-wq8c-3w2c
2024-12-22 17:15:08 -05:00
Joe Chen
3b527a36c8 repo: prevent preview and delete files in .git directories (#7870)
## Describe the pull request

Fixes
https://github.com/gogs/gogs/security/advisories/GHSA-ccqv-43vm-4f3w
2024-12-22 17:14:44 -05:00
Joe Chen
f5262441a0 ssh: make env command a passthrough (#7868)
Fixes
https://github.com/gogs/gogs/security/advisories/GHSA-vm62-9jw3-c8w3

ssh: make `env` command a passthrough (#7868)

Fixes
https://github.com/gogs/gogs/security/advisories/GHSA-vm62-9jw3-c8w3
2024-12-22 17:04:03 -05:00
Alexandre Jacquin
f6862c1f8b Fix s6-svscan path in Dockerfile (#7867)
Related to #7864

## Describe the pull request

Fix the path of the s6-svscan binary path in the Dockerfile. As
mentionned in the issue, it is probably to the alpine base image change
(from `3.17` to `3.21`).

Link to the issue:
https://github.com/gogs/gogs/issues/7864#issuecomment-2558323928

Credit to @cryptovaltt

## Test plan

Build the image and check if the application can be ran. E.g.:

```bash
docker build -t gogs:working-7864 -f Dockerfile .
docker run --name=gogs -p 10022:22 -p 10880:3000 -v gogs:working-7864 --rm
```
2024-12-22 17:01:09 -05:00
Joe Chen
bd84b41843 Dockerfile: update base image to alpine3.21 and enable trivy scan (#7863)
Link to the issue: fixes https://github.com/gogs/gogs/issues/6674
2024-12-22 17:00:43 -05:00
Joe Chen
c947affcfa api: clean file path for updating repo contents (#7859)
## Describe the pull request

Link to the issue: closes https://github.com/gogs/gogs/issues/7582
2024-12-22 16:55:01 -05:00
Joe Chen
40cb106198 repo/editor: disallow editing symlink while changing file name (#7857)
## Describe the pull request

Link to the issue: https://github.com/gogs/gogs/issues/7582
2024-12-22 16:54:18 -05:00
Mobile Mind
b89da2f6eb install: fix SMTP password config name to save (#7807) 2024-12-22 16:54:02 -05:00
Jiaxin Zhu
75969c92ef fix(templates): the green color is supposed to be purple (#7722)
Co-authored-by: jxzhu <zhujiaxin@gmail.com>
2024-12-22 16:53:45 -05:00
bitebyte
e993f1dbff Fix issue: synchronize the section name[mailer/email] of app.ini (#7704) 2024-12-22 16:52:23 -05:00
Joe Chen
8c21874c00 release: remove dev version notion 2023-02-25 20:33:19 +08:00
532 changed files with 161048 additions and 64501 deletions

View File

@@ -1,35 +0,0 @@
# Docs: https://github.com/sturdy-dev/codeball-action
name: Codeball
on: [ pull_request ]
permissions:
contents: read
issues: write
pull-requests: write
jobs:
codeball:
runs-on: ubuntu-latest
name: Codeball
steps:
# Start a new Codeball review job
# This step is asynchronous and will return a job id
- name: Trigger Codeball
id: codeball_baller
uses: sturdy-dev/codeball-action/baller@v2
# Wait for Codeball to return the status
- name: Get Status
id: codeball_status
uses: sturdy-dev/codeball-action/status@v2
with:
codeball-job-id: ${{ steps.codeball_baller.outputs.codeball-job-id }}
# If Codeball approved the contribution, approve the PR
- name: Approve PR
uses: sturdy-dev/codeball-action/approver@v2
if: ${{ steps.codeball_status.outputs.approved == 'true' }}
with:
message: "Codeball: LGTM! :+1:"

View File

@@ -39,7 +39,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
@@ -47,7 +47,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.28.3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -58,7 +58,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.28.3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -72,4 +72,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v3.28.3

View File

@@ -5,37 +5,36 @@ on:
- main
pull_request:
paths:
- '.trivy.yaml'
- 'Dockerfile'
- 'Dockerfile.next'
- 'docker/**'
- 'docker-next/**'
- '.github/workflows/docker.yml'
release:
types: [ published ]
jobs:
buildx:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
packages: write
steps:
- name: Canel previous runs
uses: styfle/cancel-workflow-action@0.9.1
with:
all_but_latest: true
access_token: ${{ secrets.GITHUB_TOKEN }}
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
config-inline: |
[worker.oci]
max-parallelism = 2
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Inspect builder
run: |
echo "Name: ${{ steps.buildx.outputs.name }}"
@@ -44,18 +43,18 @@ jobs:
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
- name: Login to Docker Hub
uses: docker/login-action@v1
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v1
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v2
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
@@ -63,8 +62,126 @@ jobs:
tags: |
gogs/gogs:latest
ghcr.io/gogs/gogs:latest
- name: Scan for container vulnerabilities
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
with:
image-ref: gogs/gogs:latest
exit-code: '1'
- name: Send email on failure
uses: dawidd6/action-send-mail@v3
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
if: ${{ failure() }}
with:
server_address: smtp.mailgun.org
server_port: 465
username: ${{ secrets.SMTP_USERNAME }}
password: ${{ secrets.SMTP_PASSWORD }}
subject: GitHub Actions (${{ github.repository }}) job result
to: github-actions-8ce6454@unknwon.io
from: GitHub Actions (${{ github.repository }})
reply_to: noreply@unknwon.io
body: |
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
buildx-next:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
concurrency:
group: ${{ github.workflow }}-next-${{ github.ref }}
cancel-in-progress: true
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Inspect builder
run: |
echo "Name: ${{ steps.buildx.outputs.name }}"
echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
echo "Status: ${{ steps.buildx.outputs.status }}"
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
- name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to DigitalOcean Container registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: registry.digitalocean.com
username: ${{ secrets.DIGITALOCEAN_USERNAME }}
password: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Build and push next-gen images
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: Dockerfile.next
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: |
gogs/gogs:next-latest
ghcr.io/gogs/gogs:next-latest
registry.digitalocean.com/gogs/gogs:next-latest
- name: Scan for container vulnerabilities
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
with:
image-ref: gogs/gogs:next-latest
exit-code: '1'
- name: Send email on failure
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
if: ${{ failure() }}
with:
server_address: smtp.mailgun.org
server_port: 465
username: ${{ secrets.SMTP_USERNAME }}
password: ${{ secrets.SMTP_PASSWORD }}
subject: GitHub Actions (${{ github.repository }}) job result
to: github-actions-8ce6454@unknwon.io
from: GitHub Actions (${{ github.repository }})
reply_to: noreply@unknwon.io
body: |
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
deploy-demo:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository == 'gogs/gogs' }}
needs: buildx-next
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Configure kubectl
run: |
mkdir -p ~/.kube
echo "${KUBECONFIG}" | base64 -d > ~/.kube/config
env:
KUBECONFIG: ${{ secrets.DIGITALOCEAN_K8S_CLUSTER_KUBECONFIG }}
- name: Restart gogs-demo deployment
timeout-minutes: 5
run: |
set -ex
kubectl rollout restart deployment gogs-demo -n gogs
kubectl rollout status deployment gogs-demo -n gogs
- name: Send email on failure
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
if: ${{ failure() }}
with:
server_address: smtp.mailgun.org
@@ -87,10 +204,10 @@ jobs:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
with:
config-inline: |
[worker.oci]
@@ -103,15 +220,61 @@ jobs:
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
- name: Compute short commit SHA
uses: benjlevesque/short-sha@v1.2
id: short-sha
uses: benjlevesque/short-sha@599815c8ee942a9616c92bcfb4f947a3b670ab0b # v3.0
- name: Build and push images
uses: docker/build-push-action@v2
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
platforms: linux/amd64
push: true
tags: |
ttl.sh/gogs/gogs-${{ env.SHA }}:1d
ttl.sh/gogs/gogs-${{ steps.short-sha.outputs.sha }}:7d
- name: Scan for container vulnerabilities
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
with:
image-ref: ttl.sh/gogs/gogs-${{ steps.short-sha.outputs.sha }}:7d
exit-code: '1'
buildx-next-pull-request:
if: ${{ github.event_name == 'pull_request'}}
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
with:
config-inline: |
[worker.oci]
max-parallelism = 2
- name: Inspect builder
run: |
echo "Name: ${{ steps.buildx.outputs.name }}"
echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
echo "Status: ${{ steps.buildx.outputs.status }}"
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
- name: Compute short commit SHA
id: short-sha
uses: benjlevesque/short-sha@599815c8ee942a9616c92bcfb4f947a3b670ab0b # v3.0
- name: Build and push next-gen images
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: Dockerfile.next
platforms: linux/amd64
push: true
tags: |
ttl.sh/gogs/gogs-next-${{ steps.short-sha.outputs.sha }}:7d
- name: Scan for container vulnerabilities
uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
with:
image-ref: ttl.sh/gogs/gogs-next-${{ steps.short-sha.outputs.sha }}:7d
exit-code: '1'
# Updates to the following section needs to be synced to all release branches within their lifecycles.
buildx-release:
@@ -125,16 +288,14 @@ jobs:
- name: Compute image tag name
run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v1
with:
config-inline: |
[worker.oci]
max-parallelism = 2
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Inspect builder
run: |
echo "Name: ${{ steps.buildx.outputs.name }}"
@@ -143,18 +304,18 @@ jobs:
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
- name: Login to Docker Hub
uses: docker/login-action@v1
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@v1
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push images
uses: docker/build-push-action@v2
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
platforms: linux/amd64,linux/arm64,linux/arm/v7
@@ -163,7 +324,72 @@ jobs:
gogs/gogs:${{ env.IMAGE_TAG }}
ghcr.io/gogs/gogs:${{ env.IMAGE_TAG }}
- name: Send email on failure
uses: dawidd6/action-send-mail@v3
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
if: ${{ failure() }}
with:
server_address: smtp.mailgun.org
server_port: 465
username: ${{ secrets.SMTP_USERNAME }}
password: ${{ secrets.SMTP_PASSWORD }}
subject: GitHub Actions (${{ github.repository }}) job result
to: github-actions-8ce6454@unknwon.io
from: GitHub Actions (${{ github.repository }})
reply_to: noreply@unknwon.io
body: |
The job "${{ github.job }}" of ${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }} completed with "${{ job.status }}".
View the job run at: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
# Updates to the following section needs to be synced to all release branches within their lifecycles.
buildx-next-release:
if: ${{ github.event_name == 'release' }}
runs-on: ubuntu-latest
permissions:
actions: write
contents: read
packages: write
steps:
- name: Compute image tag name
run: echo "IMAGE_TAG=$(echo $GITHUB_REF_NAME | cut -c 2-)" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Set up QEMU
uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Inspect builder
run: |
echo "Name: ${{ steps.buildx.outputs.name }}"
echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
echo "Status: ${{ steps.buildx.outputs.status }}"
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
- name: Login to Docker Hub
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container registry
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push next-gen images
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
file: Dockerfile.next
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
tags: |
gogs/gogs:next-${{ env.IMAGE_TAG }}
ghcr.io/gogs/gogs:next-${{ env.IMAGE_TAG }}
- name: Send email on failure
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
if: ${{ failure() }}
with:
server_address: smtp.mailgun.org

View File

@@ -30,14 +30,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v2
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
version: latest
args: --timeout=30m
go-version: 1.25.x
- name: Install Task
uses: arduino/setup-task@v1
uses: arduino/setup-task@b91d5d2c96a56797b48ac1e0e89220bf64044611 # v2.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Check Go module tidiness and generated files
@@ -52,30 +51,35 @@ jobs:
echo "Run 'go mod tidy' or 'task generate' commit them"
exit 1
fi
- name: Run golangci-lint
uses: golangci/golangci-lint-action@9fae48acfc02a90574d7c304a1758ef9895495fa # v7.0.1
with:
version: latest
args: --timeout=30m
test:
name: Test
strategy:
matrix:
go-version: [ 1.19.x, 1.20.x ]
go-version: [ 1.25.x ]
platform: [ ubuntu-latest, macos-latest ]
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests with coverage
run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./...
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v1.5.0
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
file: ./coverage
flags: unittests
- name: Send email on failure
uses: dawidd6/action-send-mail@v3
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
with:
server_address: smtp.mailgun.org
@@ -94,28 +98,28 @@ jobs:
# Running tests with race detection consumes too much memory on Windows,
# see https://github.com/golang/go/issues/46099 for details.
test-windows:
name: Test
name: Test Windows
strategy:
matrix:
go-version: [ 1.19.x, 1.20.x ]
go-version: [ 1.25.x ]
platform: [ windows-latest ]
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests with coverage
run: go test -shuffle=on -v -coverprofile=coverage -covermode=atomic ./...
- name: Upload coverage report to Codecov
uses: codecov/codecov-action@v1.5.0
uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1
with:
file: ./coverage
flags: unittests
- name: Send email on failure
uses: dawidd6/action-send-mail@v3
uses: dawidd6/action-send-mail@2cea9617b09d79a095af21254fbcb7ae95903dde # v3.12.0
if: ${{ failure() && github.event_name == 'push' && github.ref == 'refs/heads/main' }}
with:
server_address: smtp.mailgun.org
@@ -135,7 +139,7 @@ jobs:
name: Postgres
strategy:
matrix:
go-version: [ 1.19.x, 1.20.x ]
go-version: [ 1.25.x ]
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
services:
@@ -151,12 +155,12 @@ jobs:
ports:
- 5432:5432
steps:
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests with coverage
run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./internal/db/...
env:
@@ -171,18 +175,18 @@ jobs:
name: MySQL
strategy:
matrix:
go-version: [ 1.19.x, 1.20.x ]
platform: [ ubuntu-18.04 ]
go-version: [ 1.25.x ]
platform: [ ubuntu-22.04 ] # Use the lowest version possible for backwards compatibility
runs-on: ${{ matrix.platform }}
steps:
- name: Start MySQL server
run: sudo systemctl start mysql
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests with coverage
run: go test -shuffle=on -v -race -coverprofile=coverage -covermode=atomic ./internal/db/...
env:
@@ -196,16 +200,16 @@ jobs:
name: SQLite - Go
strategy:
matrix:
go-version: [ 1.19.x, 1.20.x ]
go-version: [ 1.25.x ]
platform: [ ubuntu-latest ]
runs-on: ${{ matrix.platform }}
steps:
- name: Checkout code
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with:
go-version: ${{ matrix.go-version }}
- name: Checkout code
uses: actions/checkout@v2
- name: Run tests with coverage
run: go test -shuffle=on -v -race -parallel=1 -coverprofile=coverage -covermode=atomic ./internal/db/...
env:

View File

@@ -16,7 +16,7 @@ jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5.0.1
with:
github-token: ${{ github.token }}
issue-inactive-days: '90'

View File

@@ -1,33 +0,0 @@
name: LSIF
on:
push:
paths:
- '**.go'
- 'go.mod'
- '.github/workflows/lsif.yml'
env:
GOPROXY: "https://proxy.golang.org"
jobs:
lsif-go:
if: github.repository == 'gogs/gogs'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Generate LSIF data
uses: sourcegraph/lsif-go-action@master
- name: Upload LSIF data to sourcegraph.com
continue-on-error: true
uses: docker://sourcegraph/src-cli:latest
with:
args: lsif upload -github-token=${{ secrets.GITHUB_TOKEN }}
- name: Upload LSIF data to S2
continue-on-error: true
uses: docker://sourcegraph/src-cli:latest
with:
args: -endpoint=https://sourcegraph.sourcegraph.com lsif upload -github-token=${{ secrets.GITHUB_TOKEN }}
- name: Upload LSIF data to cs.unknwon.dev
continue-on-error: true
uses: docker://sourcegraph/src-cli:latest
with:
args: -endpoint=https://cs.unknwon.dev lsif upload -github-token=${{ secrets.GITHUB_TOKEN }}

View File

@@ -12,6 +12,6 @@ jobs:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0

View File

@@ -1,24 +1,153 @@
linters-settings:
staticcheck:
checks: [
"all",
"-SA1019" # There are valid use cases of strings.Title
]
nakedret:
max-func-lines: 0 # Disallow any unnamed return statement
version: "2"
linters:
enable:
- unused
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- typecheck
- nakedret
- gofmt
- rowserrcheck
- unconvert
- goimports
- unparam
settings:
govet:
disable:
# printf: non-constant format string in call to fmt.Errorf (govet)
# showing up since golangci-lint version 1.60.1
- printf
staticcheck:
checks:
- all
- "-SA1019" # This project is under active refactoring and not all code is up to date.
- "-QF1001" # I'm a math noob
- "-ST1016" # Some legit code uses this pattern
nakedret:
max-func-lines: 0 # Disallow any unnamed return statement
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
rules:
- path: internal/auth/ldap/config.go
linters: [staticcheck]
- path: internal/cmd/import.go
linters: [staticcheck]
- path: internal/context/context.go
linters: [staticcheck]
- path: internal/db/attachment.go
linters: [staticcheck]
- path: internal/db/errors/errors.go
linters: [staticcheck]
- path: internal/db/issue.go
linters: [staticcheck]
- path: internal/db/issue_mail.go
linters: [staticcheck]
- path: internal/db/org.go
linters: [staticcheck]
- path: internal/db/repo.go
linters: [staticcheck]
- path: internal/db/schemadoc/main.go
linters: [staticcheck]
- path: internal/db/users.go
linters: [staticcheck]
- path: internal/db/webhook.go
linters: [staticcheck]
- path: internal/db/webhook_dingtalk.go
linters: [staticcheck]
- path: internal/email/email.go
linters: [staticcheck]
- path: internal/email/message.go
linters: [staticcheck]
- path: internal/form/repo.go
linters: [staticcheck]
- path: internal/form/user.go
linters: [staticcheck]
- path: internal/httplib/httplib.go
linters: [staticcheck]
- path: internal/markup/markdown.go
linters: [staticcheck]
- path: internal/route/api/v1/repo/commits.go
linters: [staticcheck]
- path: internal/route/api/v1/user/follower.go
linters: [staticcheck]
- path: internal/route/repo/branch.go
linters: [staticcheck]
- path: internal/route/repo/commit.go
linters: [staticcheck]
- path: internal/route/repo/issue.go
linters: [staticcheck]
- path: internal/route/user/profile.go
linters: [staticcheck]
- path: internal/template/template.go
linters: [staticcheck]
- path: internal/tool/tool.go
linters: [staticcheck]
- path: internal/cmd/serv.go
linters: [staticcheck]
- path: internal/db/actions_test.go
linters: [staticcheck]
- path: internal/db/milestone.go
linters: [staticcheck]
- path: internal/db/pull.go
linters: [staticcheck]
- path: internal/route/home.go
linters: [staticcheck]
- path: internal/db/release.go
linters: [staticcheck]
- path: internal/route/org/members.go
linters: [staticcheck]
- path: internal/route/org/setting.go
linters: [staticcheck]
- path: internal/db/repo_branch.go
linters: [staticcheck]
- path: internal/db/user_mail.go
linters: [staticcheck]
- path: internal/route/user/auth.go
linters: [staticcheck]
- path: internal/db/webhook_slack.go
linters: [staticcheck]
- path: internal/form/form.go
linters: [staticcheck]
- path: internal/route/org/teams.go
linters: [staticcheck]
- path: internal/route/admin/auths.go
linters: [staticcheck]
- path: internal/route/admin/users.go
linters: [staticcheck]
- path: internal/db/admin.go
linters: [staticcheck]
- path: internal/db/comment.go
linters: [staticcheck]
- path: internal/route/user/home.go
linters: [staticcheck]
- path: internal/route/user/setting.go
linters: [staticcheck]
- path: internal/db/org_team.go
linters: [staticcheck]
- path: internal/db/repo_editor.go
linters: [staticcheck]
- path: internal/process/manager.go
linters: [staticcheck]
- path: internal/db/ssh_key.go
linters: [staticcheck]
- path: internal/route/repo/pull.go
linters: [staticcheck]
- path: internal/route/repo/release.go
linters: [staticcheck]
- path: internal/route/repo/setting.go
linters: [staticcheck]
- path: internal/route/repo/wiki.go
linters: [staticcheck]
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

2
CODEOWNERS Normal file
View File

@@ -0,0 +1,2 @@
# Default
* @gogs/core

View File

@@ -1,4 +1,4 @@
FROM golang:alpine3.17 AS binarybuilder
FROM golang:alpine3.21 AS binarybuilder
RUN apk --no-cache --no-progress add --virtual \
build-deps \
build-base \
@@ -11,7 +11,7 @@ COPY . .
RUN ./docker/build/install-task.sh
RUN TAGS="cert pam" task build
FROM alpine:3.17
FROM alpine:3.21
RUN apk --no-cache --no-progress add \
bash \
ca-certificates \
@@ -41,4 +41,4 @@ VOLUME ["/data", "/backup"]
EXPOSE 22 3000
HEALTHCHECK CMD (curl -o /dev/null -sS http://localhost:3000/healthcheck) || exit 1
ENTRYPOINT ["/app/gogs/docker/start.sh"]
CMD ["/bin/s6-svscan", "/app/gogs/docker/s6/"]
CMD ["/usr/bin/s6-svscan", "/app/gogs/docker/s6/"]

View File

@@ -5,16 +5,16 @@ set -xe
# Install gosu
if [ "$(uname -m)" = "aarch64" ]; then
export arch='arm64'
export checksum='73244a858f5514a927a0f2510d533b4b57169b64d2aa3f9d98d92a7a7df80cea'
export checksum='c3805a85d17f4454c23d7059bcb97e1ec1af272b90126e79ed002342de08389b'
elif [ "$(uname -m)" = "armv7l" ]; then
export arch='armhf'
export checksum='abb1489357358b443789571d52b5410258ddaca525ee7ac3ba0dd91d34484589'
export checksum='e5866286277ff2a2159fb9196fea13e0a59d3f1091ea46ddb985160b94b6841b'
else
export arch='amd64'
export checksum='bd8be776e97ec2b911190a82d9ab3fa6c013ae6d3121eea3d0bfd5c82a0eaf8c'
export checksum='bbc4136d03ab138b1ad66fa4fc051bafc6cc7ffae632b069a53657279a450de3'
fi
wget --quiet https://github.com/tianon/gosu/releases/download/1.14/gosu-${arch} -O /usr/sbin/gosu
wget --quiet https://github.com/tianon/gosu/releases/download/1.17/gosu-${arch} -O /usr/sbin/gosu
echo "${checksum} /usr/sbin/gosu" | sha256sum -cs
chmod +x /usr/sbin/gosu

View File

@@ -4,16 +4,16 @@ set -xe
if [ "$(uname -m)" = "aarch64" ]; then
export arch='arm64'
export checksum='44fad3d61ad39d0abff33f90fdbb99a666524dbeab08dc9d138d5d3a532ff68a'
export checksum='17f325293d08f6f964e0530842e9ef1410dd5f83ee6475b493087391032b0cfd'
elif [ "$(uname -m)" = "armv7l" ]; then
export arch='arm'
export checksum='b10ae7d85749025740097b0c349b946fbabd417c7ee4d2df8ccc5604750accd9'
export checksum='e5b0261e9f6563ce3ace9e038520eb59d2c77c8d85f2b47ab41e1fe7cf321528'
else
export arch='amd64'
export checksum='b9c5986f33a53094751b5e22ccc33e050b4a0a485658442121331cbb724e631e'
export checksum='a35462ec71410cccfc428072de830e4478bc57a919d0131ef7897759270dff8f'
fi
wget --quiet https://github.com/go-task/task/releases/download/v3.12.1/task_linux_${arch}.tar.gz -O task_linux_${arch}.tar.gz
wget --quiet https://github.com/go-task/task/releases/download/v3.40.1/task_linux_${arch}.tar.gz -O task_linux_${arch}.tar.gz
echo "${checksum} task_linux_${arch}.tar.gz" | sha256sum -cs
tar -xzf task_linux_${arch}.tar.gz

View File

@@ -79,5 +79,5 @@ fi
if [ $# -gt 0 ];then
exec "$@"
else
exec /bin/s6-svscan /app/gogs/docker/s6/
exec /usr/bin/s6-svscan /app/gogs/docker/s6/
fi

4
gen.go
View File

@@ -4,5 +4,5 @@
package main
//go:generate go install golang.org/x/tools/cmd/goimports@v0.1.10
//go:generate go run github.com/derision-test/go-mockgen/cmd/go-mockgen@v1.3.3
//go:generate go install golang.org/x/tools/cmd/goimports@v0.33.0
//go:generate go run github.com/unknwon/go-mockgen/cmd/go-mockgen@v0.0.0-20251002032800-a9a94b119e3b

23
go.mod
View File

@@ -17,7 +17,7 @@ require (
github.com/go-macaron/toolbox v0.0.0-20190813233741-94defb8383c6
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/git-module v1.8.1
github.com/gogs/git-module v1.8.4
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a
@@ -37,15 +37,15 @@ require (
github.com/satori/go.uuid v1.2.0
github.com/sergi/go-diff v1.3.1
github.com/sourcegraph/run v0.12.0
github.com/stretchr/testify v1.8.1
github.com/stretchr/testify v1.10.0
github.com/unknwon/cae v1.0.2
github.com/unknwon/com v1.0.1
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e
github.com/urfave/cli v1.22.12
golang.org/x/crypto v0.6.0
golang.org/x/net v0.7.0
golang.org/x/text v0.7.0
github.com/urfave/cli v1.22.16
golang.org/x/crypto v0.31.0
golang.org/x/net v0.33.0
golang.org/x/text v0.21.0
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0
@@ -71,7 +71,7 @@ require (
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
github.com/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
github.com/djherbis/buffer v1.2.0 // indirect
@@ -105,7 +105,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 // indirect
github.com/microsoft/go-mssqldb v0.17.0 // indirect
@@ -123,9 +123,10 @@ require (
go.bobheadxi.dev/streamline v1.2.1 // indirect
go.opentelemetry.io/otel v1.11.0 // indirect
go.opentelemetry.io/otel/trace v1.11.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/tools v0.4.0 // indirect
golang.org/x/mod v0.17.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e // indirect

53
go.sum
View File

@@ -44,7 +44,7 @@ github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
@@ -80,8 +80,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/couchbase/gomemcached v0.0.0-20190515232915-c4b4ca0eb21d/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20190315194238-f9d42b11473b/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
github.com/couchbaselabs/go-couchbase v0.0.0-20190708161019-23e7ca2ce2b7/go.mod h1:mby/05p8HE5yHEAKiIH/555NoblMs7PtW6NrYshDruc=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -166,8 +166,8 @@ github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBU
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQdcMdzjbqqXMEnHfq0Or6p8=
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
github.com/gogs/git-module v1.8.1 h1:yC5BZ3unJOXC8N6/FgGQ8EtJXpOd217lgDcd2aPOxkc=
github.com/gogs/git-module v1.8.1/go.mod h1:Y3rsSqtFZEbn7lp+3gWf42GKIY1eNTtLt7JrmOy0yAQ=
github.com/gogs/git-module v1.8.4 h1:oSt8sOL4NWOGrSo/CwbS+C4YXtk76QvxyPofem/ViTU=
github.com/gogs/git-module v1.8.4/go.mod h1:bQY0aoMK5Q5+NKgy4jXe3K1GFW+GnsSk0SJK0jh6yD0=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4 h1:C7NryI/RQhsIWwC2bHN601P1wJKeuQ6U/UCOYTn3Cic=
github.com/gogs/go-gogs-client v0.0.0-20200128182646-c69cb7680fd4/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0 h1:K02vod+sn3M1OOkdqi2tPxN2+xESK4qyITVQ3JkGEv4=
@@ -225,7 +225,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/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.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@@ -342,8 +342,8 @@ github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
@@ -463,6 +463,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
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/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=
@@ -470,8 +471,11 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
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.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/unknwon/cae v1.0.2 h1:3L8/RCN1ARvD5quyNjU30EdvYkFbxBfnRcIBXugpHlg=
github.com/unknwon/cae v1.0.2/go.mod h1:HqpmD2fVq9G1oGEXrXzbgIp51uJ29Hshv41n9ljm+AA=
@@ -482,8 +486,8 @@ github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6 h1:sRrkJEHtNoaSvyXMbR
github.com/unknwon/i18n v0.0.0-20190805065654-5c6446a380b6/go.mod h1:+5rDk6sDGpl3azws3O+f+GpFSyN9GVr0K8cvQLQM2ZQ=
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e h1:Qf3QQl/zmEbWDajFEiisbKN83hLY+eq2MhbA0I1/two=
github.com/unknwon/paginater v0.0.0-20170405233947-45e5d631308e/go.mod h1:TBwoao3Q4Eb/cp+dHbXDfRTrZSsj/k7kLr2j1oWRWC0=
github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -516,8 +520,9 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
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=
@@ -549,8 +554,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.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.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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=
@@ -591,8 +596,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
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 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
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=
@@ -610,8 +615,10 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/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 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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=
@@ -663,12 +670,13 @@ golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/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.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
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 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
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=
@@ -676,8 +684,9 @@ 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.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 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -725,8 +734,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
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=

View File

@@ -18,7 +18,7 @@ import (
)
func init() {
conf.App.Version = "0.13.0+dev"
conf.App.Version = "0.13.4"
}
func main() {

View File

@@ -112,14 +112,15 @@ func newMacaron() *macaron.Macaron {
},
))
customDir := filepath.Join(conf.CustomDir(), "templates")
renderOpt := macaron.RenderOptions{
Directory: filepath.Join(conf.WorkDir(), "templates"),
AppendDirectories: []string{filepath.Join(conf.CustomDir(), "templates")},
AppendDirectories: []string{customDir},
Funcs: template.FuncMap(),
IndentJSON: macaron.Env != macaron.PROD,
}
if !conf.Server.LoadAssetsFromDisk {
renderOpt.TemplateFileSystem = templates.NewTemplateFileSystem("", renderOpt.AppendDirectories[0])
renderOpt.TemplateFileSystem = templates.NewTemplateFileSystem("", customDir)
}
m.Use(macaron.Renderer(renderOpt))

View File

@@ -361,7 +361,7 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
err = issue.PullRequest.LoadIssue()
if err != nil {
log.Error("LoadIssue: %v", err)
return
return err
}
err = PrepareWebhooks(issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
Action: api.HOOK_ISSUE_LABEL_CLEARED,

View File

@@ -357,7 +357,7 @@ func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err
err = issue.PullRequest.LoadIssue()
if err != nil {
log.Error("LoadIssue: %v", err)
return
return err
}
err = PrepareWebhooks(issue.Repo, HOOK_EVENT_PULL_REQUEST, &api.PullRequestPayload{
Action: hookAction,

View File

@@ -324,7 +324,7 @@ func SyncMirrors() {
m, err := GetMirrorByRepoID(com.StrTo(repoID).MustInt64())
if err != nil {
log.Error("GetMirrorByRepoID [%d]: %v", m.RepoID, err)
log.Error("GetMirrorByRepoID [%v]: %v", repoID, err)
continue
}

View File

@@ -1,4 +1,4 @@
// Code generated by go-mockgen 1.3.3; DO NOT EDIT.
// Code generated by go-mockgen 2.1.1; DO NOT EDIT.
//
// This file was generated by running `go-mockgen` at the root of this repository.
// To add additional mocks to this or another package, add a new entry to the

View File

@@ -125,8 +125,8 @@ func createTag(gitRepo *git.Repository, r *Release) error {
return fmt.Errorf("get branch commit: %v", err)
}
// Trim '--' prefix to prevent command line argument vulnerability.
r.TagName = strings.TrimPrefix(r.TagName, "--")
// 🚨 SECURITY: Trim any leading '-' to prevent command line argument injection.
r.TagName = strings.TrimLeft(r.TagName, "-")
if err = gitRepo.CreateTag(r.TagName, commit.ID.String()); err != nil {
if strings.Contains(err.Error(), "is not a valid tag name") {
return ErrInvalidTagName{r.TagName}

View File

@@ -107,6 +107,19 @@ func (repo *Repository) CheckoutNewBranch(oldBranch, newBranch string) error {
return nil
}
// hasSymlinkInPath returns true if there is any symlink in path hierarchy using
// the given base and relative path.
func hasSymlinkInPath(base, relPath string) bool {
parts := strings.Split(filepath.ToSlash(relPath), "/")
for i := range parts {
filePath := path.Join(append([]string{base}, parts[:i+1]...)...)
if osutil.IsSymlink(filePath) {
return true
}
}
return false
}
type UpdateRepoFileOptions struct {
OldBranch string
NewBranch string
@@ -118,8 +131,8 @@ type UpdateRepoFileOptions struct {
}
// UpdateRepoFile adds or updates a file in repository.
func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (err error) {
// 🚨 SECURITY: Prevent uploading files into the ".git" directory
func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) error {
// 🚨 SECURITY: Prevent uploading files into the ".git" directory.
if isRepositoryGitPath(opts.NewTreeName) {
return errors.Errorf("bad tree path %q", opts.NewTreeName)
}
@@ -127,15 +140,21 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
if err = repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
if err := repo.DiscardLocalRepoBranchChanges(opts.OldBranch); err != nil {
return fmt.Errorf("discard local repo branch[%s] changes: %v", opts.OldBranch, err)
} else if err = repo.UpdateLocalCopyBranch(opts.OldBranch); err != nil {
return fmt.Errorf("update local copy branch[%s]: %v", opts.OldBranch, err)
}
repoPath := repo.RepoPath()
localPath := repo.LocalCopyPath()
// 🚨 SECURITY: Prevent touching files in surprising places, reject operations
// that involve symlinks.
if hasSymlinkInPath(localPath, opts.OldTreeName) || hasSymlinkInPath(localPath, opts.NewTreeName) {
return errors.New("cannot update file with symbolic link in path")
}
repoPath := repo.RepoPath()
if opts.OldBranch != opts.NewBranch {
// Directly return error if new branch already exists in the server
if git.RepoHasBranch(repoPath, opts.NewBranch) {
@@ -144,7 +163,7 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
// Otherwise, delete branch from local copy in case out of sync
if git.RepoHasBranch(localPath, opts.NewBranch) {
if err = git.DeleteBranch(localPath, opts.NewBranch, git.DeleteBranchOptions{
if err := git.DeleteBranch(localPath, opts.NewBranch, git.DeleteBranchOptions{
Force: true,
}); err != nil {
return fmt.Errorf("delete branch %q: %v", opts.NewBranch, err)
@@ -157,35 +176,31 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
}
oldFilePath := path.Join(localPath, opts.OldTreeName)
filePath := path.Join(localPath, opts.NewTreeName)
if err = os.MkdirAll(path.Dir(filePath), os.ModePerm); err != nil {
return err
newFilePath := path.Join(localPath, opts.NewTreeName)
// Prompt the user if the meant-to-be new file already exists.
if osutil.IsExist(newFilePath) && opts.IsNewFile {
return ErrRepoFileAlreadyExist{newFilePath}
}
// If it's meant to be a new file, make sure it doesn't exist.
if opts.IsNewFile {
if com.IsExist(filePath) {
return ErrRepoFileAlreadyExist{filePath}
}
if err := os.MkdirAll(path.Dir(newFilePath), os.ModePerm); err != nil {
return errors.Wrapf(err, "create parent directories of %q", newFilePath)
}
// Ignore move step if it's a new file under a directory.
// Otherwise, move the file when name changed.
if osutil.IsFile(oldFilePath) && opts.OldTreeName != opts.NewTreeName {
if err = git.Move(localPath, opts.OldTreeName, opts.NewTreeName); err != nil {
return fmt.Errorf("git mv %q %q: %v", opts.OldTreeName, opts.NewTreeName, err)
if err := git.Move(localPath, opts.OldTreeName, opts.NewTreeName); err != nil {
return errors.Wrapf(err, "git mv %q %q", opts.OldTreeName, opts.NewTreeName)
}
}
if err = os.WriteFile(filePath, []byte(opts.Content), 0600); err != nil {
if err := os.WriteFile(newFilePath, []byte(opts.Content), 0o600); err != nil {
return fmt.Errorf("write file: %v", err)
}
if err = git.Add(localPath, git.AddOptions{All: true}); err != nil {
if err := git.Add(localPath, git.AddOptions{All: true}); err != nil {
return fmt.Errorf("git add --all: %v", err)
}
err = git.CreateCommit(
err := git.CreateCommit(
localPath,
&git.Signature{
Name: doer.DisplayName(),
@@ -219,11 +234,16 @@ func (repo *Repository) UpdateRepoFile(doer *User, opts UpdateRepoFileOptions) (
}
// GetDiffPreview produces and returns diff result of a file which is not yet committed.
func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *gitutil.Diff, err error) {
func (repo *Repository) GetDiffPreview(branch, treePath, content string) (*gitutil.Diff, error) {
// 🚨 SECURITY: Prevent uploading files into the ".git" directory.
if isRepositoryGitPath(treePath) {
return nil, errors.Errorf("bad tree path %q", treePath)
}
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
if err = repo.DiscardLocalRepoBranchChanges(branch); err != nil {
if err := repo.DiscardLocalRepoBranchChanges(branch); err != nil {
return nil, fmt.Errorf("discard local repo branch[%s] changes: %v", branch, err)
} else if err = repo.UpdateLocalCopyBranch(branch); err != nil {
return nil, fmt.Errorf("update local copy branch[%s]: %v", branch, err)
@@ -231,14 +251,21 @@ func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *
localPath := repo.LocalCopyPath()
filePath := path.Join(localPath, treePath)
if err = os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
return nil, err
// 🚨 SECURITY: Prevent touching files in surprising places, reject operations
// that involve symlinks.
if hasSymlinkInPath(localPath, treePath) {
return nil, errors.New("cannot update file with symbolic link in path")
}
if err = os.WriteFile(filePath, []byte(content), 0600); err != nil {
if err := os.MkdirAll(path.Dir(filePath), os.ModePerm); err != nil {
return nil, errors.Wrap(err, "create parent directories")
} else if err = os.WriteFile(filePath, []byte(content), 0o600); err != nil {
return nil, fmt.Errorf("write file: %v", err)
}
cmd := exec.Command("git", "diff", treePath)
// 🚨 SECURITY: Prevent including unintended options in the path to the Git command.
cmd := exec.Command("git", "diff", "--end-of-options", treePath)
cmd.Dir = localPath
cmd.Stderr = os.Stderr
@@ -254,15 +281,13 @@ func (repo *Repository) GetDiffPreview(branch, treePath, content string) (diff *
pid := process.Add(fmt.Sprintf("GetDiffPreview [repo_path: %s]", repo.RepoPath()), cmd)
defer process.Remove(pid)
diff, err = gitutil.ParseDiff(stdout, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars)
diff, err := gitutil.ParseDiff(stdout, conf.Git.MaxDiffFiles, conf.Git.MaxDiffLines, conf.Git.MaxDiffLineChars)
if err != nil {
return nil, fmt.Errorf("parse diff: %v", err)
}
if err = cmd.Wait(); err != nil {
return nil, fmt.Errorf("wait: %v", err)
}
return diff, nil
}
@@ -283,6 +308,11 @@ type DeleteRepoFileOptions struct {
}
func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (err error) {
// 🚨 SECURITY: Prevent uploading files into the ".git" directory.
if isRepositoryGitPath(opts.TreePath) {
return errors.Errorf("bad tree path %q", opts.TreePath)
}
repoWorkingPool.CheckIn(com.ToStr(repo.ID))
defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))
@@ -292,14 +322,22 @@ func (repo *Repository) DeleteRepoFile(doer *User, opts DeleteRepoFileOptions) (
return fmt.Errorf("update local copy branch[%s]: %v", opts.OldBranch, err)
}
localPath := repo.LocalCopyPath()
// 🚨 SECURITY: Prevent touching files in surprising places, reject operations
// that involve symlinks.
if hasSymlinkInPath(localPath, opts.TreePath) {
return errors.New("cannot update file with symbolic link in path")
}
if opts.OldBranch != opts.NewBranch {
if err := repo.CheckoutNewBranch(opts.OldBranch, opts.NewBranch); err != nil {
return fmt.Errorf("checkout new branch[%s] from old branch[%s]: %v", opts.NewBranch, opts.OldBranch, err)
}
}
localPath := repo.LocalCopyPath()
if err = os.Remove(path.Join(localPath, opts.TreePath)); err != nil {
filePath := path.Join(localPath, opts.TreePath)
if err = os.Remove(filePath); err != nil {
return fmt.Errorf("remove file %q: %v", opts.TreePath, err)
}
@@ -503,7 +541,7 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
return nil
}
// 🚨 SECURITY: Prevent uploading files into the ".git" directory
// 🚨 SECURITY: Prevent uploading files into the ".git" directory.
if isRepositoryGitPath(opts.TreePath) {
return errors.Errorf("bad tree path %q", opts.TreePath)
}
@@ -541,14 +579,22 @@ func (repo *Repository) UploadRepoFiles(doer *User, opts UploadRepoFileOptions)
continue
}
// 🚨 SECURITY: Prevent path traversal.
upload.Name = pathutil.Clean(upload.Name)
// 🚨 SECURITY: Prevent uploading files into the ".git" directory
// 🚨 SECURITY: Prevent uploading files into the ".git" directory.
if isRepositoryGitPath(upload.Name) {
continue
}
targetPath := path.Join(dirPath, upload.Name)
// 🚨 SECURITY: Prevent updating files in surprising place, check if the target
// is a symlink.
if osutil.IsSymlink(targetPath) {
return fmt.Errorf("cannot overwrite symbolic link: %s", upload.Name)
}
if err = com.Copy(tmpPath, targetPath); err != nil {
return fmt.Errorf("copy: %v", err)
}

View File

@@ -447,8 +447,9 @@ func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
return key, nil
}
// SearchPublicKeyByContent searches content as prefix (leak e-mail part)
// and returns public key found.
// SearchPublicKeyByContent searches a public key using the content as prefix
// (i.e. ignore the email part). It returns ErrKeyNotExist if no such key
// exists.
func SearchPublicKeyByContent(content string) (*PublicKey, error) {
key := new(PublicKey)
has, err := x.Where("content like ?", content+"%").Get(key)

View File

@@ -21,12 +21,6 @@ func Test_SSHParsePublicKey(t *testing.T) {
expType string
expLength int
}{
{
name: "dsa-1024",
content: "ssh-dss AAAAB3NzaC1kc3MAAACBAOChCC7lf6Uo9n7BmZ6M8St19PZf4Tn59NriyboW2x/DZuYAz3ibZ2OkQ3S0SqDIa0HXSEJ1zaExQdmbO+Ux/wsytWZmCczWOVsaszBZSl90q8UnWlSH6P+/YA+RWJm5SFtuV9PtGIhyZgoNuz5kBQ7K139wuQsecdKktISwTakzAAAAFQCzKsO2JhNKlL+wwwLGOcLffoAmkwAAAIBpK7/3xvduajLBD/9vASqBQIHrgK2J+wiQnIb/Wzy0UsVmvfn8A+udRbBo+csM8xrSnlnlJnjkJS3qiM5g+eTwsLIV1IdKPEwmwB+VcP53Cw6lSyWyJcvhFb0N6s08NZysLzvj0N+ZC/FnhKTLzIyMtkHf/IrPCwlM+pV/M/96YgAAAIEAqQcGn9CKgzgPaguIZooTAOQdvBLMI5y0bQjOW6734XOpqQGf/Kra90wpoasLKZjSYKNPjE+FRUOrStLrxcNs4BeVKhy2PYTRnybfYVk1/dmKgH6P1YSRONsGKvTsH6c5IyCRG0ncCgYeF8tXppyd642982daopE7zQ/NPAnJfag= nocomment",
expType: "dsa",
expLength: 1024,
},
{
name: "rsa-1024",
content: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDAu7tvIvX6ZHrRXuZNfkR3XLHSsuCK9Zn3X58lxBcQzuo5xZgB6vRwwm/QtJuF+zZPtY5hsQILBLmF+BZ5WpKZp1jBeSjH2G7lxet9kbcH+kIVj0tPFEoyKI9wvWqIwC4prx/WVk2wLTJjzBAhyNxfEq7C9CeiX9pQEbEqJfkKCQ== nocomment",

View File

@@ -109,21 +109,3 @@ func IsTwoFactorRecoveryCodeNotFound(err error) bool {
func (err ErrTwoFactorRecoveryCodeNotFound) Error() string {
return fmt.Sprintf("two-factor recovery code does not found [code: %s]", err.Code)
}
// UseRecoveryCode validates recovery code of given user and marks it is used if valid.
func UseRecoveryCode(_ int64, code string) error {
recoveryCode := new(TwoFactorRecoveryCode)
has, err := x.Where("code = ?", code).And("is_used = ?", false).Get(recoveryCode)
if err != nil {
return fmt.Errorf("get unused code: %v", err)
} else if !has {
return ErrTwoFactorRecoveryCodeNotFound{Code: code}
}
recoveryCode.IsUsed = true
if _, err = x.Id(recoveryCode.ID).Cols("is_used").Update(recoveryCode); err != nil {
return fmt.Errorf("mark code as used: %v", err)
}
return nil
}

View File

@@ -32,6 +32,7 @@ type TwoFactorsStore interface {
GetByUserID(ctx context.Context, userID int64) (*TwoFactor, error)
// IsEnabled returns true if the user has enabled 2FA.
IsEnabled(ctx context.Context, userID int64) bool
UseRecoveryCode(ctx context.Context, userID int64, code string) error
}
var TwoFactors TwoFactorsStore
@@ -121,6 +122,28 @@ func (db *twoFactors) IsEnabled(ctx context.Context, userID int64) bool {
return count > 0
}
// UseRecoveryCode validates a recovery code of given user and marks it as used
// if valid. It returns ErrTwoFactorRecoveryCodeNotFound if the code is invalid
// or already used.
func (db *twoFactors) UseRecoveryCode(ctx context.Context, userID int64, code string) error {
return db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var recoveryCode TwoFactorRecoveryCode
err := tx.Where("user_id = ? AND code = ? AND is_used = ?", userID, code, false).First(&recoveryCode).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return ErrTwoFactorRecoveryCodeNotFound{Code: code}
}
return errors.Wrap(err, "get unused recovery code")
}
err = tx.Model(&recoveryCode).Update("is_used", true).Error
if err != nil {
return errors.Wrap(err, "mark recovery code as used")
}
return nil
})
}
// generateRecoveryCodes generates N number of recovery codes for 2FA.
func generateRecoveryCodes(userID int64, n int) ([]*TwoFactorRecoveryCode, error) {
recoveryCodes := make([]*TwoFactorRecoveryCode, n)

View File

@@ -79,6 +79,7 @@ func TestTwoFactors(t *testing.T) {
{"Create", twoFactorsCreate},
{"GetByUserID", twoFactorsGetByUserID},
{"IsEnabled", twoFactorsIsEnabled},
{"UseRecoveryCode", twoFactorsUseRecoveryCode},
} {
t.Run(tc.name, func(t *testing.T) {
t.Cleanup(func() {
@@ -139,3 +140,57 @@ func twoFactorsIsEnabled(t *testing.T, db *twoFactors) {
assert.True(t, db.IsEnabled(ctx, 1))
assert.False(t, db.IsEnabled(ctx, 2))
}
func twoFactorsUseRecoveryCode(t *testing.T, db *twoFactors) {
ctx := context.Background()
// Create 2FA tokens for two users
err := db.Create(ctx, 1, "secure-key", "secure-secret")
require.NoError(t, err)
err = db.Create(ctx, 2, "secure-key", "secure-secret")
require.NoError(t, err)
// Get recovery codes for both users
var user1Codes []TwoFactorRecoveryCode
err = db.DB.Where("user_id = ?", 1).Find(&user1Codes).Error
require.NoError(t, err)
require.NotEmpty(t, user1Codes)
var user2Codes []TwoFactorRecoveryCode
err = db.DB.Where("user_id = ?", 2).Find(&user2Codes).Error
require.NoError(t, err)
require.NotEmpty(t, user2Codes)
// User 1 should be able to use their own recovery code
err = db.UseRecoveryCode(ctx, 1, user1Codes[0].Code)
require.NoError(t, err)
// Verify the code is now marked as used
var usedCode TwoFactorRecoveryCode
err = db.DB.Where("id = ?", user1Codes[0].ID).First(&usedCode).Error
require.NoError(t, err)
assert.True(t, usedCode.IsUsed)
// User 1 should NOT be able to use user 2's recovery code
// This is the key security test - recovery codes must be scoped by user
err = db.UseRecoveryCode(ctx, 1, user2Codes[0].Code)
assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected recovery code not found error when using another user's code")
// User 2's code should still be unused
var user2Code TwoFactorRecoveryCode
err = db.DB.Where("id = ?", user2Codes[0].ID).First(&user2Code).Error
require.NoError(t, err)
assert.False(t, user2Code.IsUsed, "user 2's recovery code should not be marked as used")
// User 2 should be able to use their own code
err = db.UseRecoveryCode(ctx, 2, user2Codes[0].Code)
require.NoError(t, err)
// Using an already-used code should fail
err = db.UseRecoveryCode(ctx, 1, user1Codes[0].Code)
assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected error when reusing a recovery code")
// Using a non-existent code should fail
err = db.UseRecoveryCode(ctx, 1, "invalid-code")
assert.True(t, IsTwoFactorRecoveryCodeNotFound(err), "expected error for invalid recovery code")
}

View File

@@ -18,23 +18,33 @@ import (
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/pathutil"
"gogs.io/gogs/internal/repoutil"
"gogs.io/gogs/internal/sync"
)
var wikiWorkingPool = sync.NewExclusivePool()
// WikiBranch returns the branch name used by the wiki repository. It checks if
// "main" branch exists, otherwise falls back to "master".
func WikiBranch(repoPath string) string {
if git.RepoHasBranch(repoPath, "main") {
return "main"
}
return "master"
}
// ToWikiPageURL formats a string to corresponding wiki URL name.
func ToWikiPageURL(name string) string {
return url.QueryEscape(name)
}
// ToWikiPageName formats a URL back to corresponding wiki page name,
// and removes leading characters './' to prevent changing files
// that are not belong to wiki repository.
// ToWikiPageName formats a URL back to corresponding wiki page name. It enforces
// single-level hierarchy by replacing all "/" with spaces.
func ToWikiPageName(urlString string) string {
name, _ := url.QueryUnescape(urlString)
return strings.ReplaceAll(strings.TrimLeft(path.Clean("/"+name), "/"), "/", " ")
name = pathutil.Clean(name)
return strings.ReplaceAll(name, "/", " ")
}
// WikiCloneLink returns clone URLs of repository wiki.
@@ -79,24 +89,25 @@ func (repo *Repository) LocalWikiPath() string {
// UpdateLocalWiki makes sure the local copy of repository wiki is up-to-date.
func (repo *Repository) UpdateLocalWiki() error {
return UpdateLocalCopyBranch(repo.WikiPath(), repo.LocalWikiPath(), "master", true)
wikiPath := repo.WikiPath()
return UpdateLocalCopyBranch(wikiPath, repo.LocalWikiPath(), WikiBranch(wikiPath), true)
}
func discardLocalWikiChanges(localPath string) error {
return discardLocalRepoBranchChanges(localPath, "master")
return discardLocalRepoBranchChanges(localPath, WikiBranch(localPath))
}
// updateWikiPage adds new page to repository wiki.
func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, message string, isNew bool) (err error) {
func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, message string, isNew bool) error {
wikiWorkingPool.CheckIn(com.ToStr(repo.ID))
defer wikiWorkingPool.CheckOut(com.ToStr(repo.ID))
if err = repo.InitWiki(); err != nil {
if err := repo.InitWiki(); err != nil {
return fmt.Errorf("InitWiki: %v", err)
}
localPath := repo.LocalWikiPath()
if err = discardLocalWikiChanges(localPath); err != nil {
if err := discardLocalWikiChanges(localPath); err != nil {
return fmt.Errorf("discardLocalWikiChanges: %v", err)
} else if err = repo.UpdateLocalWiki(); err != nil {
return fmt.Errorf("UpdateLocalWiki: %v", err)
@@ -111,7 +122,8 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
return ErrWikiAlreadyExist{filename}
}
} else {
os.Remove(path.Join(localPath, oldTitle+".md"))
oldTitle = ToWikiPageName(oldTitle)
_ = os.Remove(path.Join(localPath, oldTitle+".md"))
}
// SECURITY: if new file is a symlink to non-exist critical file,
@@ -119,20 +131,20 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
// as a new page operation.
// So we want to make sure the symlink is removed before write anything.
// The new file we created will be in normal text format.
os.Remove(filename)
_ = os.Remove(filename)
if err = os.WriteFile(filename, []byte(content), 0666); err != nil {
if err := os.WriteFile(filename, []byte(content), 0o666); err != nil {
return fmt.Errorf("WriteFile: %v", err)
}
if message == "" {
message = "Update page '" + title + "'"
}
if err = git.Add(localPath, git.AddOptions{All: true}); err != nil {
if err := git.Add(localPath, git.AddOptions{All: true}); err != nil {
return fmt.Errorf("add all changes: %v", err)
}
err = git.CreateCommit(
err := git.CreateCommit(
localPath,
&git.Signature{
Name: doer.DisplayName(),
@@ -143,7 +155,7 @@ func (repo *Repository) updateWikiPage(doer *User, oldTitle, title, content, mes
)
if err != nil {
return fmt.Errorf("commit changes: %v", err)
} else if err = git.Push(localPath, "origin", "master"); err != nil {
} else if err = git.Push(localPath, "origin", WikiBranch(localPath)); err != nil {
return fmt.Errorf("push: %v", err)
}
@@ -170,8 +182,7 @@ func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
}
title = ToWikiPageName(title)
filename := path.Join(localPath, title+".md")
os.Remove(filename)
_ = os.Remove(path.Join(localPath, title+".md"))
message := "Delete page '" + title + "'"
@@ -190,7 +201,7 @@ func (repo *Repository) DeleteWikiPage(doer *User, title string) (err error) {
)
if err != nil {
return fmt.Errorf("commit changes: %v", err)
} else if err = git.Push(localPath, "origin", "master"); err != nil {
} else if err = git.Push(localPath, "origin", WikiBranch(localPath)); err != nil {
return fmt.Errorf("push: %v", err)
}

27
internal/db/wiki_test.go Normal file
View File

@@ -0,0 +1,27 @@
package db
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestToWikiPageName(t *testing.T) {
tests := []struct {
input string
want string
}{
{input: "Home", want: "Home"},
{input: "../../../../tmp/target_file", want: "tmp target_file"},
{input: "..\\..\\..\\..\\tmp\\target_file", want: "tmp target_file"},
{input: "A/B", want: "A B"},
{input: "../pwn", want: "pwn"},
}
for _, test := range tests {
t.Run(test.input, func(t *testing.T) {
got := ToWikiPageName(test.input)
require.Equal(t, test.want, got)
})
}
}

View File

@@ -40,9 +40,10 @@ var (
// render renders a mail template with given data.
func render(tpl string, data map[string]any) (string, error) {
tplRenderOnce.Do(func() {
customDir := filepath.Join(conf.CustomDir(), "templates")
opt := &macaron.RenderOptions{
Directory: filepath.Join(conf.WorkDir(), "templates", "mail"),
AppendDirectories: []string{filepath.Join(conf.CustomDir(), "templates", "mail")},
AppendDirectories: []string{filepath.Join(customDir, "mail")},
Extensions: []string{".tmpl", ".html"},
Funcs: []template.FuncMap{map[string]any{
"AppName": func() string {
@@ -60,7 +61,7 @@ func render(tpl string, data map[string]any) (string, error) {
}},
}
if !conf.Server.LoadAssetsFromDisk {
opt.TemplateFileSystem = templates.NewTemplateFileSystem("mail", opt.AppendDirectories[0])
opt.TemplateFileSystem = templates.NewTemplateFileSystem("mail", customDir)
}
ts := macaron.NewTemplateSet()

View File

@@ -9,7 +9,8 @@ import (
"os/user"
)
// IsFile returns true if given path exists as a file (i.e. not a directory).
// IsFile returns true if given path exists as a file (i.e. not a directory)
// following any symlinks.
func IsFile(path string) bool {
f, e := os.Stat(path)
if e != nil {
@@ -18,8 +19,8 @@ func IsFile(path string) bool {
return !f.IsDir()
}
// IsDir returns true if given path is a directory, and returns false when it's
// a file or does not exist.
// IsDir returns true if given path is a directory following any symlinks, and
// returns false when it's a file or does not exist.
func IsDir(dir string) bool {
f, e := os.Stat(dir)
if e != nil {
@@ -28,12 +29,21 @@ func IsDir(dir string) bool {
return f.IsDir()
}
// IsExist returns true if a file or directory exists.
// IsExist returns true if a file or directory exists following any symlinks.
func IsExist(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
// IsSymlink returns true if given path is a symbolic link.
func IsSymlink(path string) bool {
fileInfo, err := os.Lstat(path)
if err != nil {
return false
}
return fileInfo.Mode()&os.ModeSymlink != 0
}
// CurrentUsername returns the username of the current user.
func CurrentUsername() string {
username := os.Getenv("USER")

View File

@@ -9,50 +9,51 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestIsFile(t *testing.T) {
tests := []struct {
path string
expVal bool
path string
want bool
}{
{
path: "osutil.go",
expVal: true,
path: "osutil.go",
want: true,
}, {
path: "../osutil",
expVal: false,
path: "../osutil",
want: false,
}, {
path: "not_found",
expVal: false,
path: "not_found",
want: false,
},
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
assert.Equal(t, test.expVal, IsFile(test.path))
assert.Equal(t, test.want, IsFile(test.path))
})
}
}
func TestIsDir(t *testing.T) {
tests := []struct {
path string
expVal bool
path string
want bool
}{
{
path: "osutil.go",
expVal: false,
path: "osutil.go",
want: false,
}, {
path: "../osutil",
expVal: true,
path: "../osutil",
want: true,
}, {
path: "not_found",
expVal: false,
path: "not_found",
want: false,
},
}
for _, test := range tests {
t.Run("", func(t *testing.T) {
assert.Equal(t, test.expVal, IsDir(test.path))
assert.Equal(t, test.want, IsDir(test.path))
})
}
}
@@ -82,13 +83,53 @@ func TestIsExist(t *testing.T) {
func TestCurrentUsername(t *testing.T) {
if oldUser, ok := os.LookupEnv("USER"); ok {
defer func() { _ = os.Setenv("USER", oldUser) }()
defer func() { t.Setenv("USER", oldUser) }()
} else {
defer func() { _ = os.Unsetenv("USER") }()
}
if err := os.Setenv("USER", "__TESTING::USERNAME"); err != nil {
t.Skip("Could not set the USER environment variable:", err)
}
t.Setenv("USER", "__TESTING::USERNAME")
assert.Equal(t, "__TESTING::USERNAME", CurrentUsername())
}
func TestIsSymlink(t *testing.T) {
// Create a temporary file
tempFile, err := os.CreateTemp("", "symlink-test-*")
require.NoError(t, err, "create temporary file")
tempFilePath := tempFile.Name()
_ = tempFile.Close()
defer func() { _ = os.Remove(tempFilePath) }()
// Create a temporary symlink
tempSymlinkPath := tempFilePath + "-symlink"
err = os.Symlink(tempFilePath, tempSymlinkPath)
require.NoError(t, err, "create temporary symlink")
defer func() { _ = os.Remove(tempSymlinkPath) }()
tests := []struct {
name string
path string
want bool
}{
{
name: "non-existent path",
path: "not_found",
want: false,
},
{
name: "regular file",
path: tempFilePath,
want: false,
},
{
name: "symlink",
path: tempSymlinkPath,
want: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
assert.Equal(t, test.want, IsSymlink(test.path))
})
}
}

View File

@@ -11,6 +11,9 @@ import (
// Clean cleans up given path and returns a relative path that goes straight
// down to prevent path traversal.
//
// 🚨 SECURITY: This function MUST be used for any user input that is used as
// file system path to prevent path traversal.
func Clean(p string) string {
p = strings.ReplaceAll(p, `\`, "/")
return strings.Trim(path.Clean("/"+p), "/")

View File

@@ -148,7 +148,7 @@ func reqRepoWriter() macaron.Handler {
}
}
// reqRepoWriter makes sure the context user has at least admin access to the repository.
// reqRepoAdmin makes sure the context user has at least admin access to the repository.
func reqRepoAdmin() macaron.Handler {
return func(c *context.Context) {
if !c.Repo.IsAdmin() {
@@ -158,6 +158,16 @@ func reqRepoAdmin() macaron.Handler {
}
}
// reqRepoOwner makes sure the context user has owner access to the repository.
func reqRepoOwner() macaron.Handler {
return func(c *context.Context) {
if !c.Repo.IsOwner() {
c.Status(http.StatusForbidden)
return
}
}
}
func mustEnableIssues(c *context.APIContext) {
if !c.Repo.Repository.EnableIssues || c.Repo.Repository.EnableExternalTracker {
c.NotFound()
@@ -250,7 +260,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Group("/repos", func() {
m.Post("/migrate", bind(form.MigrateRepo{}), repo.Migrate)
m.Delete("/:username/:reponame", repoAssignment(), repo.Delete)
m.Delete("/:username/:reponame", repoAssignment(), reqRepoOwner(), repo.Delete)
m.Group("/:username/:reponame", func() {
m.Group("/hooks", func() {
@@ -275,7 +285,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("", repo.GetContents)
m.Combo("/*").
Get(repo.GetContents).
Put(bind(repo.PutContentsRequest{}), repo.PutContents)
Put(reqRepoWriter(), bind(repo.PutContentsRequest{}), repo.PutContents)
})
m.Get("/archive/*", repo.GetArchive)
m.Group("/git", func() {

View File

@@ -16,6 +16,7 @@ import (
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/gitutil"
"gogs.io/gogs/internal/pathutil"
"gogs.io/gogs/internal/repoutil"
)
@@ -120,7 +121,8 @@ func GetContents(c *context.APIContext) {
return
}
treePath := c.Params("*")
// 🚨 SECURITY: Prevent path traversal.
treePath := pathutil.Clean(c.Params("*"))
entry, err := commit.TreeEntry(treePath)
if err != nil {
c.NotFoundOrError(gitutil.NewError(err), "get tree entry")
@@ -188,7 +190,10 @@ func PutContents(c *context.APIContext, r PutContentsRequest) {
if r.Branch == "" {
r.Branch = c.Repo.Repository.DefaultBranch
}
treePath := c.Params("*")
// 🚨 SECURITY: Prevent path traversal.
treePath := pathutil.Clean(c.Params("*"))
err = c.Repo.Repository.UpdateRepoFile(
c.User,
db.UpdateRepoFileOptions{

View File

@@ -116,19 +116,28 @@ func listUserRepositories(c *context.APIContext, username string) {
return
}
accessibleRepos, err := db.Repos.GetByCollaboratorIDWithAccessMode(c.Req.Context(), user.ID)
accessibleReposWithAccessMode, err := db.Repos.GetByCollaboratorIDWithAccessMode(c.Req.Context(), user.ID)
if err != nil {
c.Error(err, "get repositories accesses by collaborator")
return
}
accessibleRepos := make([]*db.Repository, 0, len(accessibleReposWithAccessMode))
for repo := range accessibleReposWithAccessMode {
accessibleRepos = append(accessibleRepos, repo)
}
if err = db.RepositoryList(accessibleRepos).LoadAttributes(); err != nil {
c.Error(err, "load attributes for accessible repositories")
return
}
numOwnRepos := len(ownRepos)
repos := make([]*api.Repository, 0, numOwnRepos+len(accessibleRepos))
repos := make([]*api.Repository, 0, numOwnRepos+len(accessibleReposWithAccessMode))
for _, r := range ownRepos {
repos = append(repos, r.APIFormatLegacy(&api.Permission{Admin: true, Push: true, Pull: true}))
}
for repo, access := range accessibleRepos {
for repo, access := range accessibleReposWithAccessMode {
repos = append(repos,
repo.APIFormatLegacy(&api.Permission{
Admin: access >= db.AccessModeAdmin,

View File

@@ -338,13 +338,13 @@ func InstallPost(c *context.Context, f form.Install) {
}
if len(strings.TrimSpace(f.SMTPHost)) > 0 {
cfg.Section("mailer").Key("ENABLED").SetValue("true")
cfg.Section("mailer").Key("HOST").SetValue(f.SMTPHost)
cfg.Section("mailer").Key("FROM").SetValue(f.SMTPFrom)
cfg.Section("mailer").Key("USER").SetValue(f.SMTPUser)
cfg.Section("mailer").Key("PASSWD").SetValue(f.SMTPPasswd)
cfg.Section("email").Key("ENABLED").SetValue("true")
cfg.Section("email").Key("HOST").SetValue(f.SMTPHost)
cfg.Section("email").Key("FROM").SetValue(f.SMTPFrom)
cfg.Section("email").Key("USER").SetValue(f.SMTPUser)
cfg.Section("email").Key("PASSWORD").SetValue(f.SMTPPasswd)
} else {
cfg.Section("mailer").Key("ENABLED").SetValue("false")
cfg.Section("email").Key("ENABLED").SetValue("false")
}
cfg.Section("server").Key("OFFLINE_MODE").SetValue(com.ToStr(f.OfflineMode))
cfg.Section("auth").Key("REQUIRE_EMAIL_CONFIRMATION").SetValue(com.ToStr(f.RegisterConfirm))

View File

@@ -1,4 +1,4 @@
// Code generated by go-mockgen 1.3.3; DO NOT EDIT.
// Code generated by go-mockgen 2.1.1; DO NOT EDIT.
//
// This file was generated by running `go-mockgen` at the root of this repository.
// To add additional mocks to this or another package, add a new entry to the
@@ -2780,6 +2780,9 @@ type MockTwoFactorsStore struct {
// IsEnabledFunc is an instance of a mock function object controlling
// the behavior of the method IsEnabled.
IsEnabledFunc *TwoFactorsStoreIsEnabledFunc
// UseRecoveryCodeFunc is an instance of a mock function object
// controlling the behavior of the method UseRecoveryCode.
UseRecoveryCodeFunc *TwoFactorsStoreUseRecoveryCodeFunc
}
// NewMockTwoFactorsStore creates a new mock of the TwoFactorsStore
@@ -2802,6 +2805,11 @@ func NewMockTwoFactorsStore() *MockTwoFactorsStore {
return
},
},
UseRecoveryCodeFunc: &TwoFactorsStoreUseRecoveryCodeFunc{
defaultHook: func(context.Context, int64, string) (r0 error) {
return
},
},
}
}
@@ -2824,6 +2832,11 @@ func NewStrictMockTwoFactorsStore() *MockTwoFactorsStore {
panic("unexpected invocation of MockTwoFactorsStore.IsEnabled")
},
},
UseRecoveryCodeFunc: &TwoFactorsStoreUseRecoveryCodeFunc{
defaultHook: func(context.Context, int64, string) error {
panic("unexpected invocation of MockTwoFactorsStore.UseRecoveryCode")
},
},
}
}
@@ -2841,6 +2854,9 @@ func NewMockTwoFactorsStoreFrom(i db.TwoFactorsStore) *MockTwoFactorsStore {
IsEnabledFunc: &TwoFactorsStoreIsEnabledFunc{
defaultHook: i.IsEnabled,
},
UseRecoveryCodeFunc: &TwoFactorsStoreUseRecoveryCodeFunc{
defaultHook: i.UseRecoveryCode,
},
}
}
@@ -3168,6 +3184,116 @@ func (c TwoFactorsStoreIsEnabledFuncCall) Results() []interface{} {
return []interface{}{c.Result0}
}
// TwoFactorsStoreUseRecoveryCodeFunc describes the behavior when the
// UseRecoveryCode method of the parent MockTwoFactorsStore instance is
// invoked.
type TwoFactorsStoreUseRecoveryCodeFunc struct {
defaultHook func(context.Context, int64, string) error
hooks []func(context.Context, int64, string) error
history []TwoFactorsStoreUseRecoveryCodeFuncCall
mutex sync.Mutex
}
// UseRecoveryCode delegates to the next hook function in the queue and
// stores the parameter and result values of this invocation.
func (m *MockTwoFactorsStore) UseRecoveryCode(v0 context.Context, v1 int64, v2 string) error {
r0 := m.UseRecoveryCodeFunc.nextHook()(v0, v1, v2)
m.UseRecoveryCodeFunc.appendCall(TwoFactorsStoreUseRecoveryCodeFuncCall{v0, v1, v2, r0})
return r0
}
// SetDefaultHook sets function that is called when the UseRecoveryCode
// method of the parent MockTwoFactorsStore instance is invoked and the hook
// queue is empty.
func (f *TwoFactorsStoreUseRecoveryCodeFunc) SetDefaultHook(hook func(context.Context, int64, string) error) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// UseRecoveryCode method of the parent MockTwoFactorsStore instance invokes
// the hook at the front of the queue and discards it. After the queue is
// empty, the default hook function is invoked for any future action.
func (f *TwoFactorsStoreUseRecoveryCodeFunc) PushHook(hook func(context.Context, int64, string) error) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
}
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *TwoFactorsStoreUseRecoveryCodeFunc) SetDefaultReturn(r0 error) {
f.SetDefaultHook(func(context.Context, int64, string) error {
return r0
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *TwoFactorsStoreUseRecoveryCodeFunc) PushReturn(r0 error) {
f.PushHook(func(context.Context, int64, string) error {
return r0
})
}
func (f *TwoFactorsStoreUseRecoveryCodeFunc) nextHook() func(context.Context, int64, string) error {
f.mutex.Lock()
defer f.mutex.Unlock()
if len(f.hooks) == 0 {
return f.defaultHook
}
hook := f.hooks[0]
f.hooks = f.hooks[1:]
return hook
}
func (f *TwoFactorsStoreUseRecoveryCodeFunc) appendCall(r0 TwoFactorsStoreUseRecoveryCodeFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of TwoFactorsStoreUseRecoveryCodeFuncCall
// objects describing the invocations of this function.
func (f *TwoFactorsStoreUseRecoveryCodeFunc) History() []TwoFactorsStoreUseRecoveryCodeFuncCall {
f.mutex.Lock()
history := make([]TwoFactorsStoreUseRecoveryCodeFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// TwoFactorsStoreUseRecoveryCodeFuncCall is an object that describes an
// invocation of method UseRecoveryCode on an instance of
// MockTwoFactorsStore.
type TwoFactorsStoreUseRecoveryCodeFuncCall struct {
// Arg0 is the value of the 1st argument passed to this method
// invocation.
Arg0 context.Context
// Arg1 is the value of the 2nd argument passed to this method
// invocation.
Arg1 int64
// Arg2 is the value of the 3rd argument passed to this method
// invocation.
Arg2 string
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 error
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c TwoFactorsStoreUseRecoveryCodeFuncCall) Args() []interface{} {
return []interface{}{c.Arg0, c.Arg1, c.Arg2}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c TwoFactorsStoreUseRecoveryCodeFuncCall) Results() []interface{} {
return []interface{}{c.Result0}
}
// MockUsersStore is a mock implementation of the UsersStore interface (from
// the package gogs.io/gogs/internal/db) used for unit testing.
type MockUsersStore struct {

View File

@@ -135,6 +135,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
branchName = f.NewBranchName
}
// 🚨 SECURITY: Prevent path traversal.
f.TreePath = pathutil.Clean(f.TreePath)
treeNames, treePaths := getParentTreeFields(f.TreePath)
@@ -192,6 +193,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
return
}
} else {
// 🚨 SECURITY: Do not allow editing if the target file is a symlink.
if entry.IsSymlink() {
c.FormErr("TreePath")
c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", part), tmplEditorEdit, &f)
@@ -205,7 +207,7 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
}
if !isNewFile {
_, err := c.Repo.Commit.TreeEntry(oldTreePath)
entry, err := c.Repo.Commit.TreeEntry(oldTreePath)
if err != nil {
if gitutil.IsErrRevisionNotExist(err) {
c.FormErr("TreePath")
@@ -215,6 +217,14 @@ func editFilePost(c *context.Context, f form.EditRepoFile, isNewFile bool) {
}
return
}
// 🚨 SECURITY: Do not allow editing if the old file is a symlink.
if entry.IsSymlink() {
c.FormErr("TreePath")
c.RenderWithErr(c.Tr("repo.editor.file_is_a_symlink", oldTreePath), tmplEditorEdit, &f)
return
}
if lastCommit != c.Repo.CommitID {
files, err := c.Repo.Commit.FilesChangedAfter(lastCommit)
if err != nil {
@@ -292,7 +302,8 @@ func NewFilePost(c *context.Context, f form.EditRepoFile) {
}
func DiffPreviewPost(c *context.Context, f form.EditPreviewDiff) {
treePath := c.Repo.TreePath
// 🚨 SECURITY: Prevent path traversal.
treePath := pathutil.Clean(c.Repo.TreePath)
entry, err := c.Repo.Commit.TreeEntry(treePath)
if err != nil {
@@ -333,6 +344,7 @@ func DeleteFilePost(c *context.Context, f form.DeleteRepoFile) {
c.PageIs("Delete")
c.Data["BranchLink"] = c.Repo.RepoLink + "/src/" + c.Repo.BranchName
// 🚨 SECURITY: Prevent path traversal.
c.Repo.TreePath = pathutil.Clean(c.Repo.TreePath)
c.Data["TreePath"] = c.Repo.TreePath
@@ -428,6 +440,7 @@ func UploadFilePost(c *context.Context, f form.UploadRepoFile) {
branchName = f.NewBranchName
}
// 🚨 SECURITY: Prevent path traversal.
f.TreePath = pathutil.Clean(f.TreePath)
treeNames, treePaths := getParentTreeFields(f.TreePath)
if len(treeNames) == 0 {

View File

@@ -411,6 +411,7 @@ func HTTP(c *HTTPContext) {
return
}
// 🚨 SECURITY: Prevent path traversal.
cleaned := pathutil.Clean(m[1])
if m[1] != "/"+cleaned {
c.Error(http.StatusBadRequest, "Request path contains suspicious characters")

View File

@@ -582,13 +582,27 @@ func SettingsGitHooks(c *context.Context) {
c.Success(SETTINGS_GITHOOKS)
}
func isValidHookName(name git.HookName) bool {
for _, h := range git.ServerSideHooks {
if h == name {
return true
}
}
return false
}
func SettingsGitHooksEdit(c *context.Context) {
c.Data["Title"] = c.Tr("repo.settings.githooks")
c.Data["PageIsSettingsGitHooks"] = true
c.Data["RequireSimpleMDE"] = true
name := c.Params(":name")
hook, err := c.Repo.GitRepo.Hook("custom_hooks", git.HookName(name))
name := git.HookName(c.Params(":name"))
if !isValidHookName(name) {
c.NotFound()
return
}
hook, err := c.Repo.GitRepo.Hook("custom_hooks", name)
if err != nil {
c.NotFoundOrError(osutil.NewError(err), "get hook")
return
@@ -598,8 +612,13 @@ func SettingsGitHooksEdit(c *context.Context) {
}
func SettingsGitHooksEditPost(c *context.Context) {
name := c.Params(":name")
hook, err := c.Repo.GitRepo.Hook("custom_hooks", git.HookName(name))
name := git.HookName(c.Params(":name"))
if !isValidHookName(name) {
c.NotFound()
return
}
hook, err := c.Repo.GitRepo.Hook("custom_hooks", name)
if err != nil {
c.NotFoundOrError(osutil.NewError(err), "get hook")
return

View File

@@ -43,12 +43,13 @@ type PageMeta struct {
}
func renderWikiPage(c *context.Context, isViewPage bool) (*git.Repository, string) {
wikiRepo, err := git.Open(c.Repo.Repository.WikiPath())
wikiPath := c.Repo.Repository.WikiPath()
wikiRepo, err := git.Open(wikiPath)
if err != nil {
c.Error(err, "open repository")
return nil, ""
}
commit, err := wikiRepo.BranchCommit("master")
commit, err := wikiRepo.BranchCommit(db.WikiBranch(wikiPath))
if err != nil {
c.Error(err, "get branch commit")
return nil, ""
@@ -124,7 +125,8 @@ func Wiki(c *context.Context) {
}
// Get last change information.
commits, err := wikiRepo.Log(git.RefsHeads+"master", git.LogOptions{Path: pageName + ".md"})
branch := db.WikiBranch(c.Repo.Repository.WikiPath())
commits, err := wikiRepo.Log(git.RefsHeads+branch, git.LogOptions{Path: pageName + ".md"})
if err != nil {
c.Error(err, "get commits by path")
return
@@ -143,12 +145,15 @@ func WikiPages(c *context.Context) {
return
}
wikiRepo, err := git.Open(c.Repo.Repository.WikiPath())
wikiPath := c.Repo.Repository.WikiPath()
wikiRepo, err := git.Open(wikiPath)
if err != nil {
c.Error(err, "open repository")
return
}
commit, err := wikiRepo.BranchCommit("master")
branch := db.WikiBranch(wikiPath)
commit, err := wikiRepo.BranchCommit(branch)
if err != nil {
c.Error(err, "get branch commit")
return
@@ -162,7 +167,7 @@ func WikiPages(c *context.Context) {
pages := make([]PageMeta, 0, len(entries))
for i := range entries {
if entries[i].Type() == git.ObjectBlob && strings.HasSuffix(entries[i].Name(), ".md") {
commits, err := wikiRepo.Log(git.RefsHeads+"master", git.LogOptions{Path: entries[i].Name()})
commits, err := wikiRepo.Log(git.RefsHeads+branch, git.LogOptions{Path: entries[i].Name()})
if err != nil {
c.Error(err, "get commits by path")
return

View File

@@ -267,7 +267,7 @@ func LoginTwoFactorRecoveryCodePost(c *context.Context) {
return
}
if err := db.UseRecoveryCode(userID, c.Query("recovery_code")); err != nil {
if err := db.TwoFactors.UseRecoveryCode(c.Req.Context(), userID, c.Query("recovery_code")); err != nil {
if db.IsTwoFactorRecoveryCodeNotFound(err) {
c.Flash.Error(c.Tr("auth.login_two_factor_invalid_recovery_code"))
c.RedirectSubpath("/user/login/two_factor_recovery_code")

View File

@@ -6,7 +6,6 @@ package ssh
import (
"context"
"fmt"
"io"
"net"
"os"
@@ -55,26 +54,8 @@ func handleServerConn(keyID string, chans <-chan ssh.NewChannel) {
payload := cleanCommand(string(req.Payload))
switch req.Type {
case "env":
var env struct {
Name string
Value string
}
if err := ssh.Unmarshal(req.Payload, &env); err != nil {
log.Warn("SSH: Invalid env payload %q: %v", req.Payload, err)
continue
}
// Sometimes the client could send malformed command (i.e. missing "="),
// see https://discuss.gogs.io/t/ssh/3106.
if env.Name == "" || env.Value == "" {
log.Warn("SSH: Invalid env arguments: %+v", env)
continue
}
_, stderr, err := com.ExecCmd("env", fmt.Sprintf("%s=%s", env.Name, env.Value))
if err != nil {
log.Error("env: %v - %s", err, stderr)
return
}
// We only need to accept the request and do nothing since whatever environment
// variables being set here won't be used in subsequent commands anyway.
case "exec":
cmdName := strings.TrimLeft(payload, "'()")
@@ -175,7 +156,9 @@ func Listen(opts conf.SSHOpts, appDataPath string) {
PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) {
pkey, err := db.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key))))
if err != nil {
log.Error("SearchPublicKeyByContent: %v", err)
if !db.IsErrKeyNotExist(err) {
log.Error("SearchPublicKeyByContent: %v", err)
}
return nil, err
}
return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil

View File

@@ -1474,6 +1474,7 @@ $(document).ready(function() {
headers: { "X-CSRF-Token": csrf },
maxFiles: $dropzone.data("max-file"),
maxFilesize: $dropzone.data("max-size"),
timeout: 0,
acceptedFiles:
$dropzone.data("accepts") === "*/*" ? null : $dropzone.data("accepts"),
addRemoveLinks: true,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,593 +0,0 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals VBArray, PDFJS */
'use strict';
// Initializing PDFJS global object here, it case if we need to change/disable
// some PDF.js features, e.g. range requests
if (typeof PDFJS === 'undefined') {
(typeof window !== 'undefined' ? window : this).PDFJS = {};
}
// Checking if the typed arrays are supported
// Support: iOS<6.0 (subarray), IE<10, Android<4.0
(function checkTypedArrayCompatibility() {
if (typeof Uint8Array !== 'undefined') {
// Support: iOS<6.0
if (typeof Uint8Array.prototype.subarray === 'undefined') {
Uint8Array.prototype.subarray = function subarray(start, end) {
return new Uint8Array(this.slice(start, end));
};
Float32Array.prototype.subarray = function subarray(start, end) {
return new Float32Array(this.slice(start, end));
};
}
// Support: Android<4.1
if (typeof Float64Array === 'undefined') {
window.Float64Array = Float32Array;
}
return;
}
function subarray(start, end) {
return new TypedArray(this.slice(start, end));
}
function setArrayOffset(array, offset) {
if (arguments.length < 2) {
offset = 0;
}
for (var i = 0, n = array.length; i < n; ++i, ++offset) {
this[offset] = array[i] & 0xFF;
}
}
function TypedArray(arg1) {
var result, i, n;
if (typeof arg1 === 'number') {
result = [];
for (i = 0; i < arg1; ++i) {
result[i] = 0;
}
} else if ('slice' in arg1) {
result = arg1.slice(0);
} else {
result = [];
for (i = 0, n = arg1.length; i < n; ++i) {
result[i] = arg1[i];
}
}
result.subarray = subarray;
result.buffer = result;
result.byteLength = result.length;
result.set = setArrayOffset;
if (typeof arg1 === 'object' && arg1.buffer) {
result.buffer = arg1.buffer;
}
return result;
}
window.Uint8Array = TypedArray;
window.Int8Array = TypedArray;
// we don't need support for set, byteLength for 32-bit array
// so we can use the TypedArray as well
window.Uint32Array = TypedArray;
window.Int32Array = TypedArray;
window.Uint16Array = TypedArray;
window.Float32Array = TypedArray;
window.Float64Array = TypedArray;
})();
// URL = URL || webkitURL
// Support: Safari<7, Android 4.2+
(function normalizeURLObject() {
if (!window.URL) {
window.URL = window.webkitURL;
}
})();
// Object.defineProperty()?
// Support: Android<4.0, Safari<5.1
(function checkObjectDefinePropertyCompatibility() {
if (typeof Object.defineProperty !== 'undefined') {
var definePropertyPossible = true;
try {
// some browsers (e.g. safari) cannot use defineProperty() on DOM objects
// and thus the native version is not sufficient
Object.defineProperty(new Image(), 'id', { value: 'test' });
// ... another test for android gb browser for non-DOM objects
var Test = function Test() {};
Test.prototype = { get id() { } };
Object.defineProperty(new Test(), 'id',
{ value: '', configurable: true, enumerable: true, writable: false });
} catch (e) {
definePropertyPossible = false;
}
if (definePropertyPossible) {
return;
}
}
Object.defineProperty = function objectDefineProperty(obj, name, def) {
delete obj[name];
if ('get' in def) {
obj.__defineGetter__(name, def['get']);
}
if ('set' in def) {
obj.__defineSetter__(name, def['set']);
}
if ('value' in def) {
obj.__defineSetter__(name, function objectDefinePropertySetter(value) {
this.__defineGetter__(name, function objectDefinePropertyGetter() {
return value;
});
return value;
});
obj[name] = def.value;
}
};
})();
// No XMLHttpRequest#response?
// Support: IE<11, Android <4.0
(function checkXMLHttpRequestResponseCompatibility() {
var xhrPrototype = XMLHttpRequest.prototype;
var xhr = new XMLHttpRequest();
if (!('overrideMimeType' in xhr)) {
// IE10 might have response, but not overrideMimeType
// Support: IE10
Object.defineProperty(xhrPrototype, 'overrideMimeType', {
value: function xmlHttpRequestOverrideMimeType(mimeType) {}
});
}
if ('responseType' in xhr) {
return;
}
// The worker will be using XHR, so we can save time and disable worker.
PDFJS.disableWorker = true;
Object.defineProperty(xhrPrototype, 'responseType', {
get: function xmlHttpRequestGetResponseType() {
return this._responseType || 'text';
},
set: function xmlHttpRequestSetResponseType(value) {
if (value === 'text' || value === 'arraybuffer') {
this._responseType = value;
if (value === 'arraybuffer' &&
typeof this.overrideMimeType === 'function') {
this.overrideMimeType('text/plain; charset=x-user-defined');
}
}
}
});
// Support: IE9
if (typeof VBArray !== 'undefined') {
Object.defineProperty(xhrPrototype, 'response', {
get: function xmlHttpRequestResponseGet() {
if (this.responseType === 'arraybuffer') {
return new Uint8Array(new VBArray(this.responseBody).toArray());
} else {
return this.responseText;
}
}
});
return;
}
Object.defineProperty(xhrPrototype, 'response', {
get: function xmlHttpRequestResponseGet() {
if (this.responseType !== 'arraybuffer') {
return this.responseText;
}
var text = this.responseText;
var i, n = text.length;
var result = new Uint8Array(n);
for (i = 0; i < n; ++i) {
result[i] = text.charCodeAt(i) & 0xFF;
}
return result.buffer;
}
});
})();
// window.btoa (base64 encode function) ?
// Support: IE<10
(function checkWindowBtoaCompatibility() {
if ('btoa' in window) {
return;
}
var digits =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
window.btoa = function windowBtoa(chars) {
var buffer = '';
var i, n;
for (i = 0, n = chars.length; i < n; i += 3) {
var b1 = chars.charCodeAt(i) & 0xFF;
var b2 = chars.charCodeAt(i + 1) & 0xFF;
var b3 = chars.charCodeAt(i + 2) & 0xFF;
var d1 = b1 >> 2, d2 = ((b1 & 3) << 4) | (b2 >> 4);
var d3 = i + 1 < n ? ((b2 & 0xF) << 2) | (b3 >> 6) : 64;
var d4 = i + 2 < n ? (b3 & 0x3F) : 64;
buffer += (digits.charAt(d1) + digits.charAt(d2) +
digits.charAt(d3) + digits.charAt(d4));
}
return buffer;
};
})();
// window.atob (base64 encode function)?
// Support: IE<10
(function checkWindowAtobCompatibility() {
if ('atob' in window) {
return;
}
// https://github.com/davidchambers/Base64.js
var digits =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
window.atob = function (input) {
input = input.replace(/=+$/, '');
if (input.length % 4 === 1) {
throw new Error('bad atob input');
}
for (
// initialize result and counters
var bc = 0, bs, buffer, idx = 0, output = '';
// get next character
buffer = input.charAt(idx++);
// character found in table?
// initialize bit storage and add its ascii value
~buffer && (bs = bc % 4 ? bs * 64 + buffer : buffer,
// and if not first of each 4 characters,
// convert the first 8 bits to one ascii character
bc++ % 4) ? output += String.fromCharCode(255 & bs >> (-2 * bc & 6)) : 0
) {
// try to find character in table (0-63, not found => -1)
buffer = digits.indexOf(buffer);
}
return output;
};
})();
// Function.prototype.bind?
// Support: Android<4.0, iOS<6.0
(function checkFunctionPrototypeBindCompatibility() {
if (typeof Function.prototype.bind !== 'undefined') {
return;
}
Function.prototype.bind = function functionPrototypeBind(obj) {
var fn = this, headArgs = Array.prototype.slice.call(arguments, 1);
var bound = function functionPrototypeBindBound() {
var args = headArgs.concat(Array.prototype.slice.call(arguments));
return fn.apply(obj, args);
};
return bound;
};
})();
// HTMLElement dataset property
// Support: IE<11, Safari<5.1, Android<4.0
(function checkDatasetProperty() {
var div = document.createElement('div');
if ('dataset' in div) {
return; // dataset property exists
}
Object.defineProperty(HTMLElement.prototype, 'dataset', {
get: function() {
if (this._dataset) {
return this._dataset;
}
var dataset = {};
for (var j = 0, jj = this.attributes.length; j < jj; j++) {
var attribute = this.attributes[j];
if (attribute.name.substring(0, 5) !== 'data-') {
continue;
}
var key = attribute.name.substring(5).replace(/\-([a-z])/g,
function(all, ch) {
return ch.toUpperCase();
});
dataset[key] = attribute.value;
}
Object.defineProperty(this, '_dataset', {
value: dataset,
writable: false,
enumerable: false
});
return dataset;
},
enumerable: true
});
})();
// HTMLElement classList property
// Support: IE<10, Android<4.0, iOS<5.0
(function checkClassListProperty() {
var div = document.createElement('div');
if ('classList' in div) {
return; // classList property exists
}
function changeList(element, itemName, add, remove) {
var s = element.className || '';
var list = s.split(/\s+/g);
if (list[0] === '') {
list.shift();
}
var index = list.indexOf(itemName);
if (index < 0 && add) {
list.push(itemName);
}
if (index >= 0 && remove) {
list.splice(index, 1);
}
element.className = list.join(' ');
return (index >= 0);
}
var classListPrototype = {
add: function(name) {
changeList(this.element, name, true, false);
},
contains: function(name) {
return changeList(this.element, name, false, false);
},
remove: function(name) {
changeList(this.element, name, false, true);
},
toggle: function(name) {
changeList(this.element, name, true, true);
}
};
Object.defineProperty(HTMLElement.prototype, 'classList', {
get: function() {
if (this._classList) {
return this._classList;
}
var classList = Object.create(classListPrototype, {
element: {
value: this,
writable: false,
enumerable: true
}
});
Object.defineProperty(this, '_classList', {
value: classList,
writable: false,
enumerable: false
});
return classList;
},
enumerable: true
});
})();
// Check console compatibility
// In older IE versions the console object is not available
// unless console is open.
// Support: IE<10
(function checkConsoleCompatibility() {
if (!('console' in window)) {
window.console = {
log: function() {},
error: function() {},
warn: function() {}
};
} else if (!('bind' in console.log)) {
// native functions in IE9 might not have bind
console.log = (function(fn) {
return function(msg) { return fn(msg); };
})(console.log);
console.error = (function(fn) {
return function(msg) { return fn(msg); };
})(console.error);
console.warn = (function(fn) {
return function(msg) { return fn(msg); };
})(console.warn);
}
})();
// Check onclick compatibility in Opera
// Support: Opera<15
(function checkOnClickCompatibility() {
// workaround for reported Opera bug DSK-354448:
// onclick fires on disabled buttons with opaque content
function ignoreIfTargetDisabled(event) {
if (isDisabled(event.target)) {
event.stopPropagation();
}
}
function isDisabled(node) {
return node.disabled || (node.parentNode && isDisabled(node.parentNode));
}
if (navigator.userAgent.indexOf('Opera') !== -1) {
// use browser detection since we cannot feature-check this bug
document.addEventListener('click', ignoreIfTargetDisabled, true);
}
})();
// Checks if possible to use URL.createObjectURL()
// Support: IE
(function checkOnBlobSupport() {
// sometimes IE loosing the data created with createObjectURL(), see #3977
if (navigator.userAgent.indexOf('Trident') >= 0) {
PDFJS.disableCreateObjectURL = true;
}
})();
// Checks if navigator.language is supported
(function checkNavigatorLanguage() {
if ('language' in navigator) {
return;
}
PDFJS.locale = navigator.userLanguage || 'en-US';
})();
(function checkRangeRequests() {
// Safari has issues with cached range requests see:
// https://github.com/mozilla/pdf.js/issues/3260
// Last tested with version 6.0.4.
// Support: Safari 6.0+
var isSafari = Object.prototype.toString.call(
window.HTMLElement).indexOf('Constructor') > 0;
// Older versions of Android (pre 3.0) has issues with range requests, see:
// https://github.com/mozilla/pdf.js/issues/3381.
// Make sure that we only match webkit-based Android browsers,
// since Firefox/Fennec works as expected.
// Support: Android<3.0
var regex = /Android\s[0-2][^\d]/;
var isOldAndroid = regex.test(navigator.userAgent);
// Range requests are broken in Chrome 39 and 40, https://crbug.com/442318
var isChromeWithRangeBug = /Chrome\/(39|40)\./.test(navigator.userAgent);
if (isSafari || isOldAndroid || isChromeWithRangeBug) {
PDFJS.disableRange = true;
PDFJS.disableStream = true;
}
})();
// Check if the browser supports manipulation of the history.
// Support: IE<10, Android<4.2
(function checkHistoryManipulation() {
// Android 2.x has so buggy pushState support that it was removed in
// Android 3.0 and restored as late as in Android 4.2.
// Support: Android 2.x
if (!history.pushState || navigator.userAgent.indexOf('Android 2.') >= 0) {
PDFJS.disableHistory = true;
}
})();
// Support: IE<11, Chrome<21, Android<4.4, Safari<6
(function checkSetPresenceInImageData() {
// IE < 11 will use window.CanvasPixelArray which lacks set function.
if (window.CanvasPixelArray) {
if (typeof window.CanvasPixelArray.prototype.set !== 'function') {
window.CanvasPixelArray.prototype.set = function(arr) {
for (var i = 0, ii = this.length; i < ii; i++) {
this[i] = arr[i];
}
};
}
} else {
// Old Chrome and Android use an inaccessible CanvasPixelArray prototype.
// Because we cannot feature detect it, we rely on user agent parsing.
var polyfill = false, versionMatch;
if (navigator.userAgent.indexOf('Chrom') >= 0) {
versionMatch = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
// Chrome < 21 lacks the set function.
polyfill = versionMatch && parseInt(versionMatch[2]) < 21;
} else if (navigator.userAgent.indexOf('Android') >= 0) {
// Android < 4.4 lacks the set function.
// Android >= 4.4 will contain Chrome in the user agent,
// thus pass the Chrome check above and not reach this block.
polyfill = /Android\s[0-4][^\d]/g.test(navigator.userAgent);
} else if (navigator.userAgent.indexOf('Safari') >= 0) {
versionMatch = navigator.userAgent.
match(/Version\/([0-9]+)\.([0-9]+)\.([0-9]+) Safari\//);
// Safari < 6 lacks the set function.
polyfill = versionMatch && parseInt(versionMatch[1]) < 6;
}
if (polyfill) {
var contextPrototype = window.CanvasRenderingContext2D.prototype;
var createImageData = contextPrototype.createImageData;
contextPrototype.createImageData = function(w, h) {
var imageData = createImageData.call(this, w, h);
imageData.data.set = function(arr) {
for (var i = 0, ii = this.length; i < ii; i++) {
this[i] = arr[i];
}
};
return imageData;
};
// this closure will be kept referenced, so clear its vars
contextPrototype = null;
}
}
})();
// Support: IE<10, Android<4.0, iOS
(function checkRequestAnimationFrame() {
function fakeRequestAnimationFrame(callback) {
window.setTimeout(callback, 20);
}
var isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
if (isIOS) {
// requestAnimationFrame on iOS is broken, replacing with fake one.
window.requestAnimationFrame = fakeRequestAnimationFrame;
return;
}
if ('requestAnimationFrame' in window) {
return;
}
window.requestAnimationFrame =
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
fakeRequestAnimationFrame;
})();
(function checkCanvasSizeLimitation() {
var isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
var isAndroid = /Android/g.test(navigator.userAgent);
if (isIOS || isAndroid) {
// 5MP
PDFJS.maxCanvasPixels = 5242880;
}
})();
// Disable fullscreen support for certain problematic configurations.
// Support: IE11+ (when embedded).
(function checkFullscreenSupport() {
var isEmbeddedIE = (navigator.userAgent.indexOf('Trident') >= 0 &&
window.parent !== window);
if (isEmbeddedIE) {
PDFJS.disableFullscreen = true;
}
})();
// Provides document.currentScript support
// Support: IE, Chrome<29.
(function checkCurrentScript() {
if ('currentScript' in document) {
return;
}
Object.defineProperty(document, 'currentScript', {
get: function () {
var scripts = document.getElementsByTagName('script');
return scripts[scripts.length - 1];
},
enumerable: true,
configurable: true
});
})();

View File

@@ -1,618 +0,0 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals PDFJS */
'use strict';
var FontInspector = (function FontInspectorClosure() {
var fonts;
var active = false;
var fontAttribute = 'data-font-name';
function removeSelection() {
var divs = document.querySelectorAll('div[' + fontAttribute + ']');
for (var i = 0, ii = divs.length; i < ii; ++i) {
var div = divs[i];
div.className = '';
}
}
function resetSelection() {
var divs = document.querySelectorAll('div[' + fontAttribute + ']');
for (var i = 0, ii = divs.length; i < ii; ++i) {
var div = divs[i];
div.className = 'debuggerHideText';
}
}
function selectFont(fontName, show) {
var divs = document.querySelectorAll('div[' + fontAttribute + '=' +
fontName + ']');
for (var i = 0, ii = divs.length; i < ii; ++i) {
var div = divs[i];
div.className = show ? 'debuggerShowText' : 'debuggerHideText';
}
}
function textLayerClick(e) {
if (!e.target.dataset.fontName ||
e.target.tagName.toUpperCase() !== 'DIV') {
return;
}
var fontName = e.target.dataset.fontName;
var selects = document.getElementsByTagName('input');
for (var i = 0; i < selects.length; ++i) {
var select = selects[i];
if (select.dataset.fontName !== fontName) {
continue;
}
select.checked = !select.checked;
selectFont(fontName, select.checked);
select.scrollIntoView();
}
}
return {
// Properties/functions needed by PDFBug.
id: 'FontInspector',
name: 'Font Inspector',
panel: null,
manager: null,
init: function init() {
var panel = this.panel;
panel.setAttribute('style', 'padding: 5px;');
var tmp = document.createElement('button');
tmp.addEventListener('click', resetSelection);
tmp.textContent = 'Refresh';
panel.appendChild(tmp);
fonts = document.createElement('div');
panel.appendChild(fonts);
},
cleanup: function cleanup() {
fonts.textContent = '';
},
enabled: false,
get active() {
return active;
},
set active(value) {
active = value;
if (active) {
document.body.addEventListener('click', textLayerClick, true);
resetSelection();
} else {
document.body.removeEventListener('click', textLayerClick, true);
removeSelection();
}
},
// FontInspector specific functions.
fontAdded: function fontAdded(fontObj, url) {
function properties(obj, list) {
var moreInfo = document.createElement('table');
for (var i = 0; i < list.length; i++) {
var tr = document.createElement('tr');
var td1 = document.createElement('td');
td1.textContent = list[i];
tr.appendChild(td1);
var td2 = document.createElement('td');
td2.textContent = obj[list[i]].toString();
tr.appendChild(td2);
moreInfo.appendChild(tr);
}
return moreInfo;
}
var moreInfo = properties(fontObj, ['name', 'type']);
var fontName = fontObj.loadedName;
var font = document.createElement('div');
var name = document.createElement('span');
name.textContent = fontName;
var download = document.createElement('a');
if (url) {
url = /url\(['"]?([^\)"']+)/.exec(url);
download.href = url[1];
} else if (fontObj.data) {
url = URL.createObjectURL(new Blob([fontObj.data], {
type: fontObj.mimeType
}));
download.href = url;
}
download.textContent = 'Download';
var logIt = document.createElement('a');
logIt.href = '';
logIt.textContent = 'Log';
logIt.addEventListener('click', function(event) {
event.preventDefault();
console.log(fontObj);
});
var select = document.createElement('input');
select.setAttribute('type', 'checkbox');
select.dataset.fontName = fontName;
select.addEventListener('click', (function(select, fontName) {
return (function() {
selectFont(fontName, select.checked);
});
})(select, fontName));
font.appendChild(select);
font.appendChild(name);
font.appendChild(document.createTextNode(' '));
font.appendChild(download);
font.appendChild(document.createTextNode(' '));
font.appendChild(logIt);
font.appendChild(moreInfo);
fonts.appendChild(font);
// Somewhat of a hack, should probably add a hook for when the text layer
// is done rendering.
setTimeout(function() {
if (this.active) {
resetSelection();
}
}.bind(this), 2000);
}
};
})();
// Manages all the page steppers.
var StepperManager = (function StepperManagerClosure() {
var steppers = [];
var stepperDiv = null;
var stepperControls = null;
var stepperChooser = null;
var breakPoints = {};
return {
// Properties/functions needed by PDFBug.
id: 'Stepper',
name: 'Stepper',
panel: null,
manager: null,
init: function init() {
var self = this;
this.panel.setAttribute('style', 'padding: 5px;');
stepperControls = document.createElement('div');
stepperChooser = document.createElement('select');
stepperChooser.addEventListener('change', function(event) {
self.selectStepper(this.value);
});
stepperControls.appendChild(stepperChooser);
stepperDiv = document.createElement('div');
this.panel.appendChild(stepperControls);
this.panel.appendChild(stepperDiv);
if (sessionStorage.getItem('pdfjsBreakPoints')) {
breakPoints = JSON.parse(sessionStorage.getItem('pdfjsBreakPoints'));
}
},
cleanup: function cleanup() {
stepperChooser.textContent = '';
stepperDiv.textContent = '';
steppers = [];
},
enabled: false,
active: false,
// Stepper specific functions.
create: function create(pageIndex) {
var debug = document.createElement('div');
debug.id = 'stepper' + pageIndex;
debug.setAttribute('hidden', true);
debug.className = 'stepper';
stepperDiv.appendChild(debug);
var b = document.createElement('option');
b.textContent = 'Page ' + (pageIndex + 1);
b.value = pageIndex;
stepperChooser.appendChild(b);
var initBreakPoints = breakPoints[pageIndex] || [];
var stepper = new Stepper(debug, pageIndex, initBreakPoints);
steppers.push(stepper);
if (steppers.length === 1) {
this.selectStepper(pageIndex, false);
}
return stepper;
},
selectStepper: function selectStepper(pageIndex, selectPanel) {
var i;
pageIndex = pageIndex | 0;
if (selectPanel) {
this.manager.selectPanel(this);
}
for (i = 0; i < steppers.length; ++i) {
var stepper = steppers[i];
if (stepper.pageIndex === pageIndex) {
stepper.panel.removeAttribute('hidden');
} else {
stepper.panel.setAttribute('hidden', true);
}
}
var options = stepperChooser.options;
for (i = 0; i < options.length; ++i) {
var option = options[i];
option.selected = (option.value | 0) === pageIndex;
}
},
saveBreakPoints: function saveBreakPoints(pageIndex, bps) {
breakPoints[pageIndex] = bps;
sessionStorage.setItem('pdfjsBreakPoints', JSON.stringify(breakPoints));
}
};
})();
// The stepper for each page's IRQueue.
var Stepper = (function StepperClosure() {
// Shorter way to create element and optionally set textContent.
function c(tag, textContent) {
var d = document.createElement(tag);
if (textContent) {
d.textContent = textContent;
}
return d;
}
var opMap = null;
function simplifyArgs(args) {
if (typeof args === 'string') {
var MAX_STRING_LENGTH = 75;
return args.length <= MAX_STRING_LENGTH ? args :
args.substr(0, MAX_STRING_LENGTH) + '...';
}
if (typeof args !== 'object' || args === null) {
return args;
}
if ('length' in args) { // array
var simpleArgs = [], i, ii;
var MAX_ITEMS = 10;
for (i = 0, ii = Math.min(MAX_ITEMS, args.length); i < ii; i++) {
simpleArgs.push(simplifyArgs(args[i]));
}
if (i < args.length) {
simpleArgs.push('...');
}
return simpleArgs;
}
var simpleObj = {};
for (var key in args) {
simpleObj[key] = simplifyArgs(args[key]);
}
return simpleObj;
}
function Stepper(panel, pageIndex, initialBreakPoints) {
this.panel = panel;
this.breakPoint = 0;
this.nextBreakPoint = null;
this.pageIndex = pageIndex;
this.breakPoints = initialBreakPoints;
this.currentIdx = -1;
this.operatorListIdx = 0;
}
Stepper.prototype = {
init: function init() {
var panel = this.panel;
var content = c('div', 'c=continue, s=step');
var table = c('table');
content.appendChild(table);
table.cellSpacing = 0;
var headerRow = c('tr');
table.appendChild(headerRow);
headerRow.appendChild(c('th', 'Break'));
headerRow.appendChild(c('th', 'Idx'));
headerRow.appendChild(c('th', 'fn'));
headerRow.appendChild(c('th', 'args'));
panel.appendChild(content);
this.table = table;
if (!opMap) {
opMap = Object.create(null);
for (var key in PDFJS.OPS) {
opMap[PDFJS.OPS[key]] = key;
}
}
},
updateOperatorList: function updateOperatorList(operatorList) {
var self = this;
function cboxOnClick() {
var x = +this.dataset.idx;
if (this.checked) {
self.breakPoints.push(x);
} else {
self.breakPoints.splice(self.breakPoints.indexOf(x), 1);
}
StepperManager.saveBreakPoints(self.pageIndex, self.breakPoints);
}
var MAX_OPERATORS_COUNT = 15000;
if (this.operatorListIdx > MAX_OPERATORS_COUNT) {
return;
}
var chunk = document.createDocumentFragment();
var operatorsToDisplay = Math.min(MAX_OPERATORS_COUNT,
operatorList.fnArray.length);
for (var i = this.operatorListIdx; i < operatorsToDisplay; i++) {
var line = c('tr');
line.className = 'line';
line.dataset.idx = i;
chunk.appendChild(line);
var checked = this.breakPoints.indexOf(i) !== -1;
var args = operatorList.argsArray[i] || [];
var breakCell = c('td');
var cbox = c('input');
cbox.type = 'checkbox';
cbox.className = 'points';
cbox.checked = checked;
cbox.dataset.idx = i;
cbox.onclick = cboxOnClick;
breakCell.appendChild(cbox);
line.appendChild(breakCell);
line.appendChild(c('td', i.toString()));
var fn = opMap[operatorList.fnArray[i]];
var decArgs = args;
if (fn === 'showText') {
var glyphs = args[0];
var newArgs = [];
var str = [];
for (var j = 0; j < glyphs.length; j++) {
var glyph = glyphs[j];
if (typeof glyph === 'object' && glyph !== null) {
str.push(glyph.fontChar);
} else {
if (str.length > 0) {
newArgs.push(str.join(''));
str = [];
}
newArgs.push(glyph); // null or number
}
}
if (str.length > 0) {
newArgs.push(str.join(''));
}
decArgs = [newArgs];
}
line.appendChild(c('td', fn));
line.appendChild(c('td', JSON.stringify(simplifyArgs(decArgs))));
}
if (operatorsToDisplay < operatorList.fnArray.length) {
line = c('tr');
var lastCell = c('td', '...');
lastCell.colspan = 4;
chunk.appendChild(lastCell);
}
this.operatorListIdx = operatorList.fnArray.length;
this.table.appendChild(chunk);
},
getNextBreakPoint: function getNextBreakPoint() {
this.breakPoints.sort(function(a, b) { return a - b; });
for (var i = 0; i < this.breakPoints.length; i++) {
if (this.breakPoints[i] > this.currentIdx) {
return this.breakPoints[i];
}
}
return null;
},
breakIt: function breakIt(idx, callback) {
StepperManager.selectStepper(this.pageIndex, true);
var self = this;
var dom = document;
self.currentIdx = idx;
var listener = function(e) {
switch (e.keyCode) {
case 83: // step
dom.removeEventListener('keydown', listener, false);
self.nextBreakPoint = self.currentIdx + 1;
self.goTo(-1);
callback();
break;
case 67: // continue
dom.removeEventListener('keydown', listener, false);
var breakPoint = self.getNextBreakPoint();
self.nextBreakPoint = breakPoint;
self.goTo(-1);
callback();
break;
}
};
dom.addEventListener('keydown', listener, false);
self.goTo(idx);
},
goTo: function goTo(idx) {
var allRows = this.panel.getElementsByClassName('line');
for (var x = 0, xx = allRows.length; x < xx; ++x) {
var row = allRows[x];
if ((row.dataset.idx | 0) === idx) {
row.style.backgroundColor = 'rgb(251,250,207)';
row.scrollIntoView();
} else {
row.style.backgroundColor = null;
}
}
}
};
return Stepper;
})();
var Stats = (function Stats() {
var stats = [];
function clear(node) {
while (node.hasChildNodes()) {
node.removeChild(node.lastChild);
}
}
function getStatIndex(pageNumber) {
for (var i = 0, ii = stats.length; i < ii; ++i) {
if (stats[i].pageNumber === pageNumber) {
return i;
}
}
return false;
}
return {
// Properties/functions needed by PDFBug.
id: 'Stats',
name: 'Stats',
panel: null,
manager: null,
init: function init() {
this.panel.setAttribute('style', 'padding: 5px;');
PDFJS.enableStats = true;
},
enabled: false,
active: false,
// Stats specific functions.
add: function(pageNumber, stat) {
if (!stat) {
return;
}
var statsIndex = getStatIndex(pageNumber);
if (statsIndex !== false) {
var b = stats[statsIndex];
this.panel.removeChild(b.div);
stats.splice(statsIndex, 1);
}
var wrapper = document.createElement('div');
wrapper.className = 'stats';
var title = document.createElement('div');
title.className = 'title';
title.textContent = 'Page: ' + pageNumber;
var statsDiv = document.createElement('div');
statsDiv.textContent = stat.toString();
wrapper.appendChild(title);
wrapper.appendChild(statsDiv);
stats.push({ pageNumber: pageNumber, div: wrapper });
stats.sort(function(a, b) { return a.pageNumber - b.pageNumber; });
clear(this.panel);
for (var i = 0, ii = stats.length; i < ii; ++i) {
this.panel.appendChild(stats[i].div);
}
},
cleanup: function () {
stats = [];
clear(this.panel);
}
};
})();
// Manages all the debugging tools.
var PDFBug = (function PDFBugClosure() {
var panelWidth = 300;
var buttons = [];
var activePanel = null;
return {
tools: [
FontInspector,
StepperManager,
Stats
],
enable: function(ids) {
var all = false, tools = this.tools;
if (ids.length === 1 && ids[0] === 'all') {
all = true;
}
for (var i = 0; i < tools.length; ++i) {
var tool = tools[i];
if (all || ids.indexOf(tool.id) !== -1) {
tool.enabled = true;
}
}
if (!all) {
// Sort the tools by the order they are enabled.
tools.sort(function(a, b) {
var indexA = ids.indexOf(a.id);
indexA = indexA < 0 ? tools.length : indexA;
var indexB = ids.indexOf(b.id);
indexB = indexB < 0 ? tools.length : indexB;
return indexA - indexB;
});
}
},
init: function init() {
/*
* Basic Layout:
* PDFBug
* Controls
* Panels
* Panel
* Panel
* ...
*/
var ui = document.createElement('div');
ui.id = 'PDFBug';
var controls = document.createElement('div');
controls.setAttribute('class', 'controls');
ui.appendChild(controls);
var panels = document.createElement('div');
panels.setAttribute('class', 'panels');
ui.appendChild(panels);
var container = document.getElementById('viewerContainer');
container.appendChild(ui);
container.style.right = panelWidth + 'px';
// Initialize all the debugging tools.
var tools = this.tools;
var self = this;
for (var i = 0; i < tools.length; ++i) {
var tool = tools[i];
var panel = document.createElement('div');
var panelButton = document.createElement('button');
panelButton.textContent = tool.name;
panelButton.addEventListener('click', (function(selected) {
return function(event) {
event.preventDefault();
self.selectPanel(selected);
};
})(i));
controls.appendChild(panelButton);
panels.appendChild(panel);
tool.panel = panel;
tool.manager = this;
if (tool.enabled) {
tool.init();
} else {
panel.textContent = tool.name + ' is disabled. To enable add ' +
' "' + tool.id + '" to the pdfBug parameter ' +
'and refresh (seperate multiple by commas).';
}
buttons.push(panelButton);
}
this.selectPanel(0);
},
cleanup: function cleanup() {
for (var i = 0, ii = this.tools.length; i < ii; i++) {
if (this.tools[i].enabled) {
this.tools[i].cleanup();
}
}
},
selectPanel: function selectPanel(index) {
if (typeof index !== 'number') {
index = this.tools.indexOf(index);
}
if (index === activePanel) {
return;
}
activePanel = index;
var tools = this.tools;
for (var j = 0; j < tools.length; ++j) {
if (j === index) {
buttons[j].setAttribute('class', 'active');
tools[j].active = true;
tools[j].panel.removeAttribute('hidden');
} else {
buttons[j].setAttribute('class', '');
tools[j].active = false;
tools[j].panel.setAttribute('hidden', 'true');
}
}
}
};
})();

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><path id="path4" fill="#ff0" fill-opacity="1" stroke="#000" stroke-opacity="1" stroke-width="1.254" d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"/></svg>

Before

Width:  |  Height:  |  Size: 318 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect width="33.76" height="33.76" x="3.12" y="3.12" fill="#ff0" fill-opacity="1" fill-rule="evenodd" stroke="#000" stroke-dasharray="none" stroke-miterlimit="4" stroke-opacity="1" stroke-width="1"/><path fill="#fff" fill-opacity="1" stroke="#000" stroke-dasharray="none" stroke-miterlimit="4" stroke-opacity="1" stroke-width=".93" d="m 20.677967,8.54499 c -7.342801,0 -13.295293,4.954293 -13.295293,11.065751 0,2.088793 0.3647173,3.484376 1.575539,5.150563 L 6.0267418,31.45501 13.560595,29.011117 c 2.221262,1.387962 4.125932,1.665377 7.117372,1.665377 7.3428,0 13.295291,-4.954295 13.295291,-11.065753 0,-6.111458 -5.952491,-11.065751 -13.295291,-11.065751 z"/></svg>

Before

Width:  |  Height:  |  Size: 753 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><g id="layer1" transform="translate(0,-60)"><rect width="36.461" height="34.806" x="1.77" y="62.597" fill="#ff0" fill-opacity="1" fill-rule="evenodd" stroke="#000" stroke-opacity="1" stroke-width="1.308"/><g><path fill="#fff" fill-opacity="1" fill-rule="nonzero" stroke="#000" stroke-dasharray="none" stroke-miterlimit="4" stroke-opacity="1" stroke-width="1.028" d="M 20,64.526342 C 11.454135,64.526342 4.5263421,71.454135 4.5263421,80 4.5263421,88.545865 11.454135,95.473658 20,95.473658 28.545865,95.473658 35.473658,88.545865 35.473658,80 35.473658,71.454135 28.545865,64.526342 20,64.526342 z m -0.408738,9.488564 c 3.527079,0 6.393832,2.84061 6.393832,6.335441 0,3.494831 -2.866753,6.335441 -6.393832,6.335441 -3.527079,0 -6.393832,-2.84061 -6.393832,-6.335441 0,-3.494831 2.866753,-6.335441 6.393832,-6.335441 z" transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)"/><path fill="#000" fill-opacity="1" stroke="none" d="m 7.2335209,71.819938 4.9702591,4.161823 c -1.679956,2.581606 -1.443939,6.069592 0.159325,8.677725 l -5.1263071,3.424463 c 0.67516,1.231452 3.0166401,3.547686 4.2331971,4.194757 l 3.907728,-4.567277 c 2.541952,1.45975 5.730694,1.392161 8.438683,-0.12614 l 3.469517,6.108336 c 1.129779,-0.44367 4.742234,-3.449633 5.416358,-5.003859 l -5.46204,-4.415541 c 1.44319,-2.424098 1.651175,-5.267515 0.557303,-7.748623 l 5.903195,-3.833951 C 33.14257,71.704996 30.616217,69.018606 29.02952,67.99296 l -4.118813,4.981678 C 22.411934,71.205099 18.900853,70.937534 16.041319,72.32916 l -3.595408,-5.322091 c -1.345962,0.579488 -4.1293881,2.921233 -5.2123901,4.812869 z m 8.1010311,3.426672 c 2.75284,-2.446266 6.769149,-2.144694 9.048998,0.420874 2.279848,2.56557 2.113919,6.596919 -0.638924,9.043185 -2.752841,2.446267 -6.775754,2.13726 -9.055604,-0.428308 -2.279851,-2.565568 -2.107313,-6.589485 0.64553,-9.035751 z" transform="matrix(0.88763677,0,0,0.88763677,2.2472646,8.9890584)"/></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><path fill="#ff0" fill-opacity=".941" fill-rule="nonzero" stroke="#000" stroke-dasharray="none" stroke-miterlimit="4" stroke-opacity="1" stroke-width="1.005" d="M 32.003143,1.4044602 57.432701,62.632577 6.5672991,62.627924 z"/></svg>

Before

Width:  |  Height:  |  Size: 316 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><path id="path604" fill="#ff0" fill-opacity="1" stroke="#000" stroke-opacity="1" stroke-width="1.727" d="M 25.470843,9.4933766 C 25.30219,12.141818 30.139101,14.445969 34.704831,13.529144 40.62635,12.541995 41.398833,7.3856498 35.97505,5.777863 31.400921,4.1549155 25.157674,6.5445892 25.470843,9.4933766 z M 4.5246282,17.652051 C 4.068249,11.832873 9.2742983,5.9270407 18.437379,3.0977088 29.751911,-0.87185184 45.495663,1.4008022 53.603953,7.1104009 c 9.275765,6.1889221 7.158128,16.2079421 -3.171076,21.5939521 -1.784316,1.635815 -6.380222,1.21421 -7.068351,3.186186 -1.04003,0.972427 -1.288046,2.050158 -1.232864,3.168203 1.015111,2.000108 -3.831548,1.633216 -3.270553,3.759574 0.589477,5.264544 -0.179276,10.53738 -0.362842,15.806257 -0.492006,2.184998 1.163456,4.574232 -0.734888,6.610642 -2.482919,2.325184 -7.30604,2.189143 -9.193497,-0.274767 -2.733688,-1.740626 -8.254447,-3.615254 -6.104247,-6.339626 3.468112,-1.708686 -2.116197,-3.449897 0.431242,-5.080274 5.058402,-1.39256 -2.393215,-2.304318 -0.146889,-4.334645 3.069198,-0.977415 2.056986,-2.518352 -0.219121,-3.540397 1.876567,-1.807151 1.484149,-4.868919 -2.565455,-5.942205 0.150866,-1.805474 2.905737,-4.136876 -1.679967,-5.20493 C 10.260902,27.882167 4.6872697,22.95045 4.5245945,17.652051 z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" viewBox="0 0 64 64"><path id="path2985" fill="#ff0" fill-opacity=".941" fill-rule="nonzero" stroke="#000" stroke-dasharray="none" stroke-miterlimit="4" stroke-opacity="1" stroke-width=".834" d="M 32.003143,10.913072 57.432701,53.086929 6.567299,53.083723 z"/></svg>

Before

Width:  |  Height:  |  Size: 328 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"/>

Before

Width:  |  Height:  |  Size: 84 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect id="rect4" width="36.075" height="31.097" x="1.962" y="4.452" fill="#ff0" fill-opacity="1" fill-rule="evenodd" stroke="#000" stroke-opacity="1" stroke-width="1.23"/><rect id="rect6" width="27.969" height="1.501" x="6.016" y="10.285" fill="#000" fill-opacity="1" stroke="none"/><rect id="rect8" width="27.969" height=".858" x="6.016" y="23.217" fill="#000" fill-opacity="1" stroke="none"/><rect id="rect10" width="27.969" height=".858" x="5.813" y="28.964" fill="#000" fill-opacity="1" stroke="none"/><rect id="rect12" width="27.969" height=".858" x="6.016" y="17.426" fill="#000" fill-opacity="1" stroke="none"/></svg>

Before

Width:  |  Height:  |  Size: 707 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"><rect width="33.76" height="33.76" x="3.12" y="3.12" fill="#ff0" fill-opacity="1" fill-rule="evenodd" stroke="#000" stroke-dasharray="none" stroke-miterlimit="4" stroke-opacity="1" stroke-width="1"/><path fill="#fff" fill-opacity="1" stroke="#000" stroke-opacity="1" stroke-width="1.078" d="m 17.692678,34.50206 0,-16.182224 c -1.930515,-0.103225 -3.455824,-0.730383 -4.57593,-1.881473 -1.12011,-1.151067 -1.680164,-2.619596 -1.680164,-4.405591 0,-1.992435 0.621995,-3.5796849 1.865988,-4.7617553 1.243989,-1.1820288 3.06352,-1.7730536 5.458598,-1.7730764 l 9.802246,0 0,2.6789711 -2.229895,0 0,26.3251486 -2.632515,0 0,-26.3251486 -3.45324,0 0,26.3251486 z" font-family="Arial" font-size="29.421" font-stretch="normal" font-style="normal" font-variant="normal" font-weight="normal" letter-spacing="0" text-anchor="start" word-spacing="0" writing-mode="lr-tb" style="text-align:start;line-height:125%;-inkscape-font-specification:Arial"/></svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

Some files were not shown because too many files have changed in this diff Show More