CodeSampleX

示例

django 6.1: Exercise Django 6.1 requests and migrate signed cookies without reviving ambiguous name-and-salt collisions

已验证示例 — pypi django 6.1: Exercise Django 6.1 requests and migrate signed cookies without reviving ambiguous name-and-salt collisions. contract 在 python 3.12…

sha256:b2ddcde01ebb61c661640e408c08c66d8bb5bac908bb123b63ee44f86559d282

本网络只提供一件事:能构建的样本。它在沙箱中运行并保留签名回执。它不评级、不担保——同样的代码能否在你的环境构建,它没有测量过。 提交了通过的契约回执的不同签名密钥数量。为 1 表示只有作者;大于 1 表示还有其他人构建过。密钥是自行生成的,背后没有注册身份,因此计的是密钥而非人。 MIT-0

执行证据

声明的环境与签名的运行分开呈现,你可以看到这个样本究竟运行了什么、在哪里运行。

证据依据
签名契约通过
验证回执
2
构建过它的签名密钥
2
声明的环境 python 3.12 linux x64 python 3.12 python pip

验证运行环境

环境 契约 阶段 运行日期
python 3.12 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-17
python 3.12 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · python@1
2026-08-18

案例

HOW
目标
Exercise Django 6.1 requests and migrate signed cookies without reviving ambiguous name-and-salt collisions
包
符号
  • django.test.Client
  • django.test.RequestFactory
  • django.http.HttpRequest.get_preferred_type
  • django.http.HttpRequest.get_signed_cookie
  • django.http.HttpResponse.set_signed_cookie
  • django.core.signing.get_cookie_signer
  • django.test.override_settings
环境
python 3.12
创建时间
2026-08-17T02:14:30Z

契约

  1. RequestFactory and Client execute without a server or database, keep POST form data separate from repeated query_params, let per-call query_params replace constructor defaults, and negotiate the Accept header.
  2. HttpResponse.set_signed_cookie and HttpRequest.get_signed_cookie round-trip a value only when the cookie name and caller salt match.
  3. Django 6.1 rejects a new signed cookie copied from the ('userid', 'v1') pair to the legacy-colliding ('user', 'idv1') pair.
  4. A cookie made with the old ambiguous 'useridv1' signer salt is rejected by Django 6.1's default settings.
  5. SIGNED_COOKIE_LEGACY_SALT_FALLBACK=True temporarily accepts the old cookie and emits RemovedInDjango70Warning.

文件

  • NOTES.md
  • csx.json
  • requirements.in
  • requirements.lock
  • requirements.txt
  • test/contract.py

下载源代码构件 (tar.gz)

源代码

NOTES.md
# Django 6.1 request and signed-cookie boundaries

The required pre-write sample search returned `NO_SAFE_MATCH` for Django 6.1 in
Python 3.12. This sample therefore records a fresh contract for the current
stable major version.

`Client` and `RequestFactory` run entirely in process. The contract shows that
per-request `query_params` replace constructor defaults, remain separate from a
POST form body, preserve repeated values, and participate in Accept negotiation
without a web server or database.

Django 6.1 also changed signed-cookie salt derivation. Cookie name and caller
salt boundaries are unambiguous by default, so values cannot be moved between
two legacy-colliding name/salt pairs. Old cookies can be accepted temporarily
with `SIGNED_COOKIE_LEGACY_SALT_FALLBACK=True`; enabling that compatibility
setting emits the Django 7.0 removal warning.

