Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 4 additions & 16 deletions services/file-service/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,18 @@ COPY --from=builder /app/target/release/file-service /app/file-service
RUN useradd -r -u 1001 appuser
USER appuser

# demo-coggtm tenant variant -- NEVER merge this to `main`. The tenant runs the
# image this branch builds (tag `tenant-coggtm`), so baking the switch in here is
# the only way to make its uploads fail permanently: it survives pod restarts,
# Redis restarts, TTL expiry and idle-suspend/wake, and needs no chart override:
# the deploy runner renders charts from its own bundled tree, whose file-service
# values are `config: {}` and whose build_helm_args sets a fixed list of
# config.* keys that does not include this one, so nothing overrides the image.
# Anything that does set the variable explicitly still wins -- docker-compose.yml
# passes false, so local stacks behave like the golden app.
ENV FILE_UPLOAD_ALWAYS_FAIL=true

# Click-triggered notification failure (same bake rationale as above): sharing
# a file publishes the file_shared event to a nonexistent SNS topic, so every
# share click fails with a real AWS SNS error and fires the
# Click-triggered notification failure: sharing a file publishes the
# file_shared event to a nonexistent SNS topic, so every share click fails
# with a real AWS SNS error and fires the
# NotificationEventPublishFailure alert at admin-service (incident + Devin
# session + Slack, attributed to the sharing user via reporter_email). The
# share record is still saved, but the request returns the AWS error so the
# UI shows a red failure banner. docker-compose.yml passes false, so local
# stacks behave like the golden app.
ENV FILE_SHARE_EVENT_ALWAYS_FAIL=true

# With uploads failing permanently (above), users would have no files to share.
# Seeding gives every user a few demo documents on their first file listing,
# so the share-notification failure is demoable alongside the upload failure.
# so the share flow is demoable on an account that has uploaded nothing yet.
# docker-compose.yml passes false, so local stacks behave like the golden app.
ENV FILE_SEED_DEMO_DOCS=true
Comment on lines 27 to 32

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Sibling chaos flags remain baked in

The image still sets FILE_SHARE_EVENT_ALWAYS_FAIL=true and FILE_SEED_DEMO_DOCS=true (services/file-service/Dockerfile:27-32). The PR scopes itself to uploads, so share-failure and demo-seeding behavior persist by design.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


Expand Down
63 changes: 63 additions & 0 deletions services/file-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,39 @@ impl SnsConfig {
mod tests {
use super::{parse_bool, parse_bool_env};

fn dockerfile_enables_upload_failure(dockerfile: &str) -> bool {
let logical_dockerfile = dockerfile.replace("\\\r\n", " ").replace("\\\n", " ");
logical_dockerfile.lines().any(|line| {
let instruction = line.trim();
let mut parts = instruction.splitn(2, char::is_whitespace);
if !parts
.next()
.is_some_and(|part| part.eq_ignore_ascii_case("ENV"))
{
return false;
}

let fields: Vec<_> = parts
.next()
.unwrap_or_default()
.split_whitespace()
.collect();
if fields.first().is_some_and(|field| field.contains('=')) {
return fields.iter().any(|field| {
field.split_once('=').is_some_and(|(name, value)| {
name == "FILE_UPLOAD_ALWAYS_FAIL"
&& parse_bool(value.trim_matches(['"', '\'']), false)
})
});
}

fields.first() == Some(&"FILE_UPLOAD_ALWAYS_FAIL")
&& fields
.get(1)
.is_some_and(|value| parse_bool(value.trim_matches(['"', '\'']), false))
})
}
Comment on lines +120 to +151

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Regression parser is best-effort

dockerfile_enables_upload_failure models only a subset of Docker ENV syntax (backslash continuations, simple quote stripping, legacy and equals forms). It is adequate as a guard against re-adding the truthy default but does not replicate Docker's full escaping rules.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.


#[test]
fn parse_bool_accepts_true_and_one() {
for raw in ["true", "TRUE", " True ", "1"] {
Expand Down Expand Up @@ -155,4 +188,34 @@ mod tests {
}
assert!(!super::ServerConfig::from_env().upload_always_fail);
}

#[test]
fn production_image_does_not_enable_upload_failures() {
assert!(!dockerfile_enables_upload_failure(include_str!(
"../Dockerfile"
)));
}

#[test]
fn detects_truthy_upload_failure_image_defaults() {
for dockerfile in [
"ENV FILE_UPLOAD_ALWAYS_FAIL=true",
"env FILE_UPLOAD_ALWAYS_FAIL=\"TRUE\"",
"ENV FILE_UPLOAD_ALWAYS_FAIL '1'",
"ENV OTHER=value FILE_UPLOAD_ALWAYS_FAIL=1",
"ENV OTHER=value \\\n FILE_UPLOAD_ALWAYS_FAIL=true",
] {
assert!(
dockerfile_enables_upload_failure(dockerfile),
"dockerfile={dockerfile}"
);
}
}

#[test]
fn ignores_hash_characters_inside_upload_failure_values() {
assert!(!dockerfile_enables_upload_failure(
"ENV FILE_UPLOAD_ALWAYS_FAIL=\"true#not-a-comment\""
));
}
}
Loading