mirror of
https://github.com/stack-auth/stack.git
synced 2026-06-13 21:01:21 +08:00
Merge branch 'dev' into ai-analytics
This commit is contained in:
commit
45fd5ad7da
@ -541,3 +541,6 @@ A: Put restricted-user docs at `docs-mintlify/guides/apps/authentication/restric
|
||||
|
||||
## Q: How should e2e tests switch to a newly created project?
|
||||
A: `Project.createAndSwitch` should leave `backendContext.projectKeys` set to real project API keys, not only `{ projectId, adminAccessToken }`. Internal admin access tokens are regular short-lived access tokens; keeping one in the default project context makes later server/admin requests fail with `ADMIN_ACCESS_TOKEN_EXPIRED` or validate the token against the wrong project.
|
||||
|
||||
## Q: How should backend SMTP SSRF checks be rolled out?
|
||||
A: Keep the real outbound SMTP policy in `apps/backend/src/private/implementation/smtp-egress-policy.ts`, export it through `apps/backend/src/private/index.ts`, and provide a simple `implementation-fallback` function for self-hosters. It should allow only SMTP ports 25, 465, 587, 2465, 2587, and 2525, reject internal IP literals or DNS resolutions, and initially run report-only from `emails-low-level.tsx` via `captureError("smtp-egress-policy-report-only", ...)` before enforcing hard failures.
|
||||
|
||||
@ -116,6 +116,8 @@ To see all development ports, refer to the index.html of `apps/dev-launchpad/pub
|
||||
- NEVER INSTALL A NEW PACKAGE (or anything else) WITHOUT EXPLICIT APPROVAL FROM THE USER.
|
||||
- A "development environment" is either an RDE (remote development environment; = local dashboard + prod backend) or a local emulator (local dashboard + local backend). When communicating to the user, we always say "development environment" instead of RDE or local emulator (the distinction to the user is minor, even though the implementation is quite different).
|
||||
- NEVER EVER return a server error with an internal server error that may contain information that the user shouldn't see. For example, never return the error message on a public API from an upstream provider without properly filtering it first. Most of the time, for internal server errors, you should just use StackAssertionError (which won't pass the message to the user), not StatusError (you almost never want to instantiate a StatusError with status 5xx).
|
||||
- When adding code to the `private` part of the backend, put the actual implementation into `implementation` (if the submodule is checked out), and implement a simple fallback in `implementation-fallback` for self-hosters. `implementation.generated.ts` will automatically be generated, which you can then import from `index.ts`. (See the existing code as an example.) If the submodule isn't checked out, but you need to add code to the `private` part of the backend, let the user know.
|
||||
- Security-sensitive code on the backend that shouldn't be public should be in the `private` part of the backend.
|
||||
|
||||
### Code-related
|
||||
- Use ES6 maps instead of records wherever you can.
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/backend",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@ -10,6 +10,7 @@ import { runAsynchronously, wait } from '@stackframe/stack-shared/dist/utils/pro
|
||||
import { Result } from '@stackframe/stack-shared/dist/utils/results';
|
||||
import { traceSpan } from '@stackframe/stack-shared/dist/utils/telemetry';
|
||||
import nodemailer from 'nodemailer';
|
||||
import { checkSmtpEgressPolicy } from '@/private';
|
||||
|
||||
export function isSecureEmailPort(port: number | string) {
|
||||
// "secure" in most SMTP clients means implicit TLS from byte 1 (SMTPS)
|
||||
@ -76,10 +77,27 @@ async function _lowLevelSendEmailWithoutRetries(options: LowLevelSendEmailOption
|
||||
|
||||
return await traceSpan('sending email to ' + JSON.stringify(toArray), async () => {
|
||||
try {
|
||||
const smtpEgressPolicyResult = await checkSmtpEgressPolicy({
|
||||
host: options.emailConfig.host,
|
||||
port: options.emailConfig.port,
|
||||
});
|
||||
if (smtpEgressPolicyResult.status === "error") {
|
||||
console.warn("SMTP config rejected by the egress policy.", {
|
||||
violation: smtpEgressPolicyResult.violation,
|
||||
config: strippedEmailConfig,
|
||||
});
|
||||
captureError("smtp-egress-policy-report-only", new StackAssertionError("SMTP config would be rejected by the egress policy", {
|
||||
violation: smtpEgressPolicyResult.violation,
|
||||
config: strippedEmailConfig,
|
||||
}));
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: options.emailConfig.host,
|
||||
port: options.emailConfig.port,
|
||||
secure: options.emailConfig.secure,
|
||||
disableFileAccess: true,
|
||||
disableUrlAccess: true,
|
||||
connectionTimeout: 15000,
|
||||
greetingTimeout: 10000,
|
||||
socketTimeout: 20000,
|
||||
|
||||
@ -1 +1 @@
|
||||
Subproject commit b05bcca3444c00fa6623f7eec332376031aefd2c
|
||||
Subproject commit a815ddcd1354d4bf27042626d1035709c80abdc6
|
||||
@ -1,6 +1,7 @@
|
||||
import { AiProxyBodyProcessor } from "@/lib/ai/proxy-preprocessing";
|
||||
import { SignUpRiskEngine } from "@/lib/risk-scores";
|
||||
import { createNeutralSignUpHeuristicFacts } from "@/lib/sign-up-heuristics";
|
||||
import type { SmtpEgressPolicyResult } from "../types";
|
||||
|
||||
export const signUpRiskEngine: SignUpRiskEngine = {
|
||||
async calculateRiskAssessment() {
|
||||
@ -12,3 +13,13 @@ export const signUpRiskEngine: SignUpRiskEngine = {
|
||||
};
|
||||
|
||||
export const preprocessProxyBody: AiProxyBodyProcessor = ({ parsedBody }) => parsedBody;
|
||||
|
||||
export async function checkSmtpEgressPolicy(options: {
|
||||
host: string,
|
||||
port: number,
|
||||
}): Promise<SmtpEgressPolicyResult> {
|
||||
return {
|
||||
status: "ok",
|
||||
addresses: [options.host],
|
||||
};
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
export { signUpRiskEngine, preprocessProxyBody } from "./implementation.generated";
|
||||
export { signUpRiskEngine, preprocessProxyBody, checkSmtpEgressPolicy } from "./implementation.generated";
|
||||
|
||||
11
apps/backend/src/private/types.ts
Normal file
11
apps/backend/src/private/types.ts
Normal file
@ -0,0 +1,11 @@
|
||||
export type SmtpEgressPolicyViolation = {
|
||||
reason: "disallowed-port" | "internal-ip-literal" | "internal-resolved-address" | "no-dns-addresses" | "dns-lookup-failed",
|
||||
host: string,
|
||||
port: number,
|
||||
addresses?: string[],
|
||||
cause?: unknown,
|
||||
};
|
||||
|
||||
export type SmtpEgressPolicyResult =
|
||||
| { status: "ok", addresses: string[] }
|
||||
| { status: "error", violation: SmtpEgressPolicyViolation };
|
||||
@ -325,12 +325,7 @@ const parseAuth = withTraceSpan('smart request parseAuth', async (req: NextReque
|
||||
throw new KnownErrors.BranchDoesNotExist(branchId);
|
||||
}
|
||||
|
||||
// As explained above, as a performance optimization we already fetch the user from the global database optimistically
|
||||
// If it turned out that the source-of-truth is not the global database, we'll fetch the user from the source-of-truth
|
||||
// database instead.
|
||||
const user = tenancy.config.sourceOfTruth.type === "hosted"
|
||||
? await queriesResults.userIfOnGlobalPrismaClient
|
||||
: (userId ? await getUser({ userId, projectId, branchId }) : undefined);
|
||||
const user = await queriesResults.userIfOnGlobalPrismaClient;
|
||||
|
||||
return {
|
||||
project,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/dashboard",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/dev-launchpad",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/e2e-tests",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@stackframe/hosted-components",
|
||||
"private": true,
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}09",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@stackframe/internal-tool",
|
||||
"private": true,
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node scripts/pre-dev.mjs && next dev --turbopack --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}41",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/mcp",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/mock-oauth-server",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/skills",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/docs-mintlify",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "mint dev --port ${NEXT_PUBLIC_STACK_PORT_PREFIX:-81}04 --no-open",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-docs",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-cjs-test",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/convex-example",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-demo-app",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"description": "",
|
||||
"private": true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/docs-examples",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"description": "",
|
||||
"private": true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/e-commerce-demo",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/js-example",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"description": "",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@stackframe/lovable-react-18-example",
|
||||
"private": true,
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-middleware-demo",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "react-example",
|
||||
"private": true,
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-supabase",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/example-tanstack-start-demo",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"description": "TanStack Start demo app for Stack Auth",
|
||||
"private": true,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/dashboard-ui-components",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/init-stack",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"description": "The setup wizard for Stack. https://stack-auth.com",
|
||||
"main": "dist/index.mjs",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/js",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/react",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-cli",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"description": "The CLI for Stack Auth. https://stack-auth.com",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-sc",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"exports": {
|
||||
"./force-react-server": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-shared",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsdown",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/stack-ui",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/stack",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/tanstack-start",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
|
||||
"//": "NEXT_LINE_PLATFORM template",
|
||||
"private": true,
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"//": "THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template (FOR package.json FILES, PLEASE EDIT package-template.json)",
|
||||
"name": "@stackframe/template",
|
||||
"private": true,
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"repository": "https://github.com/hexclave/stack-auth",
|
||||
"sideEffects": false,
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/swift-sdk",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"private": true,
|
||||
"description": "Stack Auth Swift SDK",
|
||||
"scripts": {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@stackframe/sdk-spec",
|
||||
"version": "2.8.102",
|
||||
"version": "2.8.103",
|
||||
"private": true,
|
||||
"description": "Stack Auth SDK specification files",
|
||||
"scripts": {}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user