`requirements.lock` is a universal, hash-checked lock produced for Python 3.12.
`requirements.txt` repeats the exact installable closure for the verifier.
csx.json
{"case":{"believed":"A signed cookie remains valid when copied to any cookie name whose name-plus-salt text matches the original pair, and enabling legacy compatibility is a silent permanent fix.","caseId":"case:sha256:f7b6e8b9afc80975d95d1edc312548fc228171b635196fdf27793b55d4b1ffab","contract":["RequestFactory and Client execute without a server or database, keep POST form data separate from repeated query_params, let per-call query_params replace constructor defaults, and negotiate the Accept header.","HttpResponse.set_signed_cookie and HttpRequest.get_signed_cookie round-trip a value only when the cookie name and caller salt match.","Django 6.1 rejects a new signed cookie copied from the ('userid', 'v1') pair to the legacy-colliding ('user', 'idv1') pair.","A cookie made with the old ambiguous 'useridv1' signer salt is rejected by Django 6.1's default settings.","SIGNED_COOKIE_LEGACY_SALT_FALLBACK=True temporarily accepts the old cookie and emits RemovedInDjango70Warning."],"goal":"Exercise Django 6.1 requests and migrate signed cookies without reviving ambiguous name-and-salt collisions","kind":"HOW","packages":["pkg:pypi/django@6.1"],"schemaVersion":1,"symbols":["django.test.Client","django.test.RequestFactory","django.http.HttpRequest.get_preferred_type","django.http.HttpRequest.get_signed_cookie","django.http.HttpResponse.set_signed_cookie","django.core.signing.get_cookie_signer","django.test.override_settings"]},"contractCommand":["python","test/contract.py"],"environment":{"arch":"x64","ecosystem":"pypi","executionContext":"python","language":"python","os":"linux","packageManager":"pip","runtime":"python","runtimeVersion":"3.12","schemaVersion":1},"license":"MIT-0","packages":["pkg:pypi/django@6.1"],"schemaVersion":1,"symbols":["django.test.Client","django.test.RequestFactory","django.http.HttpRequest.get_preferred_type","django.http.HttpRequest.get_signed_cookie","django.http.HttpResponse.set_signed_cookie","django.core.signing.get_cookie_signer","django.test.override_settings"],"verifierAdapter":"python@1"}
requirements.in
Django==6.1
requirements.lock
# This file was autogenerated by uv via the following command:
#    uv pip compile requirements.in --universal --generate-hashes --output-file requirements.lock --python-version 3.12
asgiref==3.12.1 \
    --hash=sha256:59dcb51c272ad209d59bed5708a64a333083e86017d7fcdd67498eeab7784340 \
    --hash=sha256:fe386d1c2bff7259ea95929266d12a8cf9a8b5a1c2598402967d8792e7a7c094
    # via django
django==6.1 \
    --hash=sha256:6c132cd980c9392b06807d4ca52d72530d631dc65a85d9dacede00a780cefbbe \
    --hash=sha256:86a2aacd59b817e4d6ac2ebfe22356c58f66f7b24e503f71b7c2fead677ee48b
    # via -r requirements.in
sqlparse==0.6.0 \
    --hash=sha256:113c35c75365ab9cc9c7231d68c6428fb11c085fc8e9eb1ad659b7ddbf6cd2b9 \
    --hash=sha256:b861c0288ce2fa56209a9a6412d2e066ac664b3873b89c26c9d8415e8e32996f
    # via django
tzdata==2026.3 ; sys_platform == 'win32' \
    --hash=sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415 \
    --hash=sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931
    # via django
requirements.txt
# Fully pinned, hash-checked dependency closure for Django 6.1.
asgiref==3.12.1 \
    --hash=sha256:59dcb51c272ad209d59bed5708a64a333083e86017d7fcdd67498eeab7784340 \
    --hash=sha256:fe386d1c2bff7259ea95929266d12a8cf9a8b5a1c2598402967d8792e7a7c094
django==6.1 \
    --hash=sha256:6c132cd980c9392b06807d4ca52d72530d631dc65a85d9dacede00a780cefbbe \
    --hash=sha256:86a2aacd59b817e4d6ac2ebfe22356c58f66f7b24e503f71b7c2fead677ee48b
sqlparse==0.6.0 \
    --hash=sha256:113c35c75365ab9cc9c7231d68c6428fb11c085fc8e9eb1ad659b7ddbf6cd2b9 \
    --hash=sha256:b861c0288ce2fa56209a9a6412d2e066ac664b3873b89c26c9d8415e8e32996f
