Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ jobs:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
queries: security-extended
# tests/security/fixtures/** are DELIBERATELY vulnerable planted-vuln
# samples that the security enforcement-matrix suite scans/asserts on;
# scanning them here just re-flags the intentional issues.
config: |
paths-ignore:
- tests/security/fixtures

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@ce28f5bb42b7a9f2c824e633a3f6ee835bab6858 # v3.29.0
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/helm-chart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ jobs:
docker build -t forge/$svc:0.1.0 -f deploy/docker/$svc.Dockerfile .
done

# The chart declares postgresql/redis/minio subchart deps in Chart.yaml, so
# `helm install`/`upgrade` needs them vendored into charts/ before it runs —
# even though the kind overlay disables them (it stands up external in-cluster
# datastores). Without this, install fails "missing in charts/ directory".
- name: Add subchart repos + build chart dependencies
run: |
helm repo add bitnami https://charts.bitnami.com/bitnami
helm dependency build ${CHART}

- name: Run kind smoke tests (install + helm test + upgrade/rollback)
env:
FORGE_KIND_CLUSTER: forge-ci # reuse the kind-action cluster
Expand Down
10 changes: 10 additions & 0 deletions apps/api/forge_api/auth/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ def resolve_model_client(
workspace_id: uuid.UUID,
*,
secret_id: uuid.UUID | None = None,
model: str | None = None,
redactor: Callable[[str], str] = redact_text,
) -> ModelClient:
"""Resolve a provider-agnostic BYOK :class:`ModelClient` for a workspace.
Expand All @@ -382,9 +383,16 @@ def resolve_model_client(
never logged. The injected ``redactor`` scrubs any provider exception before
it is re-raised as ``ModelClientError``.

``model`` overrides the env-configured model name — used by the
Adaptive Orchestration model router (``ao-model-router``) to bind a
tier-resolved model onto the workspace's provider/key without touching
any other client knob.

Raises ``ModelClientError`` when no provider is configured, and
``ModelClientUnavailable`` when the provider SDK extra is not installed.
"""
import dataclasses

from forge_agent.providers import ModelClientConfig, ModelClientError, build_model_client

if secret_id is not None:
Expand All @@ -405,6 +413,8 @@ def resolve_model_client(
"no model provider configured; set FORGE_MODEL_PROVIDER and a BYOK "
"key (env or vault under MODEL_PROVIDER, + FORGE_MODEL_NAME for OpenAI)"
)
if model:
config = dataclasses.replace(config, model=model)
return build_model_client(config, redactor=redactor)

# -- OAuth descriptor --------------------------------------------------- #
Expand Down
17 changes: 15 additions & 2 deletions apps/api/forge_api/routers/saml.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,21 @@ def _require_enabled(config: SsoConfiguration) -> None:


def _safe_next(target: str | None) -> str:
"""Only same-origin relative paths are honoured (open-redirect guard)."""
if target and target.startswith("/") and not target.startswith("//"):
"""Only same-origin relative paths are honoured (open-redirect guard).

Rejects protocol-relative (``//host``) and backslash-normalised
(``/\\host``) targets — browsers treat ``\\`` as ``/``, so ``/\\evil.com``
would otherwise redirect off-origin — plus any embedded control characters.
"""
if (
target
and target.startswith("/")
and not target.startswith("//")
and "\\" not in target
and "\r" not in target
and "\n" not in target
and "\t" not in target
):
return target
return "/"

Expand Down
Loading
Loading