- Remove residual <<<<<<< HEAD marker from api_org_test.go
- Convert code.gitea.io/gitea to mokoconsulting paths in 5 new test files:
cmd/serv_test.go, models/auth/twofactor_test.go,
modules/git/commit_info_nogogit_test.go,
routers/private/hook_pre_receive_test.go,
services/actions/notifier_helper_test.go
- Add changelog entries for new features (#460, #507, #513)
Claude-Session: https://claude.ai/code/session_011AAFzotGMf3ayvXhEmStCd
Backport #38182Fix#38177
Make WalkGitLog can handle EOF and context errors correctly, and don't
export these private functions & methods & structs.
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Backport #38108
- Enforce repository token scope on RSS/Atom feed endpoints so a PAT
without repo scope can no longer read private repo commit data.
- Block HTTP redirects during repository migration clones to prevent
SSRF reaching internal addresses via an attacker-controlled redirect.
- Redact the notification subject after repo access is revoked so
private issue/PR metadata is no longer leaked through the notification
API.
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Backport #38044 by @metsw24-max
**Packages-index stanza injection via Debian control file**
A `.deb` whose `control` file appends extra paragraphs after a blank
line was still accepted, and `ParseControlFile` stored the whole
multi-stanza blob in `p.Control`. That blob is re-emitted verbatim into
the generated `Packages` index, so the embedded blank line splits it
into separate stanzas and an uploader can smuggle a package entry with
an attacker-chosen `Filename` into the shared index. A binary control
file only holds one stanza, so parsing now stops at the blank line that
terminates it; well-formed packages are unaffected and the new subtest
covers the trailing-stanza case.
Signed-off-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: metsw24-max <metsw24@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: bircni <bircni@icloud.com>
Adds configurable cascade rules per repo. When a PR merges into a
source branch, the system auto-creates PRs to each configured target
branch. Skips if a matching PR already exists.
- Model: CascadeMergeRule (repo_id, source, target, enabled, auto_merge)
- Migration v362 creates cascade_merge_rule table
- Notifier hooks into MergePullRequest/AutoMergePullRequest events
- API: CRUD at /repos/{owner}/{repo}/cascade_rules (admin only)
Claude-Session: https://claude.ai/code/session_011AAFzotGMf3ayvXhEmStCd
4 built-in presets: default, software-development, support-tickets,
bug-tracking. API endpoints to list presets, apply to org, and copy
statuses between orgs. Web UI dropdown on org settings page.
Claude-Session: https://claude.ai/code/session_011AAFzotGMf3ayvXhEmStCd
Add configurable per-user/team/deploy-key allowlist for deleting
protected branches. Previously, protected branches could never be
deleted via git push. Now admins can configure deletion permissions
with the same granularity as force-push allowlists.
- 6 new model fields: CanDelete, EnableDeleteAllowlist, DeleteAllowlistUserIDs/TeamIDs, DeleteAllowlistDeployKeys, DeleteAllowlistActionsUser
- CanUserDelete() method with admin-level default (higher than push)
- Migration v361 adds columns to protected_branch table
- Pre-receive hook checks delete allowlist instead of unconditional block
- CanDeleteBranch service uses CanUserDelete instead of IsBranchProtected
- API create/edit endpoints support delete allowlist fields
- Web UI settings page with radio buttons and user/team dropdowns
- 12 new locale strings for the delete allowlist UI
Claude-Session: https://claude.ai/code/session_011AAFzotGMf3ayvXhEmStCd
Gitea's ListWorkflows already uses ListEntriesRecursiveFast (git ls-tree -r)
which discovers workflows in subdirectories. Added test cases confirming
subdirectory and deeply nested paths are recognized by IsWorkflow.
Moved 6 repo-specific workflows (no FILE INFORMATION sync header) to
.mokogitea/workflows/custom/ to separate them from template-synced workflows:
deploy-mokogitea, deploy-dev, cascade-dev, pr-rc-release, test-mokogitea,
upstream-bug-sync.
Also fixes deploy-mokogitea.yml: merged the dev health check into the deploy
job as step 1 to avoid runner status reporting failures on inter-job handoff
(check-dev job was recorded as "skipped" despite passing, cancelling deploy).
Closes#693
Claude-Session: https://claude.ai/code/session_011AAFzotGMf3ayvXhEmStCd
Backport #37867 by @bircni
- When a commit subject is a bare URL, `linkProcessor` wrapped it in its
own `<a>` to that URL. Because HTML cannot nest anchors, the wrapping
default link (the action run / commit link) was lost and the action
title became unclickable — clicking it sent the user to the URL from the
commit message instead of the action log.
- Drop `linkProcessor` from `PostProcessCommitMessageSubject` so the
whole subject stays wrapped in the default link. URLs in subjects now
render as text inside that link; URLs in commit bodies are unaffected.
Fixes#37865
Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Add PATCH /users/{username}/tokens/{id} API endpoint and web UI edit
button so token scopes can be modified after creation without having
to delete and recreate the token.
Add IsRequired field to IssueStatusDef. Open and Closed statuses are
seeded as required and cannot be deleted. Delete attempts return an
error flash in the web UI and ErrStatusRequired in the model layer.
API response now includes is_required field.
- Change CreateIssueOption fields from *int64 (optional) to int64
- API auto-assigns org defaults when value is 0
- MCP gitea_issue_create now requires status_id, priority_id, type_id
(pass 0 for org default)
- Keep optional on gitea_issue_update (partial updates)
Co-Authored-By: Moko Consulting <hello@mokoconsulting.tech>
Issue Status/Priority/Type API:
- Expose status_id, priority_id, type_id (with resolved names) on Issue API struct
- New endpoints: GET /orgs/{org}/issue-statuses, /issue-priorities, /issue-types
- CreateIssue and EditIssue handlers accept status_id, priority_id, type_id
- MCP tools: 5 new tools + updated create/update with metadata params
Org Wiki Tab:
- Convention repos: wiki (public) and wiki-private (members-only)
- Inline wiki rendering with markdown pipeline, sidebar, footer, page list
- Public/private view dropdown (same UX as org profile README selector)
- External wiki mode: link to outside URL from wiki tab
- Wiki mode setting in org settings (internal vs external with URL field)
- Migration 354: add wiki_mode and wiki_url to user table
- manifest_sync.go: add VersionPrefix and ElementName to XML parse and DB sync
- deploy workflow: use manifest API for version prefix instead of hardcoded sed
- pre-release workflow: rename moko-platform paths to mokoplatform
- All workflow headers: rename moko-platform references
- manifest.xml: update root element to mokoplatform, add version-prefix field
- Server: symlink /opt/mokoplatform -> /opt/moko-platform for compatibility
Add CDN system that serves release assets via a dedicated hostname
(e.g., cdn.mokoconsulting.tech) with per-asset public/private toggles,
IP/referrer allowlists, and aggressive caching headers.
- Host-based routing intercepts CDN domain before auth middleware
- Per-attachment cdn_public flag controls CDN visibility
- Releases in an update stream are excluded from CDN (update server takes precedence)
- CORS, ETag, Cache-Control headers for downstream CDN compatibility
- IP/CIDR and referrer domain allowlists for abuse prevention
Add the ability to filter issues by custom field values throughout
the entire search stack:
- DB: applyCustomFieldCondition joins custom_field_value with AND
semantics (all specified fields must match)
- Indexer: CustomFieldFilters map passed through SearchOptions and
ToDBOptions
- Web: parse cf_{fieldID}={value} query params, show dropdown
filters in the issue list sidebar for org-level fields
- API: both SearchIssues and ListIssues accept cf_ query params
Closes#496
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add `custom_fields` map to IssueTemplate struct so templates can specify
default values (e.g. `Priority: Medium`). On new issue form, org-level
issue-scoped fields appear in the sidebar with template defaults pre-selected.
NewIssuePost saves the values after issue creation. The API create issue
endpoint also accepts `custom_fields` by name.
Closes#493
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comment out custom-fields API routes in api.go that referenced handler
functions from the deleted routers/api/v1/repo/custom_field.go. Remove
the unreferenced modules/structs/custom_field.go and the duplicate
v1_25/v323 migration (superseded by v1_27/v343).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full namespace migration: update the Go module path and all import
statements from git.mokoconsulting.tech to code.mokoconsulting.tech.
Also updates all URL references in templates, workflows, configs,
tests, and documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add key editing, domain enforcement, purchase webhooks, public
validation API, channels multiselect, Joomla downloadkey element,
licensing feature toggle, unified update system, release tag
enforcement, heartbeat tracking, and improved settings UX.
Phase 1: Full key display with AbsoluteShort dates, master package
protection (hide edit/delete in UI, reject in handlers).
Phase 2: Key edit page with template, handlers, and routes for both
repo and org levels. Master keys redirect away.
Phase 3: Domain restriction checking against CSV allowlist,
MaxSites enforcement via CountUniqueDomainsByKey and
IsDomainKnownForKey, dlid query param support for Joomla.
Phase 4: Purchase webhook (POST /license-keys/purchase) with
PaymentRef idempotency. Public validation endpoint
(POST /license-keys/validate) outside auth middleware.
PATCH /license-keys/{id} for API key editing.
Phase 5: Channels multiselect using org UpdateStreamConfig streams
rendered as checkboxes, stored as JSON arrays.
Additional: downloadkey XML element, LicensingEnabled toggle on
UpdateStreamConfig, Dolibarr endpoint unified with key validation,
release tag suffix enforcement, LastHeartbeatUnix field with
TouchHeartbeat, and cleaned-up settings pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add REST API for managing license packages and keys:
- GET/POST /api/v1/repos/{owner}/{repo}/license-packages
- GET/POST /api/v1/repos/{owner}/{repo}/license-keys
- DELETE /api/v1/repos/{owner}/{repo}/license-keys/{id}
- GET /api/v1/repos/{owner}/{repo}/license-keys/{id}/usage
API structs for create/edit/response, with raw key only returned on
creation. Requires repo admin permissions.
Ref #239
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The WithFileConfig for LandingPage tried to parse the app.ini
LANDING_PAGE string value (e.g. "home") as JSON into a struct,
causing a startup hang. The landing page setting should only be
managed through the dynamic config system (admin UI).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a dynamic config option to set the default landing page for
unauthenticated visitors from Site Administration > Settings. Options:
- Home (default explore page)
- Explore (repository explore)
- Organizations (org explore)
- Login (redirect to login)
- Custom path (any internal URL like /MokoConsulting)
The setting takes effect immediately without restart, using the same
dynamic config system as maintenance mode and web banner. Falls back
to the static LANDING_PAGE setting from app.ini if not configured.
Closes#240
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rebrand the built-in actions bot user from upstream Gitea naming to
MokoGitea branding:
- Name: gitea-actions → mokogitea-actions
- FullName: Gitea Actions → MokoGitea Actions
- Email: teabot@gitea.io → mokogitea-actions[bot]@mokoconsulting.tech
Add backward-compatible name recognition so all three bot name variants
(mokogitea-actions, gitea-actions, github-actions) with optional [bot]
suffix resolve to the same system user.
Add WhitelistActionsUser, MergeWhitelistActionsUser, and
ForcePushAllowlistActionsUser toggles to branch protection rules,
allowing CI/CD workflows to push to protected branches when explicitly
enabled. Previously the actions bot (virtual user ID -2) could never be
added to whitelist because updateUserWhitelist() only validates real
database users.
Closes#233
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add runtime ${APP_NAME} placeholder substitution in locale strings so
all user-facing text reflects the configured APP_NAME from app.ini.
Replace 52 hardcoded locale strings, template literals, HTTP auth
realm headers, and Swagger API titles/descriptions with the
configurable value.
Closes#1
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backport #37588 by @pandareen
## Summary
Fixes
[go-gitea/gitea#37564](https://github.com/go-gitea/gitea/issues/37564):
when an OIDC provider returns a `picture` claim, Gitea is supposed to
download that image as the user's avatar (if `[oauth2_client]
UPDATE_AVATAR = true`). Two latent bugs prevented this from working
consistently:
1. **Default Go User-Agent rejected by some image hosts.**
`oauth2UpdateAvatarIfNeed` used `http.Get`, which sends `User-Agent:
Go-http-client/1.1`. Hosts like `upload.wikimedia.org` reject that UA
with `403`, and every error path silently returned, so the user was left
with an identicon and **no log line** to diagnose the issue.
2. **Link-account *register* path skipped avatar sync.** First-time OIDC
sign-ins where auto-registration is disabled (or required a
username/password retype) go through `LinkAccountPostRegister`, which
created the user but never called `oauth2SignInSync`. So the avatar /
full name / SSH keys from the IdP were dropped on the floor for those
users, even though the existing-account-link path (`oauth2LinkAccount`)
and the auto-register path (`handleOAuth2SignIn`) both already did the
sync.
## Changes
- `routers/web/auth/oauth.go` — `oauth2UpdateAvatarIfNeed` now uses
`http.NewRequest` + `http.DefaultClient.Do`, sets `User-Agent: Gitea
<version>`, and logs every failure path at `Warn` (invalid URL, fetch
error, non-200, body read error, oversize body, upload error). No silent
failures.
- `routers/web/auth/linkaccount.go` — `LinkAccountPostRegister` now
calls `oauth2SignInSync` after a successful user creation, mirroring the
auto-register and link-existing-account flows.
- `tests/integration/oauth_avatar_test.go` — new
`TestOAuth2AvatarFromPicture` integration test with five sub-cases:
- `AutoRegister_FetchesAvatarFromPictureWithGiteaUA` — happy path,
asserts `use_custom_avatar=true`, an avatar hash is set, exactly one
HTTP request was made, and the request carried a `Gitea ` UA. The mock
server enforces the UA prefix to mirror real-world hosts that reject
Go's default UA.
- `AutoRegister_NonOK_DoesNotUpdateAvatar` — server returns 403; user's
avatar must remain unset.
- `AutoRegister_EmptyPicture_NoFetch` — empty `picture` claim must not
trigger any HTTP request.
- `AutoRegister_UpdateAvatarFalse_NoFetch` — `UPDATE_AVATAR=false` must
not trigger any HTTP request.
- `LinkAccountRegister_FetchesAvatarFromPicture` — guards the
`linkaccount.go` fix; without the new `oauth2SignInSync` call this
assertion fails.
## Test plan
- [x] `go test -tags 'sqlite sqlite_unlock_notify' -run
'^TestOAuth2AvatarFromPicture$' ./tests/integration/ -v` — 5/5 sub-tests
pass.
- [x] Manual: log in as a Keycloak user with `picture` claim pointing at
`https://avatars.githubusercontent.com/u/9919?v=4` — Gitea avatar is
replaced with the GitHub picture.
- [x] Manual: same flow with `https://upload.wikimedia.org/...` —
request now succeeds (or returns a clearly logged `Warn` line if
rate-limited with `429`); previously it silently 403'd.
- [x] Manual: `UPDATE_AVATAR=false` — user keeps the identicon, no
outbound request in container logs.
- [ ] Reviewer: please double-check that no other call sites of
`oauth2UpdateAvatarIfNeed` rely on the old `http.Get` behaviour.
## Related
- Upstream issue: go-gitea/gitea#37564
--------------------------------------------
AI Editor was used in this PR
---------
Signed-off-by: silverwind <me@silverwind.io>
Co-authored-by: pandareen <7270563+pandareen@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
When a user signs in, sends notifications with username, IP address,
user agent, and timestamp. Notifications go through:
- Email to the user's registered address
- ntfy push to the configured topic
Enabled by default, configurable via app.ini:
[login_notification]
ENABLED = true
The notification fires asynchronously (goroutine) so it doesn't
block the login redirect. Hooks into handleSignInFull which is the
single choke point for all auth methods (password, 2FA, OAuth).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The update checker now emails the first admin user when a new version
is found on the configured channel. Notifications are deduplicated —
only sent once per new version, not on every cron tick.
- Added NotifyFunc callback in updatechecker module
- Wired to mailer in cron task registration
- Created mail_update.go with plain-text email including version,
channel, release URL, and docker pull command
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Matches the Joomla update server pattern used across all Moko repos.
Removed the non-standard 'security' channel. All five standard
channels now present in updates.xml.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the Go module path from code.gitea.io/gitea to
git.mokoconsulting.tech/MokoConsulting/MokoGitea across the entire
codebase.
Scope:
- go.mod module declaration
- 2,235 Go source files (import paths)
- Dockerfile WORKDIR and COPY paths
- Swagger API templates
- golangci.yml linter config
External dependencies (code.gitea.io/gitea-vet, code.gitea.io/sdk/gitea,
gitea.com/gitea/act, etc.) are intentionally NOT renamed — they are
separate upstream modules.
Closes#132
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace JSON API-based update checking with Joomla-style updates.xml
that supports multiple update streams (stable, dev, security).
Changes:
- Add updates.xml at repo root with stable/dev/security channels
following the same XML structure as MokoOnyx and other Joomla repos
- Rewrite updatechecker module to parse XML with channel filtering
- Add CHANNEL setting to [update_checker] config (default: stable)
- Show channel name and docker pull command in admin dashboard banner
Config example:
[update_checker]
ENABLED = true
CHANNEL = stable
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove .mod -> AMPL mapping from conflictingExtLangMap (AMPL lexer
doesn't exist in chroma v2.23.1, causing a panic when viewing .mod
files). Upstream doesn't have this mapping either.
- Update 500 error page issue link to MokoGitea repo
- Update home page install/license links to MokoGitea repo
- Update theme settings link to MokoGitea repo
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This PR hardens artifact URL signing by encoding signature inputs in an
unambiguous binary payload before computing the HMAC.
What it changes:
- replace direct concatenation-style signing inputs with explicit
payload builders
- encode string fields with a length prefix before appending their bytes
- encode integer fields as fixed-width binary values instead of decimal
text
- apply the same hardening to both:
- Actions Artifact V4 signing in `routers/api/actions/artifactsv4.go`
- artifact download signing in `routers/api/v1/repo/action.go`
- add regression tests that verify distinct field combinations produce
distinct payloads and signatures
Why:
The previous signing logic built HMAC inputs by appending multiple
fields without a strongly structured representation. That kind of
construction can create ambiguity at field boundaries, where different
parameter combinations may serialize into the same byte stream for
signing.
This change removes that ambiguity by constructing a deterministic
payload format with explicit boundaries between fields.
Backport #37707
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
Add four new API endpoints under /repos/{owner}/{repo}/issues/bulk/ for
performing batch operations across multiple issues in a single request.
Each endpoint returns a partial-failure result with per-issue success/failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Restore original modules/badge/badge.go (upstream) and adapt our
presets to use GenerateBadge() and the existing Badge type.
- Rename Generate → GenerateRepoBadge to avoid conflict
- Add FormatRepoBadgeSVG for SVG rendering
- Add RenderFlat/RenderFlatSquare methods on Badge
- Fix API handler to use new function names
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- GetLatestCommitStatus takes db.ListOptions not int
- Use GetRepoLicenses() instead of non-existent repo.License field
- Use repo.Topics instead of repo.HasWiki() (not a method)
- licenseBadge now takes ctx parameter
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>