tzdata==2026.3 ; sys_platform == 'win32' \
    --hash=sha256:4a1518b8993086a7982523e071643f3c0e5f213e75b21318e78bcabfff9d1415 \
    --hash=sha256:dc096730c87af6cab1b171c9d532be840741ff5d459015e7f6947bd7d7e54931
test/contract.py
import json
import warnings

from django.conf import settings


if not settings.configured:
    settings.configure(
        ALLOWED_HOSTS=["testserver"],
        DEBUG=False,
        INSTALLED_APPS=[],
        MIDDLEWARE=[],
        ROOT_URLCONF=__name__,
        SECRET_KEY="public-sample-secret-not-for-production",
        SECRET_KEY_FALLBACKS=[],
    )

import django

django.setup()

from django.core import signing
from django.core.signing import BadSignature
from django.http import JsonResponse
from django.test import Client, RequestFactory, override_settings
from django.urls import path
from django.utils.deprecation import RemovedInDjango70Warning


def inspect_request(request):
    return JsonResponse(
        {
            "method": request.method,
            "tags": request.GET.getlist("tag"),
            "name": request.POST.get("name"),
            "preferred": request.get_preferred_type(
                ["text/html", "application/json"]
            ),
        }
    )


def issue_cookie(request):
    response = JsonResponse({"issued": True})
    response.set_signed_cookie(
        "userid", "trusted-value", salt="v1", httponly=True, samesite="Lax"
    )
    return response


def read_original_cookie(request):
    return JsonResponse(
        {"value": request.get_signed_cookie("userid", salt="v1")}
    )


def read_colliding_cookie(request):
    try:
        value = request.get_signed_cookie("user", salt="idv1")
    except BadSignature:
        return JsonResponse({"accepted": False})
    return JsonResponse({"accepted": True, "value": value})


urlpatterns = [
    path("inspect/", inspect_request),
    path("issue/", issue_cookie),
    path("read-original/", read_original_cookie),
    path("read-collision/", read_colliding_cookie),
]


def response_json(response):
    return json.loads(response.content.decode("utf-8"))


factory = RequestFactory(
    headers={"accept": "application/json"},
    query_params={"tag": "constructor-default"},
)
factory_request = factory.post(
    "/inspect/",
    data={"name": "factory-body"},
    query_params={"tag": ["stable", "current"]},
)
factory_payload = response_json(inspect_request(factory_request))
assert factory_payload == {
    "method": "POST",
    "tags": ["stable", "current"],
    "name": "factory-body",
    "preferred": "application/json",
}

client = Client(
    headers={"accept": "application/json"},
    query_params={"tag": "constructor-default"},
)
client_payload = client.post(
    "/inspect/",
    data={"name": "client-body"},
    query_params={"tag": ["one", "two"]},
).json()
assert client_payload == {
    "method": "POST",
    "tags": ["one", "two"],
    "name": "client-body",
    "preferred": "application/json",
}

issued = client.get("/issue/")
signed_value = issued.cookies["userid"].value
assert issued.cookies["userid"]["httponly"] is True
assert issued.cookies["userid"]["samesite"] == "Lax"
assert client.get("/read-original/").json() == {"value": "trusted-value"}

# These pairs collided under Django's old cookie_name + salt derivation:
#   ("userid", "v1") -> "useridv1"
#   ("user", "idv1") -> "useridv1"
# Django 6.1's default signer keeps those boundaries unambiguous.
client.cookies["user"] = signed_value
assert client.get("/read-collision/").json() == {"accepted": False}

# Model a cookie emitted by Django 6.0 and earlier using the legacy salt.
legacy_value = signing.get_cookie_signer(salt="useridv1").sign("legacy-value")
client.cookies["user"] = legacy_value
assert client.get("/read-collision/").json() == {"accepted": False}

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always", RemovedInDjango70Warning)
    with override_settings(SIGNED_COOKIE_LEGACY_SALT_FALLBACK=True):
        legacy_payload = client.get("/read-collision/").json()

assert legacy_payload == {"accepted": True, "value": "legacy-value"}
assert any(issubclass(item.category, RemovedInDjango70Warning) for item in caught)

print("Django 6.1 request and signed-cookie contract passed")

原始种子者

csx-seed