address review

markdown-link-rule
Felix Ableitner 2024-01-04 16:10:18 +01:00
parent dc17cb13e9
commit d793d803b0
10 changed files with 75 additions and 29 deletions

View File

@ -129,7 +129,7 @@ test("Purge post, linked image removed", async () => {
expect(content2).toBe(""); expect(content2).toBe("");
}); });
test("Images in remote post are proxied", async () => { test("Images in remote post are proxied if setting enabled", async () => {
let user = await registerUser(beta, betaUrl); let user = await registerUser(beta, betaUrl);
let community = await createCommunity(gamma); let community = await createCommunity(gamma);
@ -137,7 +137,6 @@ test("Images in remote post are proxied", async () => {
image: Buffer.from("test"), image: Buffer.from("test"),
}; };
const upload = await user.uploadImage(upload_form); const upload = await user.uploadImage(upload_form);
console.log(upload);
let post = await createPost( let post = await createPost(
gamma, gamma,
community.community_view.community.id, community.community_view.community.id,
@ -145,8 +144,8 @@ test("Images in remote post are proxied", async () => {
"![](http://example.com/image2.png)", "![](http://example.com/image2.png)",
); );
expect(post.post_view.post).toBeDefined(); expect(post.post_view.post).toBeDefined();
// remote image gets proxied after upload // remote image gets proxied after upload
console.log(post.post_view.post);
expect( expect(
post.post_view.post.url?.startsWith( post.post_view.post.url?.startsWith(
"http://lemmy-gamma:8561/api/v3/image_proxy?url", "http://lemmy-gamma:8561/api/v3/image_proxy?url",
@ -160,7 +159,7 @@ test("Images in remote post are proxied", async () => {
let epsilonPost = await resolvePost(epsilon, post.post_view.post); let epsilonPost = await resolvePost(epsilon, post.post_view.post);
expect(epsilonPost.post).toBeDefined(); expect(epsilonPost.post).toBeDefined();
console.log(epsilonPost.post);
// remote image gets proxied after federation // remote image gets proxied after federation
expect( expect(
epsilonPost.post!.post.url?.startsWith( epsilonPost.post!.post.url?.startsWith(
@ -173,3 +172,35 @@ test("Images in remote post are proxied", async () => {
), ),
).toBeTruthy(); ).toBeTruthy();
}); });
test("No image proxying if setting is disabled", async () => {
let user = await registerUser(beta, betaUrl);
let community = await createCommunity(alpha);
const upload_form: UploadImage = {
image: Buffer.from("test"),
};
const upload = await user.uploadImage(upload_form);
let post = await createPost(
alpha,
community.community_view.community.id,
upload.url,
"![](http://example.com/image2.png)",
);
expect(post.post_view.post).toBeDefined();
// remote image doesnt get proxied after upload
expect(
post.post_view.post.url?.startsWith("http://127.0.0.1:8551/pictrs/image/"),
).toBeTruthy();
expect(post.post_view.post.body).toBe("![](http://example.com/image2.png)");
let gammaPost = await resolvePost(delta, post.post_view.post);
expect(gammaPost.post).toBeDefined();
// remote image doesnt get proxied after federation
expect(
gammaPost.post!.post.url?.startsWith("http://127.0.0.1:8551/pictrs/image/"),
).toBeTruthy();
expect(gammaPost.post!.post.body).toBe("![](http://example.com/image2.png)");
});

View File

@ -39,7 +39,7 @@ import {
loginUser, loginUser,
} from "./shared"; } from "./shared";
import { PostView } from "lemmy-js-client/dist/types/PostView"; import { PostView } from "lemmy-js-client/dist/types/PostView";
import { ResolveObject } from "lemmy-js-client"; import { EditSite, ResolveObject } from "lemmy-js-client";
let betaCommunity: CommunityView | undefined; let betaCommunity: CommunityView | undefined;
@ -72,6 +72,16 @@ function assertPostFederation(postOne?: PostView, postTwo?: PostView) {
} }
test("Create a post", async () => { test("Create a post", async () => {
// Setup some allowlists and blocklists
let editSiteForm: EditSite = {
allowed_instances: ["lemmy-beta"],
};
await delta.editSite(editSiteForm);
editSiteForm.allowed_instances = [];
editSiteForm.blocked_instances = ["lemmy-alpha"];
await epsilon.editSite(editSiteForm);
if (!betaCommunity) { if (!betaCommunity) {
throw "Missing beta community"; throw "Missing beta community";
} }
@ -104,6 +114,12 @@ test("Create a post", async () => {
await expect( await expect(
resolvePost(epsilon, postRes.post_view.post), resolvePost(epsilon, postRes.post_view.post),
).rejects.toStrictEqual(Error("couldnt_find_object")); ).rejects.toStrictEqual(Error("couldnt_find_object"));
// remove added allow/blocklists
editSiteForm.allowed_instances = [];
editSiteForm.blocked_instances = [];
await delta.editSite(editSiteForm);
await epsilon.editSite(editSiteForm);
}); });
test("Create a post in a non-existent community", async () => { test("Create a post in a non-existent community", async () => {

View File

@ -177,13 +177,6 @@ export async function setupLogins() {
]; ];
await gamma.editSite(editSiteForm); await gamma.editSite(editSiteForm);
editSiteForm.allowed_instances = ["lemmy-beta"];
await delta.editSite(editSiteForm);
editSiteForm.allowed_instances = [];
editSiteForm.blocked_instances = ["lemmy-alpha"];
await epsilon.editSite(editSiteForm);
// Create the main alpha/beta communities // Create the main alpha/beta communities
// Ignore thrown errors of duplicates // Ignore thrown errors of duplicates
try { try {
@ -528,7 +521,7 @@ export async function likeComment(
export async function createCommunity( export async function createCommunity(
api: LemmyHttp, api: LemmyHttp,
name_: string = randomString(5), name_: string = randomString(10),
): Promise<CommunityResponse> { ): Promise<CommunityResponse> {
let description = "a sample description"; let description = "a sample description";
let form: CreateCommunity = { let form: CreateCommunity = {

View File

@ -34,11 +34,10 @@ pub async fn save_user_settings(
let site_view = SiteView::read_local(&mut context.pool()).await?; let site_view = SiteView::read_local(&mut context.pool()).await?;
let slur_regex = local_site_to_slur_regex(&site_view.local_site); let slur_regex = local_site_to_slur_regex(&site_view.local_site);
let bio = process_markdown_opt(&data.bio, &slur_regex, &context).await?; let bio = diesel_option_overwrite(process_markdown_opt(&data.bio, &slur_regex, &context).await?);
let avatar = proxy_image_link_opt_api(&data.avatar, &context).await?; let avatar = proxy_image_link_opt_api(&data.avatar, &context).await?;
let banner = proxy_image_link_opt_api(&data.banner, &context).await?; let banner = proxy_image_link_opt_api(&data.banner, &context).await?;
let bio = diesel_option_overwrite(bio);
let display_name = diesel_option_overwrite(data.display_name.clone()); let display_name = diesel_option_overwrite(data.display_name.clone());
let matrix_user_id = diesel_option_overwrite(data.matrix_user_id.clone()); let matrix_user_id = diesel_option_overwrite(data.matrix_user_id.clone());
let email_deref = data.email.as_deref().map(str::to_lowercase); let email_deref = data.email.as_deref().map(str::to_lowercase);

View File

@ -49,7 +49,7 @@ pub async fn fetch_link_metadata(
// https://github.com/LemmyNet/lemmy/issues/1964 // https://github.com/LemmyNet/lemmy/issues/1964
let html_bytes = response.bytes().await.map_err(LemmyError::from)?.to_vec(); let html_bytes = response.bytes().await.map_err(LemmyError::from)?.to_vec();
let mut metadata = extract_opengraph_data(&html_bytes, url)?; let mut metadata = extract_opengraph_data(&html_bytes, url).unwrap_or_default();
metadata.content_type = content_type.map(|c| c.to_string()); metadata.content_type = content_type.map(|c| c.to_string());
if generate_thumbnail && is_image { if generate_thumbnail && is_image {
@ -71,14 +71,13 @@ pub async fn fetch_link_metadata_opt(
url: Option<&Url>, url: Option<&Url>,
generate_thumbnail: bool, generate_thumbnail: bool,
context: &LemmyContext, context: &LemmyContext,
) -> Result<LinkMetadata, LemmyError> { ) -> LinkMetadata {
let metadata = match &url { match &url {
Some(url) => fetch_link_metadata(url, generate_thumbnail, context) Some(url) => fetch_link_metadata(url, generate_thumbnail, context)
.await .await
.unwrap_or_default(), .unwrap_or_default(),
_ => Default::default(), _ => Default::default(),
}; }
Ok(metadata)
} }
/// Extract site metadata from HTML Opengraph attributes. /// Extract site metadata from HTML Opengraph attributes.
@ -300,6 +299,7 @@ mod tests {
context::LemmyContext, context::LemmyContext,
request::{extract_opengraph_data, fetch_link_metadata}, request::{extract_opengraph_data, fetch_link_metadata},
}; };
use lemmy_utils::settings::SETTINGS;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serial_test::serial; use serial_test::serial;
use url::Url; use url::Url;

View File

@ -846,9 +846,13 @@ pub async fn process_markdown(
context: &LemmyContext, context: &LemmyContext,
) -> LemmyResult<String> { ) -> LemmyResult<String> {
let text = remove_slurs(text, slur_regex); let text = remove_slurs(text, slur_regex);
let (text, links) = markdown_rewrite_image_links(text); if context.settings().pictrs_config()?.image_proxy {
RemoteImage::create(&mut context.pool(), links).await?; let (text, links) = markdown_rewrite_image_links(text);
Ok(text) RemoteImage::create(&mut context.pool(), links).await?;
Ok(text)
} else {
Ok(text)
}
} }
pub async fn process_markdown_opt( pub async fn process_markdown_opt(
@ -862,6 +866,11 @@ pub async fn process_markdown_opt(
} }
} }
/// Rewrite a link to go through `/api/v3/image_proxy` endpoint. This is only for remote urls and
/// if image_proxy setting is enabled.
///
/// The parameter `image_proxy` is the config value of `pictrs.image_proxy`. Its necessary to pass
/// as separate parameter so it can be changed in tests.
pub(crate) async fn proxy_image_link( pub(crate) async fn proxy_image_link(
link: Url, link: Url,
image_proxy: bool, image_proxy: bool,

View File

@ -84,7 +84,7 @@ pub async fn create_post(
} }
// Fetch post links and pictrs cached image // Fetch post links and pictrs cached image
let metadata = fetch_link_metadata_opt(url.as_ref(), true, &context).await?; let metadata = fetch_link_metadata_opt(url.as_ref(), true, &context).await;
let url = proxy_image_link_opt_apub(url, &context).await?; let url = proxy_image_link_opt_apub(url, &context).await?;
// Only need to check if language is allowed in case user set it explicitly. When using default // Only need to check if language is allowed in case user set it explicitly. When using default

View File

@ -224,7 +224,7 @@ impl Object for ApubPost {
let do_generate_thumbnail = thumbnail_url.is_none() && allow_generate_thumbnail; let do_generate_thumbnail = thumbnail_url.is_none() && allow_generate_thumbnail;
// Generate local thumbnail only if no thumbnail was federated and 'sensitive' attributes allow it. // Generate local thumbnail only if no thumbnail was federated and 'sensitive' attributes allow it.
let metadata = fetch_link_metadata_opt(url.as_ref(), do_generate_thumbnail, context).await?; let metadata = fetch_link_metadata_opt(url.as_ref(), do_generate_thumbnail, context).await;
if let Some(thumbnail_url_) = metadata.thumbnail { if let Some(thumbnail_url_) = metadata.thumbnail {
thumbnail_url = Some(thumbnail_url_.into()); thumbnail_url = Some(thumbnail_url_.into());
} }

View File

@ -30,6 +30,8 @@ pub struct LocalImageForm {
pub pictrs_delete_token: String, pub pictrs_delete_token: String,
} }
/// Stores all images which are hosted on remote domains. When attempting to proxy an image, it
/// is checked against this table to avoid Lemmy being used as a general purpose proxy.
#[skip_serializing_none] #[skip_serializing_none]
#[derive(PartialEq, Eq, Debug, Clone)] #[derive(PartialEq, Eq, Debug, Clone)]
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))] #[cfg_attr(feature = "full", derive(Queryable, Identifiable))]

View File

@ -21,12 +21,8 @@
pictrs: { pictrs: {
url: "http://pictrs:8080/" url: "http://pictrs:8080/"
# api_key: "API_KEY" # api_key: "API_KEY"
<<<<<<< HEAD
cache_remote_images: true
image_proxy: true image_proxy: true
=======
cache_external_link_previews: true cache_external_link_previews: true
>>>>>>> main
} }
#opentelemetry_url: "http://otel:4137" #opentelemetry_url: "http://otel:4137"