638f5a8312
* Fix avatar path resolution in dev-env * changeset * extract dev-env assets to dedicated folder * add comment * fix fmt
274 lines
8.2 KiB
TypeScript
274 lines
8.2 KiB
TypeScript
import { AtpAgent } from '@atproto/api'
|
|
import { TestNetworkNoAppView, ImageRef, SeedClient } from '@atproto/dev-env'
|
|
import { BlobNotFoundError } from '@atproto/repo'
|
|
import basicSeed from './seeds/basic'
|
|
import {
|
|
RepoBlobRef,
|
|
RepoRef,
|
|
} from '../src/lexicon/types/com/atproto/admin/defs'
|
|
import { Main as StrongRef } from '../src/lexicon/types/com/atproto/repo/strongRef'
|
|
|
|
describe('moderation', () => {
|
|
let network: TestNetworkNoAppView
|
|
let agent: AtpAgent
|
|
let sc: SeedClient
|
|
|
|
let repoSubject: RepoRef
|
|
let recordSubject: StrongRef
|
|
let blobSubject: RepoBlobRef
|
|
let blobRef: ImageRef
|
|
|
|
beforeAll(async () => {
|
|
network = await TestNetworkNoAppView.create({
|
|
dbPostgresSchema: 'moderation',
|
|
})
|
|
|
|
agent = network.pds.getClient()
|
|
sc = network.getSeedClient()
|
|
await basicSeed(sc)
|
|
await network.processAll()
|
|
repoSubject = {
|
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
did: sc.dids.bob,
|
|
}
|
|
const post = sc.posts[sc.dids.carol][0]
|
|
recordSubject = {
|
|
$type: 'com.atproto.repo.strongRef',
|
|
uri: post.ref.uriStr,
|
|
cid: post.ref.cidStr,
|
|
}
|
|
blobRef = post.images[1]
|
|
blobSubject = {
|
|
$type: 'com.atproto.admin.defs#repoBlobRef',
|
|
did: sc.dids.carol,
|
|
cid: blobRef.image.ref.toString(),
|
|
}
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await network.close()
|
|
})
|
|
|
|
it('takes down accounts', async () => {
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: repoSubject,
|
|
takedown: { applied: true, ref: 'test-repo' },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
const res = await agent.api.com.atproto.admin.getSubjectStatus(
|
|
{
|
|
did: repoSubject.did,
|
|
},
|
|
{ headers: network.pds.adminAuthHeaders() },
|
|
)
|
|
expect(res.data.subject.did).toEqual(sc.dids.bob)
|
|
expect(res.data.takedown?.applied).toBe(true)
|
|
expect(res.data.takedown?.ref).toBe('test-repo')
|
|
})
|
|
|
|
it('restores takendown accounts', async () => {
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: repoSubject,
|
|
takedown: { applied: false },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
const res = await agent.api.com.atproto.admin.getSubjectStatus(
|
|
{
|
|
did: repoSubject.did,
|
|
},
|
|
{ headers: network.pds.adminAuthHeaders() },
|
|
)
|
|
expect(res.data.subject.did).toEqual(sc.dids.bob)
|
|
expect(res.data.takedown?.applied).toBe(false)
|
|
expect(res.data.takedown?.ref).toBeUndefined()
|
|
})
|
|
|
|
it('takes down records', async () => {
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: recordSubject,
|
|
takedown: { applied: true, ref: 'test-record' },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
const res = await agent.api.com.atproto.admin.getSubjectStatus(
|
|
{
|
|
uri: recordSubject.uri,
|
|
},
|
|
{ headers: network.pds.adminAuthHeaders() },
|
|
)
|
|
expect(res.data.subject.uri).toEqual(recordSubject.uri)
|
|
expect(res.data.takedown?.applied).toBe(true)
|
|
expect(res.data.takedown?.ref).toBe('test-record')
|
|
})
|
|
|
|
it('restores takendown records', async () => {
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: recordSubject,
|
|
takedown: { applied: false },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
const res = await agent.api.com.atproto.admin.getSubjectStatus(
|
|
{
|
|
uri: recordSubject.uri,
|
|
},
|
|
{ headers: network.pds.adminAuthHeaders() },
|
|
)
|
|
expect(res.data.subject.uri).toEqual(recordSubject.uri)
|
|
expect(res.data.takedown?.applied).toBe(false)
|
|
expect(res.data.takedown?.ref).toBeUndefined()
|
|
})
|
|
|
|
describe('blob takedown', () => {
|
|
it('takes down blobs', async () => {
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: blobSubject,
|
|
takedown: { applied: true, ref: 'test-blob' },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
const res = await agent.api.com.atproto.admin.getSubjectStatus(
|
|
{
|
|
did: blobSubject.did,
|
|
blob: blobSubject.cid,
|
|
},
|
|
{ headers: network.pds.adminAuthHeaders() },
|
|
)
|
|
expect(res.data.subject.did).toEqual(blobSubject.did)
|
|
expect(res.data.subject.cid).toEqual(blobSubject.cid)
|
|
expect(res.data.takedown?.applied).toBe(true)
|
|
expect(res.data.takedown?.ref).toBe('test-blob')
|
|
})
|
|
|
|
it('removes blob from the store', async () => {
|
|
const tryGetBytes = network.pds.ctx
|
|
.blobstore(blobSubject.did)
|
|
.getBytes(blobRef.image.ref)
|
|
await expect(tryGetBytes).rejects.toThrow(BlobNotFoundError)
|
|
})
|
|
|
|
it('prevents blob from being referenced again.', async () => {
|
|
const referenceBlob = sc.post(sc.dids.carol, 'pic', [], [blobRef])
|
|
await expect(referenceBlob).rejects.toThrow('Could not find blob:')
|
|
})
|
|
|
|
it('prevents blob from being reuploaded', async () => {
|
|
const attempt = sc.uploadFile(
|
|
sc.dids.carol,
|
|
'../dev-env/assets/key-alt.jpg',
|
|
'image/jpeg',
|
|
)
|
|
await expect(attempt).rejects.toThrow(
|
|
'Blob has been takendown, cannot re-upload',
|
|
)
|
|
})
|
|
|
|
it('prevents image blob from being served.', async () => {
|
|
const attempt = agent.api.com.atproto.sync.getBlob({
|
|
did: sc.dids.carol,
|
|
cid: blobRef.image.ref.toString(),
|
|
})
|
|
await expect(attempt).rejects.toThrow('Blob not found')
|
|
})
|
|
|
|
it('restores blob when takedown is removed', async () => {
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: blobSubject,
|
|
takedown: { applied: false },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
|
|
// Can post and reference blob
|
|
const post = await sc.post(sc.dids.carol, 'pic', [], [blobRef])
|
|
expect(post.images[0].image.ref.equals(blobRef.image.ref)).toBeTruthy()
|
|
|
|
// Can fetch through image server
|
|
const res = await agent.api.com.atproto.sync.getBlob({
|
|
did: sc.dids.carol,
|
|
cid: blobRef.image.ref.toString(),
|
|
})
|
|
|
|
expect(res.data.byteLength).toBeGreaterThan(9000)
|
|
})
|
|
|
|
it('prevents blobs of takendown accounts from being served.', async () => {
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: {
|
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
did: sc.dids.carol,
|
|
},
|
|
takedown: { applied: true },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
const blobParams = {
|
|
did: sc.dids.carol,
|
|
cid: blobRef.image.ref.toString(),
|
|
}
|
|
// public, disallow
|
|
const attempt1 = agent.api.com.atproto.sync.getBlob(blobParams)
|
|
await expect(attempt1).rejects.toThrow(/Repo has been takendown/)
|
|
// logged-in, disallow
|
|
const attempt2 = agent.api.com.atproto.sync.getBlob(blobParams, {
|
|
headers: sc.getHeaders(sc.dids.bob),
|
|
})
|
|
await expect(attempt2).rejects.toThrow(/Repo has been takendown/)
|
|
// logged-in as account, allow
|
|
const res1 = await agent.api.com.atproto.sync.getBlob(blobParams, {
|
|
headers: sc.getHeaders(sc.dids.carol),
|
|
})
|
|
expect(res1.data.byteLength).toBeGreaterThan(9000)
|
|
// admin role, allow
|
|
const res2 = await agent.api.com.atproto.sync.getBlob(blobParams, {
|
|
headers: network.pds.adminAuthHeaders(),
|
|
})
|
|
expect(res2.data.byteLength).toBeGreaterThan(9000)
|
|
// revert takedown
|
|
await agent.api.com.atproto.admin.updateSubjectStatus(
|
|
{
|
|
subject: {
|
|
$type: 'com.atproto.admin.defs#repoRef',
|
|
did: sc.dids.carol,
|
|
},
|
|
takedown: { applied: false },
|
|
},
|
|
{
|
|
encoding: 'application/json',
|
|
headers: network.pds.adminAuthHeaders(),
|
|
},
|
|
)
|
|
})
|
|
})
|
|
})
|