mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 10:23:14 +00:00
Point dev agent at seahaven-open-swe-dev org + un-pin the owner guard (#27)
Dev now runs against the dedicated seahaven-open-swe-dev org (repo openswe-dev-sandbox), isolated from the real Sea Haven org. Two changes: - config-store.ts: iacManagedSsm repo targeting is now per-env — dev = seahaven-open-swe-dev/openswe-dev-sandbox, prod stays Sea-Haven-Industries/open-swe-pilot. ALLOWED_GITHUB_ORGS tracks the env owner. Adds a dev-only SEED_USER_MAPPINGS param so the triggering GitHub login (amoussa1229) resolves and @openswe comments aren't skipped. - fetch-config.sh: replace the unconditional hard-pin to Sea-Haven-Industries with an owner GUARD that HONORS the configured owner (OPENSWE_REPO_OWNER override, else the SSM value) but forces a per-env safe org when the normalized owner is blank or the upstream langchain-ai. Normalization (lowercase, strip whitespace, first path segment, drop dots) catches langchain-ai/<repo>, langchain-ai., and case variants without over-blocking legit orgs (e.g. langchain-ai-fork). Fallback org is per-env so dev can't fall back into the real org. Reviews: GPT-4.1 cross-review APPROVE (round 1 found a path/dot bypass -> hardened, round 2 clean); /sh-security-review authz one LOW (env-invariant fallback) -> fixed. Positive org allowlist still enforced by the app via ALLOWED_GITHUB_ORGS.
This commit is contained in:
parent
082768ff51
commit
5e30bc6be2
2 changed files with 58 additions and 22 deletions
|
|
@ -80,10 +80,20 @@ APP_ENV_LINK="${APP_DIR}/.env" # symlink -> ENV_FILE
|
|||
SERVICE_USER="${SERVICE_USER:-openswe}"
|
||||
SERVICE_GROUP="${SERVICE_GROUP:-${SERVICE_USER}}"
|
||||
|
||||
# Sea Haven override: upstream defaults DEFAULT_REPO_OWNER to "langchain-ai".
|
||||
# For the Sea Haven deployment it MUST be the org. fetch-config forces this so a
|
||||
# stale/blank SSM value can never point the agent at the upstream org.
|
||||
SH_REPO_OWNER="${OPENSWE_REPO_OWNER:-Sea-Haven-Industries}"
|
||||
# Sea Haven owner guard inputs (applied after the store is read, below). The owner
|
||||
# normally comes from SSM /open-swe-<env>/DEFAULT_REPO_OWNER; OPENSWE_REPO_OWNER is an
|
||||
# explicit operator override that wins over the store. FORBIDDEN = the upstream org
|
||||
# the fork must never target; SAFE = the fallback when the resolved owner is blank or
|
||||
# forbidden. (Upper/lower + whitespace are normalized before the guard check.)
|
||||
OPENSWE_REPO_OWNER="${OPENSWE_REPO_OWNER:-}"
|
||||
FORBIDDEN_REPO_OWNER="langchain-ai"
|
||||
# Fallback org when the resolved owner is blank/forbidden — PER-ENV (mirrors the
|
||||
# iacManagedSsm owner) so a dev box can NEVER fall back into the real Sea Haven org;
|
||||
# it stays isolated in its own dev org. Defends the blank/upstream cases in-env.
|
||||
case "$ENV" in
|
||||
dev) SAFE_REPO_OWNER="seahaven-open-swe-dev" ;;
|
||||
*) SAFE_REPO_OWNER="Sea-Haven-Industries" ;;
|
||||
esac
|
||||
|
||||
for bin in aws jq; do
|
||||
command -v "$bin" >/dev/null 2>&1 || { echo "fetch-config: '$bin' not found on PATH" >&2; exit 3; }
|
||||
|
|
@ -191,16 +201,30 @@ while IFS=$'\t' read -r nb vb; do
|
|||
done < <(batch_get_secrets_tsv)
|
||||
log "loaded ${secret_count} secret(s) from Secrets Manager"
|
||||
|
||||
# --- Sea Haven DEFAULT_REPO_OWNER hard pin -----------------------------------
|
||||
# T5 OSWE-OWNER-04: a HARD pin, not a deny-list. Whatever the store holds (blank,
|
||||
# upstream 'langchain-ai', a case/space variant, or any other org), the owner is
|
||||
# unconditionally forced to the Sea Haven org so the agent can never target the
|
||||
# wrong owner.
|
||||
cur_owner="${VARS[DEFAULT_REPO_OWNER]:-}"
|
||||
if [ -n "$cur_owner" ] && [ "$cur_owner" != "$SH_REPO_OWNER" ]; then
|
||||
log "WARNING: SSM DEFAULT_REPO_OWNER differs from the pinned org -> hard-pinning to '${SH_REPO_OWNER}'"
|
||||
fi
|
||||
VARS[DEFAULT_REPO_OWNER]="$SH_REPO_OWNER"
|
||||
# --- Sea Haven DEFAULT_REPO_OWNER guard --------------------------------------
|
||||
# OSWE-OWNER-04 (revised for multi-org): HONOR the configured owner — the
|
||||
# OPENSWE_REPO_OWNER env override if set, else the store value — so per-env orgs
|
||||
# work (dev = seahaven-open-swe-dev, prod = Sea-Haven-Industries). But GUARD the two
|
||||
# values that must NEVER reach the agent: blank, and the upstream 'langchain-ai' org
|
||||
# (the fork's origin). Either falls back to the Sea Haven org so a stale/blank/mis-set
|
||||
# value can never point the agent upstream. Comparison is case- and whitespace-
|
||||
# insensitive. The POSITIVE org allowlist is enforced by the app (ALLOWED_GITHUB_ORGS).
|
||||
resolved_owner="${OPENSWE_REPO_OWNER:-${VARS[DEFAULT_REPO_OWNER]:-}}"
|
||||
# Normalize for the guard CHECK ONLY (the original value is what gets stored when
|
||||
# allowed): lowercase, strip whitespace, take the FIRST path segment so a value like
|
||||
# 'langchain-ai/open-swe' still trips the guard, and drop dots (GitHub owners contain
|
||||
# none) so 'langchain-ai.' can't slip past. Homoglyph/unicode variants are out of scope
|
||||
# here — the owner comes from admin-written SSM/IaC, not attacker-controlled input.
|
||||
norm_owner="$(printf '%s' "$resolved_owner" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
|
||||
norm_owner="${norm_owner%%/*}"
|
||||
norm_owner="${norm_owner//./}"
|
||||
case "$norm_owner" in
|
||||
"" | "$FORBIDDEN_REPO_OWNER")
|
||||
log "WARNING: DEFAULT_REPO_OWNER ('${resolved_owner:-<blank>}') is blank or the upstream org -> forcing '${SAFE_REPO_OWNER}'"
|
||||
resolved_owner="$SAFE_REPO_OWNER"
|
||||
;;
|
||||
esac
|
||||
VARS[DEFAULT_REPO_OWNER]="$resolved_owner"
|
||||
|
||||
# --- FAIL-FAST: required vars -------------------------------------------------
|
||||
# Hard-required regardless of mode:
|
||||
|
|
|
|||
|
|
@ -123,16 +123,21 @@ export function iacManagedSsm(env: EnvName): Record<string, string> {
|
|||
// recon: seahaven.com Route53 zone + *.seahaven.com ACM cert are live on the ALB
|
||||
// — distinct from the on-prem seahavenind.com). dev = openswe-dev, prod = openswe.
|
||||
const host = `https://openswe${env === "dev" ? "-dev" : ""}.seahaven.com`;
|
||||
return {
|
||||
// Repo targeting is PER-ENV: dev drives the disposable sandbox repo in the
|
||||
// dedicated seahaven-open-swe-dev org (isolates dev-agent activity from the real
|
||||
// Sea Haven org); prod stays on the Sea Haven org pilot repo. fetch-config's owner
|
||||
// GUARD honors this value (rejecting only blank / the upstream langchain-ai org).
|
||||
const repo =
|
||||
env === "dev"
|
||||
? { owner: "seahaven-open-swe-dev", name: "openswe-dev-sandbox" }
|
||||
: { owner: "Sea-Haven-Industries", name: "open-swe-pilot" };
|
||||
const managed: Record<string, string> = {
|
||||
// Sandbox provider — plan keeps stock langsmith (T9). Stable.
|
||||
SANDBOX_TYPE: "langsmith",
|
||||
// Sea Haven org pin. fetch-config ALSO hard-pins this at boot, but owning the
|
||||
// real value here keeps SSM self-consistent rather than blank.
|
||||
DEFAULT_REPO_OWNER: "Sea-Haven-Industries",
|
||||
// Repo allowlist (comma list) — the Sea Haven org. Stable/derivable.
|
||||
ALLOWED_GITHUB_ORGS: "Sea-Haven-Industries",
|
||||
// Pilot repo (project memory: Sea-Haven-Industries/open-swe-pilot).
|
||||
DEFAULT_REPO_NAME: "open-swe-pilot",
|
||||
DEFAULT_REPO_OWNER: repo.owner,
|
||||
// Repo-owner allowlist (comma list) — the env's org. The app gates triggers on this.
|
||||
ALLOWED_GITHUB_ORGS: repo.owner,
|
||||
DEFAULT_REPO_NAME: repo.name,
|
||||
// Dashboard URLs — derived from the public host.
|
||||
DASHBOARD_BASE_URL: host,
|
||||
DASHBOARD_API_BASE_URL: host,
|
||||
|
|
@ -140,6 +145,13 @@ export function iacManagedSsm(env: EnvName): Record<string, string> {
|
|||
// Primary builder model (project memory team_settings: anthropic:claude-opus-4-8).
|
||||
LLM_MODEL_ID: "anthropic:claude-opus-4-8",
|
||||
};
|
||||
// Dev e2e smoke: seed the owner's user_mapping so an @openswe comment from the
|
||||
// triggering GitHub login resolves (an unmapped commenter is silently skipped).
|
||||
// Dev-only — prod seeds its mappings via its own operator config. login:email.
|
||||
if (env === "dev") {
|
||||
managed.SEED_USER_MAPPINGS = "amoussa1229:adam@seahavenind.com";
|
||||
}
|
||||
return managed;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue